2016-05-06 29 views
1

C++からC#ライブラリを呼びたいと思います。C++がC#関数を呼び出すときに、C#から委譲されたC++関数を呼び出す方法は?

C#ライブラリは、結果を報告するために委任された関数 を要求します。

多分私の目的は混乱しています:概念は、C++のC#関数を呼び出し、C#関数はC++からコールバック関数を呼び出します。

私はC#コールのC++コールバック関数でブロックされていますが、COM相互運用機能は私にとっては不可解です。

私のサンプルコードは、可能:

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++コード: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。しかし、行 CSharpDLLPtr-> CalltheCallbackFun(& cppcallback、& aa);

は、QueryInterface、GetTypeInfo関数に入り、次にC++ main関数に直接戻ります。 C#の行です。

Console.Write("before call callbackFun\n"); 

に到達しませんでした。

C#でC++コールバック関数を登録するにはどうすればよいですか?

答えて

3

私はCOMのアプローチ(コードと登録が多すぎます)が嫌いです。代わりにPInvokeManaged CLIを使用することをおすすめします。

あなたのケースでは(私はすでに大きなCOMインフラストラクチャを構築していると仮定します)、少し具体的な回避策を使うことをお勧めします。PInvoke pointerCallBackに、IntPtrvoid* C++と同等)として渡すことをお勧めします。そして、C#の側に

Marshal.GetDelegateForFunctionPointer

COMバックDelegateに変換IntPtrを台無しにしません。 IntPtrCOMインターフェイスに変換された方法を確認しました。 x64の場合はLongLongx86の場合はLongとなります。その型はポインタを保持するのに理想的です(BTWでは、クラスにポインタを渡すことができますCallback)。つまり、AnyCPUの設定は役に立たなくなります。

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++クライアント部分:ここで

は、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 
+0

ありがとう、私はここにコメントする必要があります タイプ交換することがより良いです:私は 'var'タイプの交換について理解していなかった@GaigerChen VAR より良い互換性 –

+0

ため NativeDelegateType などを?トピックについては、 'PInvoke'を使用して追加することには多くの利点があります。ラッパーを記述する必要はなく、' C++配列 '、' LPCTSTR'と 'Structs'を明示的に変換することなく渡すことができます。 –

+0

私の環境では:VS2005、varの使用はコンパイラによって渡されませんでしたが、NativeDelegateTypeは可能でした。 –

1

私はまだPInvokeを使用することをお勧めしますが、とManaged CLIまたは少なくとも私の以前のからのアプローチCOM接近回答を追加します。

COM wrappersdelegatesに使用することはお勧めできません。次のコードはCallbackInterfaceからC#に宣言し、これを実装するオブジェクトを取ります。interfaceです。あなたは特別なオブジェクトを作成する必要がありC++からこのcallbackを使用するためには

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; 
     } 
    } 
} 

。ここでは、プログラム全体のコードは次のとおりです。

#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特定のオブジェクト(BSTRSAFEARRAYなど)の場合は、PInvokeよりもゆっくりと動作します。

関連する問題