当C ++调用C#函数时,如何从C#调用C ++委托函数?

时间:2016-05-06 11:02:05

标签: c# c++ com-interop

我想从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 ++回调函数?

2 个答案:

答案 0 :(得分:3)

我不喜欢COM方法(代码和注册太多),我建议你改用PInvokeManaged CLI

但是在您的情况下(假设您已经构建了大型COM基础架构),我可以建议使用少量PInvoke具体的解决方法。 想法是将pointer作为CallBack传递给IntPtr函数(void* C ++等价物)。然后在C#端将其转换回Delegate

  

Marshal.GetDelegateForFunctionPointer

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)

虽然我仍然建议您使用PInvokeManaged CLI,或者至少使用我之前answer的方法,但我想添加COM方法答案。

COM wrappers用于delegates并不是一个好主意。以下代码从CallbackInterface声明为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慢。