我有一个用C#完成的COM服务器,其中一种方法带有回调函数。
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class REDIServer
{
public bool Connect([MarshalAs(UnmanagedType.FunctionPtr)] ref CallBackFunction callback)
{
// send callback deeper into C# code
return _service.Connect(callback);
}
从该dll生成tlb代码以便可以在我的C ++客户端中使用它时,它看起来像这样:
virtual HRESULT __stdcall Connect (
/*[in,out]*/ long * callback,
/*[out,retval]*/ VARIANT_BOOL * pRetVal ) = 0;
最后,当我尝试使用C ++中的Connect()调用时,我会像这样使用它:
void Callback()
{
cout << "got a callback\n";
}
int main()
{
HRESULT hr = CoInitialize(NULL);
try
{
REDIXLServer::_REDIServerPtr ptr(__uuidof(REDIXLServer::REDIServer));
VARIANT_BOOL ret;
hr = ptr->Connect((long *) &Callback, &ret);
}
catch (_com_error & e)
{
cout << e.ErrorMessage() << endl;
}
该程序在Conect()调用时崩溃。我收到一个消息框“ Managed Debugging Assistant'InvalidFunctionPointerInDelegate已检测到问题...。无效的函数指针已传递到运行时中,以转换为委托。...”
那我做错了什么?谢谢
答案 0 :(得分:0)
首先,如果要与本机COM客户端进行互操作,我建议使用COM events instead of callbacks
此外,我建议不要使用[ClassInterface(ClassInterfaceType.AutoDual)]
,因为它在生成的tlh / tli文件中会产生很多杂物。而是定义一个接口并在您的类中实现它。
public delegate double FunctionCallback(int arg);
[ComVisible(true)]
[Guid("5F11C485-0AA8-461D-BB56-32D620D17311")]
public interface ITestServer
{
double Connect([MarshalAs(UnmanagedType.FunctionPtr)] FunctionCallback callback, int arg);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class TestServer : ITestServer
{
public double Connect(FunctionCallback callback, int arg)
{
Console.Out.WriteLine("In server, calling callback...");
return callback(arg);
}
}
您不需要通过ref(ref CallBackFunction
发送回调,因为您将不会更改它以指向不同的实现,并且由于Callback
与&Callback
相同,因此您无论如何都无法取消引用-这就是它崩溃的原因。
在您的本机客户端中,回调将建模为long
:
double Connect (long callback, long arg );
发送函数指针时,只需将其强制转换为long
:
double triple(int arg) {
return arg * 3.0;
}
double halve(int arg) {
return arg / 2.0;
}
int main() {
CoInitialize(NULL);
server::ITestServerPtr svr(__uuidof(server::TestServer));
printf("tripled: %g\n", svr->Connect((long)triple, 5));
printf("halved: %g\n", svr->Connect((long)halve, 5));
return 0;
}