2012-03-06 12 views
6

मुझे सी ++ प्रोग्राम लेने, सीएलआर लोड करने और सी # लाइब्रेरी में एक फ़ंक्शन कॉल करने की आवश्यकता है। मुझे जिस फ़ंक्शन को कॉल करने की आवश्यकता है वह पैरामीटर के रूप में COM इंटरफ़ेस में लेता है।सीएलआर होस्टिंग: एक मनमाने ढंग से विधि हस्ताक्षर के साथ एक समारोह बुलाओ?

मेरे समस्या, CLR होस्टिंग इंटरफेस सिर्फ आप इस हस्ताक्षर के साथ एक विधि कॉल करने लगता है:

int Foo(String arg) 

उदाहरण के लिए, इस सी ++ कोड भार CLR और "test.exe में P.Test समारोह चलाता है ":

ICLRRuntimeHost *pClrHost = NULL; 
HRESULT hrCorBind = CorBindToRuntimeEx(NULL, L"wks", 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*)&pClrHost); 

HRESULT hrStart = pClrHost->Start(); 

DWORD retVal; 
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(L"C:\\Test.exe", L"P", L"Test", L"", &retVal); 

मुझे क्या करना इस विधि हस्ताक्षर के साथ एक समारोह फोन की आवश्यकता है (ध्यान दें मैं सी # कोड के मालिक हैं, तो मैं इसे बदल सकते हैं):

void SomeFunction(IFoo interface) 

जहां IFoo एक कॉम इंटरफ़ेस है। मैं भी क्या मैं अगर मैं इस तरह एक समारोह कह सकते हैं की जरूरत है कर सकता है:

IntPtr SomeFunction(); 

मैं SomeFunction का निर्माण एक सही प्रतिनिधि तो Marshal.GetFunctionPointerForDelegate का उपयोग हो सकता था। हालांकि, मैं यह नहीं समझ सकता कि होस्टिंग इंटरफेस को एक int func (स्ट्रिंग) हस्ताक्षर के साथ फ़ंक्शन कॉल करने के अलावा कुछ भी कैसे करना है।

क्या कोई जानता है कि सी ++ कोड से सी # फ़ंक्शन को एक अलग हस्ताक्षर के साथ कैसे कॉल करें?

(। नोट मैं इस के लिए C++/CLI उपयोग नहीं कर सकते मैं होस्टिंग API का उपयोग करने की जरूरत है।)

+0

क्या आप सी # प्रोग्राम के साथ बातचीत करने की कोशिश कर रहे हैं (उदा। पैरामीटर के रूप में एक COM इंटरफ़ेस भेजें या एक वापस प्राप्त करें) या बस C++ से C# तक कुछ विधि कॉल करें? – AVIDeveloper

+0

मुझे वास्तव में सी ++ से एक सी # फ़ंक्शन कॉल करने की आवश्यकता है (और एक इंट्राट्रेट वापस सी # से प्राप्त करें, एक int नहीं)।मैं बाकी का ख्याल रख सकता हूं, लेकिन मुझे ऐसा करने का कोई तरीका नहीं दिख रहा है। – LCC

+0

मुझे लगता है कि आप जो कर रहे हैं उसके बारे में कुछ और जानकारी होगी। क्या आप सिर्फ एक चीज करने के लिए सी # कोड प्राप्त करने की कोशिश कर रहे हैं, या फिर आप एक और निरंतर इंटरऑप की उम्मीद कर रहे हैं। यदि आप प्रबंधित और अप्रबंधित दोनों पक्षों को नियंत्रित करते हैं तो सी ++/सीएलआई क्यों संभावना नहीं है? आखिरकार ऐसा कोई तरीका है जिससे आप क्रॉस ओवर को प्रबंधित करने के लिए फिर से परिभाषित कर सकते हैं (इस प्रकार डीएलआईएमपोर्ट का उपयोग कर रहे हैं)। – Guvante

उत्तर

7

संपादित करें: मैं अपने जवाब को अद्यतन करने के 64-बिट मान पास करने के लिए कोड शामिल करने के लिए है, तो वादा किया यहाँ जाता है ..

  • यदि कोई 32-बिट सिस्टम के लिए कम जटिल समाधान में रूचि रखता है तो मैंने मूल उत्तर छोड़ा।

नोट: जब से तुम CorBindToRuntimeEx, जो .net 4.0 में अप्रचलित है का उपयोग कर रहे है, मैं एक .net 2.0 अनुरूप समाधान अच्छे पुराने Win32 एपीआई का उपयोग कर मान लेंगे।

तो, क्रम सी # और C के बीच डेटा पास करने में ++ (हमारे मामले में - एक प्रतिनिधि के IntPtr), हम एक छोटे से Win32 DLL परियोजना, दो सीधी-सपाट तरीकों के साथ, SharedMem नामित पैदा हो जाएगी।

SharedMem.h

#pragma once 

#ifdef SHAREDMEM_EXPORTS 
#define SHAREDMEM_API __declspec(dllexport) 
#else 
#define SHAREDMEM_API __declspec(dllimport) 
#endif 

#define SHAREDMEM_CALLING_CONV __cdecl 

extern "C" { 
    SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV SetSharedMem(ULONGLONG _64bitValue); 
    SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV GetSharedMem(ULONGLONG* p64bitValue); 
} 

अब कार्यान्वयन फ़ाइल के लिए:

SharedMem.cpp

#include "stdafx.h" 
#include "SharedMem.h" 

HANDLE  hMappedFileObject = NULL; // handle to mapped file 
LPVOID  lpvSharedMem = NULL;  // pointer to shared memory 
const int SHARED_MEM_SIZE = sizeof(ULONGLONG); 

BOOL CreateSharedMem() 
{ 
    // Create a named file mapping object 
    hMappedFileObject = CreateFileMapping(
          INVALID_HANDLE_VALUE, 
          NULL, 
          PAGE_READWRITE, 
          0, 
          SHARED_MEM_SIZE, 
          TEXT("shmemfile") // Name of shared mem file 
         ); 

    if (hMappedFileObject == NULL) 
    { 
     return FALSE; 
    } 

    BOOL bFirstInit = (ERROR_ALREADY_EXISTS != GetLastError()); 

    // Get a ptr to the shared memory 
    lpvSharedMem = MapViewOfFile(hMappedFileObject, FILE_MAP_WRITE, 0, 0, 0); 

    if (lpvSharedMem == NULL) 
    { 
     return FALSE; 
    } 

    if (bFirstInit) // First time the shared memory is accessed? 
    { 
     ZeroMemory(lpvSharedMem, SHARED_MEM_SIZE); 
    } 

    return TRUE; 
} 

BOOL SetSharedMem(ULONGLONG _64bitValue) 
{ 
    BOOL bOK = CreateSharedMem(); 

    if (bOK) 
    { 
     ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem; 
     *pSharedMem = _64bitValue; 
    } 

    return bOK; 
} 

BOOL GetSharedMem(ULONGLONG* p64bitValue) 
{ 
    if (p64bitValue == NULL) return FALSE; 

    BOOL bOK = CreateSharedMem(); 

    if (bOK) 
    { 
     ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem; 
     *p64bitValue = *pSharedMem; 
    } 

    return bOK; 
} 
  • ध्यान दें कि सादगी के लिए मैं सिर्फ एक साझा कर रहा 64-बिट मान, लेकिन यह एक सामान्य है सी # और सी ++ के बीच स्मृति साझा करने का तरीका। जैसा कि आप फिट देखते हैं, अन्य डेटा प्रकारों को साझा करने के लिए SHARED_MEM_SIZE और/या कार्यों को जोड़ने के लिए स्वतंत्र महसूस करें।

यह कैसे हम उपरोक्त विधियों का उपभोग करेंगे: हम सेट करने के लिए सी # तरफ SetSharedMem() इस्तेमाल करेंगे प्रतिनिधि के IntPtr 64-बिट मूल्य के रूप में (चाहे अगर कोड एक 32 या एक पर चलता है 64 बिट सिस्टम)।

सी # कोड

namespace CSharpCode 
{ 
    delegate void VoidDelegate(); 

    static public class COMInterfaceClass 
    { 
     [DllImport("SharedMem.dll")] 
     static extern bool SetSharedMem(Int64 value); 

     static GCHandle gcDelegateHandle; 

     public static int EntryPoint(string ignored) 
     { 
      IntPtr pFunc = IntPtr.Zero; 
      Delegate myFuncDelegate = new VoidDelegate(SomeMethod); 
      gcDelegateHandle = GCHandle.Alloc(myFuncDelegate); 
      pFunc = Marshal.GetFunctionPointerForDelegate(myFuncDelegate); 
      bool bSetOK = SetSharedMem(pFunc.ToInt64()); 
      return bSetOK ? 1 : 0; 
     } 

     public static void SomeMethod() 
     { 
      MessageBox.Show("Hello from C# SomeMethod!"); 
      gcDelegateHandle.Free(); 
     } 
    } 
} 
  • नोट आदेश कचरा-एकत्र होने से प्रतिनिधि को रोकने के लिए GCHandle का उपयोग।
  • अच्छे उपायों के लिए, हम वापसी मूल्य का उपयोग सफलता/विफलता ध्वज के रूप में करेंगे।

सी ++ तरफ हम, GetSharedMem() का उपयोग कर 64-बिट मान प्राप्त यह एक समारोह सूचक में बदलने और सी # प्रतिनिधि आह्वान करेंगे।

सी ++ कोड

#include "SharedMem.h" 
typedef void (*VOID_FUNC_PTR)(); 

void ExecCSharpCode() 
{ 
    ICLRRuntimeHost *pClrHost = NULL; 
    HRESULT hrCorBind = CorBindToRuntimeEx(
           NULL, 
           L"wks", 
           0, 
           CLSID_CLRRuntimeHost, 
           IID_ICLRRuntimeHost, 
           (PVOID*)&pClrHost 
          ); 

    HRESULT hrStart = pClrHost->Start(); 

    DWORD retVal; 
    HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
           szPathToAssembly, 
           L"CSharpCode.COMInterfaceClass", 
           L"EntryPoint", 
           L"", 
           &retVal // 1 for success, 0 is a failure 
          ); 

    if (hrExecute == S_OK && retVal == 1) 
    { 
     ULONGLONG nSharedMemValue = 0; 
     BOOL bGotValue = GetSharedMem(&nSharedMemValue); 
     if (bGotValue) 
     { 
      VOID_FUNC_PTR CSharpFunc = (VOID_FUNC_PTR)nSharedMemValue; 
      CSharpFunc(); 
     } 
    } 
} 

मूल उत्तर - 32-बिट सिस्टम

यहाँ के लिए अच्छा है एक समाधान है कि आदेश प्रतिनिधि समारोह में परिवर्तित करने में IntPtr.ToInt32() के प्रयोग पर आधारित है । ptr। int पर जो स्थिर C# EntryPoint विधि से वापस आ गया है।

(*) प्रतिनिधि को कचरे से एकत्र होने से रोकने के लिए GCHandle का उपयोग नोट करें।

सी # कोड

namespace CSharpCode 
{ 
    delegate void VoidDelegate(); 

    public class COMInterfaceClass 
    { 
     static GCHandle gcDelegateHandle; 

     public static int EntryPoint(string ignored) 
     { 
      IntPtr pFunc = IntPtr.Zero; 
      Delegate myFuncDelegate = new VoidDelegate(SomeMethod); 
      gcDelegateHandle = GCHandle.Alloc(myFuncDelegate); 
      pFunc = Marshal.GetFunctionPointerForDelegate(myFuncDelegate); 
      return (int)pFunc.ToInt32(); 
     } 

     public static void SomeMethod() 
     { 
      MessageBox.Show("Hello from C# SomeMethod!"); 
      gcDelegateHandle.Free(); 
     } 
    } 
} 

सी ++ कोड हम एक समारोह सूचक को लौट int मूल्य कन्वर्ट करने के लिए है, इसलिए आवश्यकता होगी हम एक शून्य समारोह ptr को परिभाषित करते हुए बंद शुरू करेंगे। प्रकार:

typedef void (*VOID_FUNC_PTR)(); 

और कोड के बाकी काफी अपने मूल कोड की तरह दिखता है, बदलने और समारोह ptr को क्रियान्वित करने के योग के साथ।

ICLRRuntimeHost *pClrHost = NULL; 
HRESULT hrCorBind = CorBindToRuntimeEx(
          NULL, 
          L"wks", 
          0, 
          CLSID_CLRRuntimeHost, 
          IID_ICLRRuntimeHost, 
          (PVOID*)&pClrHost 
         ); 

HRESULT hrStart = pClrHost->Start(); 

DWORD retVal; 
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
          szPathToAssembly, 
          L"CSharpCode.COMInterfaceClass", 
          L"EntryPoint", 
          L"", 
          &retVal 
         ); 

if (hrExecute == S_OK) 
{ 
    VOID_FUNC_PTR func = (VOID_FUNC_PTR)retVal; 
    func(); 
} 

अतिरिक्त

की ए लिटिल बिट तुम भी क्रम में जो विधि निष्पादित करने के लिए चुनने के लिए string इनपुट का उपयोग कर सकते हैं:

public static int EntryPoint(string interfaceName) 
{ 
    IntPtr pFunc = IntPtr.Zero; 

    if (interfaceName == "SomeMethod") 
    { 
     Delegate myFuncDelegate = new VoidDelegate(SomeMethod); 
     gcDelegateHandle = GCHandle.Alloc(myFuncDelegate); 
     pFunc = Marshal.GetFunctionPointerForDelegate(myFuncDelegate); 
    } 

    return (int)pFunc.ToInt32(); 
} 
  • आप और भी अधिक प्राप्त कर सकते हैं दिए गए स्ट्रिंग इनपुट के अनुसार सही विधि खोजने के लिए प्रतिबिंब का उपयोग करके सामान्य।
+0

आप मान रहे हैं कि IntPtr 32 बिट पूर्णांक में फिट होगा, हमेशा यह मामला नहीं होगा। मुझे लगता है कि मैं पूर्णांक के उच्च/निम्न आधा पाने के लिए दो बार कॉल कर सकता हूं जो कि एक विशाल हैक है। =/ – LCC

+0

सहमत हुए। मैंने अभी अपने पीसी पर ToInt32() और 64-बिट अंतर के बारे में एक अस्वीकरण लिखने के लिए स्विच किया है और मुझे लगता है कि आप पहले से ही वहां हैं। मुझे नहीं लगता कि आपको दो बार फोन करने की ज़रूरत है, लेकिन कुछ किया जाना चाहिए। एक 'MemoryMappedFile' समाधान दिमाग में आता है, लेकिन अभी यह बहुत देर हो चुकी है इसलिए मुझे बाद में अपना जवाब अपडेट करना होगा। – AVIDeveloper

+0

@AVID डेवलपर नोब प्रश्न: 64 बिट्स के मामले में, मेमोरी मैप की गई फाइलें वास्तव में सबसे आसान मार्ग है? इसके अलावा बस इसके चारों ओर एक बड़े प्रकार के मूल्य को पारित करने के लिए अजीब लगता है कि उपकरणों का एक नया सेट आवश्यक है। और, हम प्रकार स्ट्रिंग^वापस सी ++ भूमि के मूल्यों को वापस कर सकते हैं (इसे स्ट्रिंग के रूप में उपयोग करना: 'static_cast (retVal.bstrVal) ', देखें' http://blogs.msdn.com/b/msdnforum/archive/2010/07/09/use-clr4-hosting-api-to-invoke-net-assembly-from-native-c.aspx'), और तार 64 बिट मानों को ले जाने में सक्षम होना चाहिए। –

संबंधित मुद्दे