我想从C ++调用一个C#库。
C#库,请求委派的函数, 为了报告结果。
也许我的目的令人困惑:概念是,C ++调用C#函数,C#函数将从C ++调用回调函数。
我在C#调用C ++回调函数中被阻止,COM Interop对我来说是神秘的。
我的示例代码为:
C#代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace CSharpLibraryNameSpace
{
// Interface declaration.
public delegate int NativeDelegateType(int x);
//public delegate int NativeDelegateType([In, MarshalAs(UnmanagedType.LPStr)] string arg1);
public interface ManagedInterface
{
int Add(int Number1, int Number2);
int CalltheCallbackFun(NativeDelegateType callbackFun);
};
// Interface implementation.
public class ManagedCSharpClass : ManagedInterface
{
public int Add(int Number1, int Number2)
{
Console.Write("Add\n");
return Number1 + Number2;
}
public int
CalltheCallbackFun(
/*[MarshalAs(UnmanagedType.FunctionPtr)]*/ NativeDelegateType callbackFun)
{
Console.Write("BB\n");
string str;
str = "AAA";
unsafe
{
fixed (char* p = str)
{
Console.Write("before call callbackFun\n");
callbackFun(0x01);
}
}
return 0;
}
}
}
C ++代码:
#include <windows.h>
// Import the type library.
#import "CSharpLibrary.tlb" raw_interfaces_only
using namespace CSharpLibrary;
typedef void (__stdcall * C_Callback)(int);
__declspec(dllexport) int __stdcall theCallback(void)
{
return 0;
}/*theCallback*/
class CPPcallback :public _NativeDelegateType
{
public:
CPPcallback(){};
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ UINT *pctinfo)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo **ppTInfo)
{
if (ppTInfo == NULL)
return E_INVALIDARG;
*ppTInfo = NULL;
if(iTInfo != 0)
return DISP_E_BADINDEX;
AddRef(); // AddRef and return pointer to cached
// typeinfo for this object.
*ppTInfo = NULL;
return NOERROR;
}
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [in] */ REFIID riid,
/* [size_is][in] */ LPOLESTR *rgszNames,
/* [in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ DISPID *rgDispId)
{
return E_NOTIMPL;
}
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS *pDispParams,
/* [out] */ VARIANT *pVarResult,
/* [out] */ EXCEPINFO *pExcepInfo,
/* [out] */ UINT *puArgErr)
{
return 0;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(/* [in] */ REFIID riid,
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{
if (riid == IID_IUnknown)
{
*ppvObject = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
if (riid == IID_IDispatch) {
*ppvObject = static_cast<IDispatch*>(this);
AddRef();
return S_OK;
}
//if (riid == IID_CPPcallback )
{
*ppvObject = static_cast<CPPcallback*>(this);
AddRef();
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef( void)
{
return InterlockedIncrement(&_refCount);
}
virtual ULONG STDMETHODCALLTYPE Release( void)
{
return InterlockedDecrement(&_refCount);
}
private:
long _refCount;
};
int main(int argc, char *argv[])
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
ManagedInterfacePtr CSharpDLLPtr(__uuidof(ManagedCSharpClass));
long lResult = 0;
// Call the Add method.
CSharpDLLPtr->Add(5, 10, &lResult);
long aa;
aa = 3;
CPPcallback cppcallback;
CSharpDLLPtr->CalltheCallbackFun(&cppcallback, &aa);
wprintf(L"The result is %d\n", lResult);
// Uninitialize COM.
CoUninitialize();
return 0;
}
C ++调用C#函数有效。但行 CSharpDLLPtr-&gt; CalltheCallbackFun(&amp; cppcallback,&amp; aa);
将进入函数QueryInterface,GetTypeInfo然后直接返回C ++ main函数。那是C#中的行
Console.Write("before call callbackFun\n");
不会到达。
如何在C#中注册C ++回调函数?
答案 0 :(得分:3)
我不喜欢COM
方法(代码和注册太多),我建议你改用PInvoke
和Managed CLI
。
但是在您的情况下(假设您已经构建了大型COM
基础架构),我可以建议使用少量PInvoke
具体的解决方法。
想法是将pointer
作为CallBack
传递给IntPtr
函数(void*
C ++等价物)。然后在C#端将其转换回Delegate
COM
不会破坏IntPtr
。我已检查IntPtr
如何转换为COM
界面。事实证明,在x64
上它变为LongLong
而在x86
时变为Long
。因此,该类型是保持指针的理想选择(顺便说一句,你可以将指针传递给Callback
内的类)。这意味着AnyCPU
配置将毫无用处。
以下是代码示例C#重写部分:
using System;
using System.Runtime.InteropServices;
namespace CSharpLibraryNameSpace
{
// Any delegate decorated as for PInoke
public delegate int NativeDelegateType([MarshalAs(UnmanagedType.LPWStr)] string strMsg);
// Interface declaration.
[ComVisible(true)]
public interface ManagedInterface
{
int Add(int Number1, int Number2);
int CalltheCallbackFun(IntPtr callbackFnPtr);
};
// Interface implementation.
[ComVisible(true)]
public class ManagedCSharpClass : ManagedInterface
{
public int Add(int Number1, int Number2)
{
Console.Write("Inside MANAGED Add Num1={0} Num2={1}\n", Number1, Number2);
return Number1 + Number2;
}
public int CalltheCallbackFun(IntPtr callbackFnPtr)
{
Console.Write("Inside MANAGED CalltheCallbackFun Before Call ptr={0}\n", callbackFnPtr);
//Convert IntPtr to Delegate
NativeDelegateType callback =
Marshal.GetDelegateForFunctionPointer(callbackFnPtr,
typeof(NativeDelegateType)) as NativeDelegateType;
int nRet = callback("Message from C# :)");
Console.Write("Inside MANAGED CalltheCallbackFun After Call Result={0}\n", nRet);
return nRet;
}
}
}
C++
客户端部分:
#import "CSharpLibrary.tlb" raw_interfaces_only
using namespace CSharpLibrary;
int SimpleCallbackFunction(const wchar_t* pszMsg)
{
wprintf(L"Inside C++ UNMANAGED Callback Param=\"%s\"\n", pszMsg);
return 77;
}
int main()
{
wprintf(L"Inside C++ UNMANAGED Start\n");
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
ManagedInterfacePtr CSharpDLLPtr(__uuidof(ManagedCSharpClass));
long lResult = 0;
// Call the Add method.
CSharpDLLPtr->Add(5, 10, &lResult); //lResult == 15
//! For x64 you need to convert to LongLong
CSharpDLLPtr->CalltheCallbackFun((long)SimpleCallbackFunction, &lResult);
wprintf(L"Inside C++ UNMANAGED Main result is %d\n", lResult);
// Uninitialize COM.
CoUninitialize();
return 0;
}
输出结果为:
Inside C++ UNMANAGED Start
Inside MANAGED Add Num1=5 Num2=10
Inside MANAGED CalltheCallbackFun Before Call ptr=2625906
Inside C++ UNMANAGED Callback Param="Message from C# :)"
Inside MANAGED CalltheCallbackFun After Call Result=77
Inside C++ UNMANAGED Main result is 77
答案 1 :(得分:1)
虽然我仍然建议您使用PInvoke
和Managed CLI
,或者至少使用我之前answer
的方法,但我想添加COM
方法答案。
将COM wrappers
用于delegates
并不是一个好主意。以下代码从Callback
将Interface
声明为C#
,并使用实现此interface
的对象。
using System;
using System.Runtime.InteropServices;
namespace CSharpLibraryNameSpace
{
//Callback Interface declaration
[ComVisible(true)]
public interface CallbackInterface1
{
int InvokeUnmanaged(string strMsg);
}
[ComVisible(true)]
public interface ManagedInterface
{
int Add(int Number1, int Number2);
int CalltheCallbackFun(CallbackInterface1 callback);
};
// Interface implementation.
[ComVisible(true)]
public class ManagedCSharpClass : ManagedInterface
{
public int Add(int Number1, int Number2)
{
Console.Write("Inside MANAGED Add Num1={0} Num2={1}\n", Number1, Number2);
return Number1 + Number2;
}
public int CalltheCallbackFun(CallbackInterface1 callback)
{
Console.Write("Inside MANAGED CalltheCallbackFun Before Call\n");
int nRet = callback.InvokeUnmanaged("Message from C# :)");
Console.Write("Inside MANAGED CalltheCallbackFun After Call Result={0}\n", nRet);
return nRet;
}
}
}
要从callback
使用此C++
,您需要制作特殊对象。这是整个程序代码:
#import "CSharpLibrary.tlb" raw_interfaces_only
using namespace CSharpLibrary;
class CPPcallback :public CallbackInterface1
{
public:
CPPcallback(){};
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ UINT *pctinfo)
{
*pctinfo = 1;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo **ppTInfo)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [in] */ REFIID riid,
/* [size_is][in] */ LPOLESTR *rgszNames,
/* [in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ DISPID *rgDispId)
{
return E_NOTIMPL;
}
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS *pDispParams,
/* [out] */ VARIANT *pVarResult,
/* [out] */ EXCEPINFO *pExcepInfo,
/* [out] */ UINT *puArgErr)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(/* [in] */ REFIID riid,
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{
if (riid == IID_IUnknown)
{
*ppvObject = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
if (riid == IID_IDispatch) {
*ppvObject = static_cast<IDispatch*>(this);
AddRef();
return S_OK;
}
if (riid == __uuidof(CallbackInterface1))
{
*ppvObject = static_cast<CallbackInterface1*>(this);
AddRef();
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef( void)
{
return InterlockedIncrement(&_refCount);
}
virtual ULONG STDMETHODCALLTYPE Release( void)
{
return InterlockedDecrement(&_refCount);
}
virtual HRESULT __stdcall InvokeUnmanaged (
/*[in]*/ BSTR strMsg,
/*[out,retval]*/ long * pRetVal ) override
{
wprintf(L"Inside C++ UNMANAGED Callback Param=\"%s\"\n", strMsg);
*pRetVal = 77;
return S_OK;
}
private:
long _refCount;
};
int main()
{
wprintf(L"Inside C++ UNMANAGED Start\n");
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
ManagedInterfacePtr CSharpDLLPtr(__uuidof(ManagedCSharpClass));
long nRes = 0;
// Call the Add method.
CSharpDLLPtr->Add(5, 10, &nRes);
//Callback holder instance
CPPcallback cppcallback;
//Call COM Managed method which calls our Callback
CSharpDLLPtr->CalltheCallbackFun(&cppcallback, &nRes);
wprintf(L"Inside C++ UNMANAGED Main result is %d\n", nRes);
// Uninitialize COM.
CoUninitialize();
return 0;
}
程序输出为:
Inside C++ UNMANAGED Start
Inside MANAGED Add Num1=5 Num2=10
Inside MANAGED CalltheCallbackFun Before Call
Inside C++ UNMANAGED Callback Param="Message from C# :)"
Inside MANAGED CalltheCallbackFun After Call Result=77
Inside C++ UNMANAGED Main result is 77
如果你隐藏IDispatch
实现背后的代码将几乎与之前的答案中的代码一样短但是你将面对所有COM
个特定对象(如BSTR
,{{ 1}})它绝对比SAFEARRAY
慢。