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

时间:2011-12-28 15:32:26

标签: c# .net c dll

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

#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);
}

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

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

编辑:忘记添加Main方法:

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

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

2 个答案:

答案 0 :(得分:4)

要使这项工作正确,您需要做三件事:

  • 你需要告诉pinvoke marshaller关于实际委托类型,使用Delegate不够好。这将产生错误的thunk,无法正确编组参数。这就是你所看到的。
  • 如果不是__stdcall和[UnmanagedFunctionPointer]属性,你需要告诉编组关于调用约定。如果出现这种错误会导致堆栈失败,并且很难发生硬碰撞。
  • 您需要存储对委托对象的引用,以便垃圾收集器不会收集它。它无法看到本机代码所持有的引用。出现这种错误会导致本机代码在下一次垃圾回收后因硬崩溃而失败。

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

[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);
    }
}

答案 1 :(得分:1)

您的代理签名必须与本机回调的签名相匹配,同时必须正确设置UnmanagedFunctionPointerAttribute

在你的情况下如此:

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

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

用法:

{
    rdOnAllDone(rdOnAllDoneCallback);
}

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