我正在尝试PInvoke到这个C ++库函数:
int Initialize(Callback* callback);
struct Callback
{
//.......
void virtual StateChanged(int state) = 0;
};
我尝试过这种天真的C#代码,但它不起作用。
[DllImport("CPlusPlusLibrary.dll", SetLastError = true, EntryPoint = "?Initialize@CPlusPlusLibrary@@YAHPAUCallback@1@@Z")]
public static extern int Initialize(Callback callback);
[StructLayout(LayoutKind.Sequential)]
public class Callback
{
//....
public delegate void IntDelegate(int state);
public IntDelegate StateChanged(int state);
}
var callback = new Callback();
var result = Initialize(callback);
答案 0 :(得分:3)
据我所知,这样做是不可能的。方法不像字段那样“在那里”,除此之外,使用虚方法创建结构将在对象中创建vtable指针,而不是在c#镜像类中考虑。
你可以做的是PInvoke方法,它接受一个functionPointer(在C ++中)并在那里传递一个委托(C#)。然后,您可以使用此函数指针从本机代码中调用它,您的代理将启动。
然后,您可以更改stateChange方法定义以将Callback *作为第一个参数,因此当您从本机代码调用它时,您可以传递一个对象指针,该指针负责该更改并将其编组回c#中的Callback。
//编辑没有本机dll的源代码,在c#和c ++之间构建桥梁是我的想法。这可以用c ++ / cli或c ++来完成,坚持原生我的想法会是这样的:
//c++ <--> new c++ dll <--> c#
struct CallbackBridge : public Callback
{
void (*_stateChanged)(int);
virtual void stateChanged(int state)
{
if (_stateChanged)
_stateChanged(this, state);
}
};
void* CreateCallback() { return new CallbackBridge(); }
void DeleteCallback(void* callback); { delete callback; }
void setStateChanged(void* callback, void (*ptr)(void*, int))
{
CallbackBridge* bridge = (CallbackBridge*)callback;
bridge->stateChanged = ptr;
}
///... other helper methods
这里的想法是将你的对象视为一个黑盒子(因此无处不在 - 它可以是任何指针,但是从c#你只会看到这个aa SafeHandle / IntPtr并编写你可以PInvoke来创建的辅助方法/删除和修改对象。您可以通过这种方法为对象提供委托来模拟这些虚拟调用。
来自c#的用法可能如下所示:(为简单起见,IntPtr,可以使用SafeHandle):
IntPtr callback = CreateCallback();
SetStateChanged(callback, myCallback);
//somewhere later:
DeleteCallback(callback);
void MyCallback(IntrPtr callback, int state)
{
int someData = SomeOtherHelperMethod(callback);
ConsoleWrite("SomeData of callback is {0} and it has changed it's state to {1}", someData, state);
}
我知道,对于更大的对象来说这有点笨拙,但是如果没有c ++ / cli包装器,我从来没有找到任何更好的方法来处理虚拟调用等所有棘手的情况。
答案 1 :(得分:2)
您的C#类不能替代C ++结构,它具有完全不同的内部组织。由于虚方法,C ++结构有一个v表。它不再是POD类型。它有一个隐藏字段,其中包含指向v表的指针。以及编译器生成的构造函数和析构函数。 v表是指向虚方法的指针数组。 C#类有类似的东西,称为&#34;方法表&#34;,但它的组织完全不同。例如,它将具有System.Object方法,并包含指向所有方法的指针,而不仅仅是虚拟方法。类对象布局也完全不同。在此上下文中,C ++语言中的 struct 关键字只是 class 关键字的替代,默认情况下所有成员都是公开的。
需要使用C ++ / CLI语言编写的包装类。这种语言有一点学习曲线,但如果您有使用.NET和C ++的经验,那么它并不陡峭。这种包装器的Boilerplate代码位于this answer。您可以通过Marshal :: GetFunctionPointerForDelegate()返回的函数指针从本机代码回调到托管代码。
答案 2 :(得分:0)
我听说你没有C ++库的源代码。然后你可以看一下this。
但是将类从非托管dll导出到没有源代码的托管版本对我来说听起来很危险。