c# - 从 C# 调用 C DLL 方法的正确方法

标签 c# .net c dll

我正在尝试从用 C 编写的第三方 DLL 执行一些方法(在这种特殊情况下,rdOnAllDone),并查看头文件,我发现了这个:

#ifndef TDECLSDONE
#ifdef STDCALL
#define     CCON        __stdcall
#else
#define     CCON        __cdecl
#endif
#define TDECLSDONE
#endif
#define     DLLIMP      __declspec (dllimport)
DLLIMP int CCON rdOnAllDone (void(CCON *)(int));

在寻找调用此方法的方法后,我做了这个:

[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate();

private static void rdOnAllDoneCallback()
{
    Console.WriteLine("rdOnAllDoneCallback invoked");
}

该方法被正确调用,只是我无法获取 int 参数。所以我尝试像这样添加输入参数 int

[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate(int number);

private static void rdOnAllDoneCallback(int number)
{
    Console.WriteLine("rdOnAllDoneCallback invoked " + number);
}

但现在委托(delegate)被调用了两次,它使程序崩溃并出现以下错误“vshosts32.exe 已停止工作”

调用此 DLL 方法的正确方法是什么?

编辑:忘记添加 Main 方法:

public static void Main()
{
    rdOnAllDoneCallbackDelegate del3 = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(del3);

    while (true)
    {
        Thread.Sleep(1000);
    }
}

最佳答案

要使这项工作正常进行,您需要做三件事:

  • 您需要告诉 pinvoke 编码器有关实际 委托(delegate)类型的信息,使用 Delegate 还不够好。这将创建错误的 thunk,无法正确整理参数。这就是您看到的情况。
  • 如果调用约定不是具有 [UnmanagedFunctionPointer] 属性的 __stdcall,则需要将调用约定告诉编码器。弄错这个错误会导致堆栈不平衡,并很有可能发生严重崩溃。
  • 您需要存储对委托(delegate)对象的引用,这样垃圾收集器就不会收集它。它看不到 native 代码持有的引用。错误的处理会导致 native 代码在下一次垃圾回收后发生硬崩溃。

所以这应该能更好地工作,根据需要进行调整:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);

[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate d);

class Foo {
    private static rdOnAllDoneCallbackDelegate callback;   // Keeps it referenced

    public static void SetupCallback() {
       callback = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
       rdOnAllDone(callback);
    }

    private static void rdOnAllDoneCallback(int parameter) {
       Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
    }
}

关于c# - 从 C# 调用 C DLL 方法的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8657805/

相关文章:

c# - 分配给 Lazy<> 时的反协方差问题

c# - 跟随/剪辑到形状的文本?

c# - 当使用 OleDBDataReader 从 Access 数据库中选择一个备注字段时,它只返回部分字符串。我怎样才能得到整个字符串?

c - 再次自动运行 C 代码

c - C 中的 isatty() 是做什么用的?

C# 不能跨程序集边界使用,因为它的泛型类型参数是嵌入式互操作类型

c# - ASP.NET MVC 多个开始形式?

c# - javascript :void(0) called 时 WebBrowser 控件未完成加载

c# - GZip减压给出空白文件

c - 链表排序函数仅循环一次