2011-12-16 28 views
2

我想要的PInvoke到这个C++库函数:封送一个结构与用C·P的方法/调用

int Initialize(Callback* callback); 

struct Callback 
{ 
    //....... 
    void virtual StateChanged(int state) = 0; 
}; 

我已经试过这个天真的C#代码,但它不工作。

[DllImport("CPlusPlusLibrary.dll", SetLastError = true, EntryPoint = "[email protected]@@[email protected]@@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

您需要制作一个C++/CLI包装器。 – 2011-12-16 12:29:19

+0

@Alex Burtsev,您在C#中的回调定义不正确。您正尝试传递C#类的实例,同时在C++ API中接受指针。 C++中的Callback结构基本上是一个(纯粹的)抽象类,尽管您跳过了其余的代码。该结构只包含一个指向虚函数表的指针,所以对于32位进程,大小只有4个字节,对于64位进程,大小只有8个字节。如果您将虚拟函数表中的地址指向C#中的StateChanged指针。你的代码可以工作。 – xInterop 2015-12-13 04:46:40

回答

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#中,你只会看到这一个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包装我从来没有发现任何更好的办法能够处理所有这些棘手的情况下,像

0

我听说你的虚拟来电等。没有C++库的源代码。那么你可以看看this

但是,从非托管dll导出类到没有源代码的托管类对我来说听起来很危险。

2

您的C#类不能替代C++结构,它具有完全不同的内部组织。由于虚拟方法,C++结构具有一个v表。它不再是POD类型。它有一个隐藏字段,其中包含一个指向v表的指针。还有一个编译器生成的构造函数和析构函数。 v表是一个指向虚拟方法的指针数组。 C#类有一些类似的东西,叫做“方法表”,但它的组织方式完全不同。它将包含System.Object方法,并且包含指向所有方法的指针,而不仅仅指向虚拟指针。类对象布局也完全不同。 结构关键字在这种情况下的C++语言只是关键字的替代品,默认情况下所有成员都是公共的。

需要用C++/CLI语言编写的包装类。这种语言有一点学习曲线,但如果你有.NET和C++的经验,那么它就不会陡峭。这种包装的样板代码在this answer。您可以通过由Marshal :: GetFunctionPointerForDelegate()返回的函数指针从本地代码回调到托管代码中。