如何从C#调用具有void*callback和object参数的C++ Dll中的函数

And*_*dre 7 c c# c++ pinvoke

我正在尝试创建一个C dll的包装器,我试图调用一个具有回调函数的函数,接收一个对象作为传回的指针.

.h文件delares

extern int SetErrorHandler(void (*handler) (int, const char*, void*),
                              void *data_ptr);
Run Code Online (Sandbox Code Playgroud)

处理程序是一个回调函数,在发生错误时调用,data_ptr是传递给你的任何对象(状态),对于我的应用程序就是这个(当前对象).

我能够在一个dll中调用函数,它使用编组的常量类型,如简单类型字符串,整数等.但我无法弄清楚如何编写一个指向C#对象的指针作为状态.

为了通过在这里搜索我已经找到的对象引用传递对象引用,否则似乎我需要一个结构类型来能够编组该函数,所以我创建了一个结构来保存我的对象:

[StructLayout(LayoutKind.Sequential)]
struct MyObjectState
{
   public object state;
}
Run Code Online (Sandbox Code Playgroud)

编辑:我试图在属性[MarshalAs(UnmanagedType.Struct, SizeConst = 4)]上放置一个属性:public object state但是这会产生相同的错误,所以我删除了它,似乎它无论如何都不会起作用.

该struct包含一个对象属性,用于保存回调函数的任何对象.

我在C#中声明了委托如下:

delegate void ErrorHandler(int errorCode, IntPtr name, IntPtr data);
Run Code Online (Sandbox Code Playgroud)

然后我在C#中声明了导入函数,如下所示:

[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SetErrorHandler handler, IntPtr data);
Run Code Online (Sandbox Code Playgroud)

然后我在我的C#代码中创建了一个回调函数:

void MyErrorHandler(int errorCode, IntPtr name, IntPtr data)
{
    var strName = Marshal.PtrToStringAnsi(name);
    var state = new MyObjectState();
    Marshal.PtrToStructure(data, state);
    Console.WriteLine(strName);
}
Run Code Online (Sandbox Code Playgroud)

我可以调用库函数如下:

var state = new MyObjectState()
{
    state = this
};
IntPtr pStruct = Marshal.AllocHGlobal(Marshal.SizeOf(state));
Marshal.StructureToPtr(state, pStruct, true);
int ret = SetErrorHandler(MyErrorHandler, pStruct);
Run Code Online (Sandbox Code Playgroud)

调用工作和回调函数被调用,但我无法访问回调函数中的数据,当我尝试时,Marshal.PtrToStructure我得到一个错误:

结构不能是值类.

我在这里做了很多搜索,在Marshall上发现了各种各样的东西,但是没有任何东西可以帮助我实现这一目标

谢谢.

Dav*_*nan 3

你让事情变得比需要的更加复杂。您的 C# 客户端不需要使用该data_ptr参数,因为 C# 委托已经具有用于维护指针的内置机制this

所以你可以简单地传递IntPtr.Zero给代表。data_ptr在错误处理程序委托中,您只需忽略“since thiswill be available”的值。

如果您不明白这个描述,这里有一个简短的程序来说明我的意思。请注意MyErrorHandler实例方法如何充当错误处理程序并可以修改实例数据。

class Wrapper
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void ErrorHandler(int errorCode, string name, IntPtr data);

    [DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int SetErrorHandler(ErrorHandler handler, IntPtr data);

    void MyErrorHandler(int errorCode, string name, IntPtr data)
    {
        lastError = errorCode;
        lastErrorName = name;
    }

    public Wrapper()
    {
        SetErrorHandler(MyErrorHandler, IntPtr.Zero);
    }            

    public int lastError { get; set; }
    public string lastErrorName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Wrapper wrapper = new Wrapper();
    }
}
Run Code Online (Sandbox Code Playgroud)