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

Gon*_*oso 2 .net c 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));
Run Code Online (Sandbox Code Playgroud)

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

[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");
}
Run Code Online (Sandbox Code Playgroud)

该方法被正确调用,只是我无法获取 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:忘记添加 Main 方法:

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

    while (true)
    {
        Thread.Sleep(1000);
    }
}
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 6

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

  • 您需要告诉 pinvoke marshaller实际的委托类型,使用 Delegate 还不够好。这将创建错误的 thunk,不会正确地整理参数。这就是你所看到的。
  • 如果它不是带有 [UnmanagedFunctionPointer] 属性的 __stdcall,您需要告诉编组器有关调用约定的信息。犯这个错误会使筹码不平衡,很可能会导致严重崩溃。
  • 您需要存储对委托对象的引用,以便垃圾收集器不会收集它。它看不到本机代码持有的引用。弄错了会导致本机代码在下一次垃圾收集后严重崩溃而失败。

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

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)