2012-03-06 2 views
6

C++ 프로그램을 가져와 CLR을로드하고 C# 라이브러리에서 함수를 호출해야합니다. 호출해야하는 함수는 COM 인터페이스를 매개 변수로 사용합니다.CLR 호스팅 : 임의의 메서드 서명을 사용하여 함수를 호출 하시겠습니까?

내 문제는 CLR 호스팅 인터페이스 만이 서명 메소드를 호출 할 것으로 보인다

:

int Foo(String arg) 

예,이 C++ 코드로드 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); 

내가이 방법 서명이있는 함수를 호출하기 만하면됩니다 (필자는 C# 코드를 자신의주의를, 그래서 그것을 변경할 수) :

void SomeFunction(IFoo interface) 

여기서 IFoo는 com 인터페이스입니다. 난 내가 이런 식으로 함수를 호출 할 수 할 경우 무엇을해야 수 :

IntPtr SomeFunction(); 

내가 된 SomeFuncion가 올바른 대리인이 다음 Marshal.GetFunctionPointerForDelegate를 사용하여 구성있을 수 있습니다. 그러나 호스팅 인터페이스가 int func (string) 시그니처가있는 함수를 호출하는 것 이외의 다른 작업을 수행하는 방법을 알아낼 수는 없습니다.

누구나 C# 코드에서 다른 서명으로 C# 함수를 호출하는 방법을 알고 있습니까?

(. 나는 이것을 위해 C++/CLI를 사용할 수 없습니다 나는 호스팅 API를 사용합니다.)

+0

C# 프로그램과 상호 작용 (예 : 매개 변수로 COM 인터페이스 보내기 또는 다시 가져 오기)하거나 C++에서 C#으로 일부 메서드를 호출하려고합니까? – AVIDeveloper

+0

저는 C++에서 하나의 C# 함수를 호출해야합니다 (그리고 int가 아닌 C#에서 IntPtr을 다시 얻습니다).나는 나머지를 돌볼 수는 있지만 이것을 할 방법이 없다. – LCC

+0

당신이하고있는 일에 대해 좀 더 자세하게 생각하면 도움이 될 것 같습니다. C# 코드를 가져 와서 한 가지 작업을 수행하거나 더 많은 상호 운용성을 기대하고 있습니까? 관리되는 쪽과 관리되지 않는 쪽 모두를 제어하는 ​​경우 C++/CLI가 가능하지 않은 이유는 무엇입니까? 마지막으로 관리되는 크로스 오버를 다시 정의 할 수있는 방법이 있습니다 (따라서 DllImport 사용). – Guvante

답변

7

편집 : 내가 그래서, 64 비트 값을 전달하기위한 코드를 포함하는 내 대답을 업데이트 약속 여기가 ..

  • 32 비트 시스템에서 덜 복잡한 솔루션에 관심이있는 사람은 원래 답변을 남겼습니다.

참고 : 당신이 .NET 4.0에서 사용되지 않습니다 CorBindToRuntimeEx를 사용하고 있기 때문에, 나는 그물 좋은 오래된의 Win32 API를 사용하여 2.0 호환 솔루션을 가정합니다.

그래서, C# 및 C 사이에 데이터를 전달하기 위해 ++ (우리의 경우 - 대리인의 IntPtr), 우리는 두 직선 전달 방법과, SHAREdmem이라는 이름의 작은에서 Win32 DLL 프로젝트를 만듭니다.

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; 
} 
  • 참고 : 구현 파일에 대한 지금 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); 
    } 
    

    64 비트 값이지만 일반적입니다. C#과 C++ 사이에서 메모리를 공유하는 방법. SHARED_MEM_SIZE를 확대하거나 다른 데이터 유형을 공유하기 위해 함수를 자유롭게 추가하십시오.

이 우리는 위의 방법을 소비하는 것 어떻게 우리가 설정하기 위해 C#을 측면에 SetSharedMem()를 사용할 것이다 대리인의 IntPtr 64 비트 값 (코드 32 또는에서 실행에 관계없이 경우 64 비트 시스템).

C# 코드

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 용도.
  • 좋은 측정을 위해 반환 값을 성공/실패 플래그로 사용합니다.

C++ 측에서는 GetSharedMem()을 사용하여 64 비트 값을 추출하고이를 함수 포인터로 변환하고 C# 대리자를 호출합니다.

C++

#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 비트 시스템 여기서

좋은 대리자 FUNC를 변환하기 위해 IntPtr.ToInt32()를 사용하여 기반으로하는 솔루션입니다 . ptr. 정적 C# EntryPoint 메서드에서 반환되는 int으로 변경하십시오.

(*) 위임자가 가비지 수집되지 않도록하려면 GCHandle을 사용합니다.

C# 코드

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

C++ 코드 우리는 함수 포인터로 반환 int 값을 변환해야합니다, 그래서 우리는 무효 기능 PTR을 정의하여 시작합니다. 타입 :

typedef void (*VOID_FUNC_PTR)(); 

그리고 코드의 나머지 부분은 원래 코드와 비슷하게 보이고 ptr 함수를 변환하고 실행합니다. 당신은 더 얻을 수

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(); 
} 
  • 를 : 추가

    의 약간은 또한 실행하기 위해 어떤 방법을 선택하기 위해 string 입력을 사용할 수있다

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

    주어진 문자열 입력에 따라 올바른 메소드를 찾기 위해 리플렉션을 사용하여 일반.

+0

IntPtr이 32 비트 정수에 맞을 것으로 가정하고 있습니다. 항상 그렇지는 않습니다. 나는 거대한 해킹 인 정수의 상한/하한 반을 얻기 위해 두 번 호출 할 수 있다고 생각한다. =/ – LCC

+0

동의. ToInt32() 및 64 비트 간격에 대한 면책 ​​조항을 작성하기 위해 필자는 지금 PC를 켜 놓았습니다. 나는 두 번 전화 할 필요가 없다고 생각하지만, 뭔가해야한다. 'MemoryMappedFile' 솔루션이 마음에 들지만, 지금은 너무 늦어서 나중에 내 대답을 업데이트해야 할 것입니다. – AVIDeveloper

+0

@AVideveloper Noob 질문 : 64 비트의 경우 메모리 매핑 된 파일이 실제로 가장 쉬운 경로입니까? 그 외에도 더 큰 유형의 가치를 전달하기 위해서는 그 도구의 완전히 새로운 세트가 필요하다는 것이 이상하게 보입니다. 그리고 우리는 String 형의 값을 C++으로 반환 할 수 있습니다. (문자열로 사용하면'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 비트 값을 전달할 수 있어야합니다. –