【问题标题】:Struggling to call the method from native DLL using C#努力使用 C# 从本机 DLL 调用方法
【发布时间】:2017-08-17 18:39:53
【问题描述】:

这是我试图调用的方法签名。

EXTERN_C
HRESULT
QueryData(
    _Outptr_opt_result_bytebuffer_(*SizeOfData)  PBYTE * Data,
    _Out_opt_                                   UINT32* SizeOfData,
    _In_                                        BOOL    IsDataType
) 

上面的方法不是我的代码,它是供应商代码,不幸的是我没有足够的知识来调用这个方法。我所知道的只是它应该给我一个数据块。

这是我到目前为止所做的。

 [DllImport("DataGetter.dll")]
        internal static extern int QueryData(IntPtr data, UIntPtr sizeOfData, bool isDataType);

IntPtr data= new IntPtr();
            UIntPtr sizeOfData= new UIntPtr();
            bool isDataType= true;
            int hresult = QueryData(data, sizeOfData, isDataType);

我的方法没有失败,但它没有返回数据中的任何内容。知道如何从 C# 调用这个奇怪的方法吗?

【问题讨论】:

  • 你有没有尝试调用任何更简单的函数?还是明确设置调用约定? [DllImport("DataGetter.dll", CallingConvention = CallingConvention.Cdecl)]
  • 我过去曾使用 P/Invoke 和简单的函数,但是,关于这个 DLL,这是它公开的唯一方法。有趣的是,当我添加调用约定时,调用失败并出现以下错误。 “QueryData 使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。请检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。”
  • 我已经很久没有做任何 p/invoke 了,但我认为你的问题是“数据”成员。该函数正在接受 BYTE**(不是 BYTE*)。显然,它在内部分配了一个缓冲区,并返回一个指向缓冲区的指针及其大小。除非你在调用后做一些清理工作,否则你很可能会导致内存泄漏。
  • 是的,你打算如何释放内存?是在 COM 堆上分配的吗?
  • 不确定如何释放内存?你知道从 C# 代码本身中释放的方法吗?这足够 Marshal.DestroyStructure(data, typeof(IntPtr)); 吗?

标签: c# c++ .net pinvoke


【解决方案1】:

这里有两个问题:首先是将QueryData 设置的值获取到DatasizeOfData,它们获取指向局部变量的指针。您可以使用 refout 关键字来实现,因此 C++ 中的 UINT32* SizeOfData 变为 ref System.UInt32 SizeOfDataKey difference 在它们之间是 out 参数不必在函数调用之前初始化。其次是将C++中定义的非托管数组转换成C#。你可以用Marshall.Copy来做。

有一件事尚不清楚,但应该在文档中说明 - 每当从 C++ 返回的数组是动态分配的并且需要在 C# 中释放时。如果是这样,您将发生内存泄漏,这将增加每次调用函数时的内存使用量。最简单的测试方法是调用函数 1000000 次并检查内存使用情况。

完整代码:

    [DllImport("DataGetter.dll"]
    internal static extern int QueryData(out IntPtr data, out System.UInt32 sizeOfData, bool isDataType);

    void example()
    {
        IntPtr dataPtr;
        System.UInt32 sizeOfData;
        bool isDataType = false;
        int hresult = QueryData(out dataPtr, out sizeOfData, isDataType);
        var data = new byte[sizeOfData];
        Marshal.Copy(dataPtr, data, 0, (int)sizeOfData);
        // data now contains retreived bytes
    }

旧帖子: 试试看。

    [DllImport("DataGetter.dll")]
    internal static extern int QueryData(ref IntPtr data, ref System.UInt32 sizeOfData, bool isDataType);

我不确定PBYTE 是什么,但我想它是指向 BYTE 的指针。 函数应该改变datasizeOfData 变量。

【讨论】:

  • 这会返回一些东西,但是,你知道如何从 IntPtr 获取字节数组吗?
  • 我做了类似跟随的事情,但似乎正在工作,但不确定这是否正确。 IntPtr 数据=新的 IntPtr(); UIntPtr sizeOfData=new UIntPtr();布尔 isDataType = 假; int hresult = QueryData(参考数据,参考 sizeOfData,isDataType);列表 dataBits = 新列表(); for (int i = 0; i
  • @user1144852 我已经更新了我的答案,其中包含代码片段和一些解释。我不知道只要数据正确,我该如何帮助您进行验证。你能做一些测试吗?你知道会发生什么吗?
  • @DavidHeffernan 我不得不做一点研究,我不相信。据我发现它是“根据 Visual C++ 编译器,__cdecl 是 C 和 C++ 程序的默认值,WinAPI 函数使用 __stdcall 约定。” (stackoverflow.com/a/3404412/5945883,第 2 点)。根据 winnt.h 标准调用是用 STDAPI 定义的,而不是 EXTERN_C。所以没有stdcall关键字,所以使用default,也就是cdecl,对吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-08
  • 1970-01-01
  • 1970-01-01
  • 2015-06-29
  • 2017-09-24
  • 2015-02-16
  • 1970-01-01
相关资源
最近更新 更多