我正在尝试创建一个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上发现了各种各样的东西,但是没有任何东西可以帮助我实现这一目标
谢谢.
你让事情变得比需要的更加复杂。您的 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)