Edit: tôi hứa sẽ cập nhật câu trả lời của tôi để bao gồm mã để thông qua giá trị 64-bit, vì vậy đây là ..
- Tôi để lại câu trả lời gốc nếu ai đó quan tâm đến giải pháp ít phức tạp hơn cho hệ thống 32 bit.
Lưu ý: Vì bạn đang sử dụng CorBindToRuntimeEx
, đó là lỗi thời trong .net 4.0, tôi sẽ giả định một .net 2.0 giải pháp phù hợp sử dụng tốt Win32 API cũ.
Vì vậy, để truyền dữ liệu giữa C# và C++ (trong trường hợp của chúng tôi - các IntPtr
của một đại biểu), chúng ta sẽ tạo một dự án Win32 DLL nhỏ, tên SharedMem, với hai phương pháp thẳng về phía trước.
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);
}
Bây giờ cho các tập tin thực hiện:
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;
}
- Lưu ý rằng vì đơn giản tôi chỉ chia sẻ một Giá trị 64 bit, nhưng đây là tổng quát cách chia sẻ bộ nhớ giữa C# và C++. Vui lòng phóng to SHARED_MEM_SIZE và/hoặc thêm các chức năng để chia sẻ các loại dữ liệu khác mà bạn thấy phù hợp.
Đây là cách chúng tôi sẽ tiêu thụ các phương pháp trên: chúng tôi sẽ sử dụng SetSharedMem()
trên C# bên nhằm thiết lập của IntPtr
như một giá trị 64-bit (không phân biệt nếu mã chạy trên một phiên bản 32 hoặc một đại biểu Hệ thống 64 bit).
C# Mã
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();
}
}
}
- Lưu ý việc sử dụng
GCHandle
để ngăn chặn các đại biểu từ việc thu gom rác.
- Để có các biện pháp tốt, chúng tôi sẽ sử dụng giá trị trả về làm cờ thành công/lỗi.
Trên mặt C++, chúng tôi sẽ trích xuất giá trị 64 bit bằng cách sử dụng GetSharedMem()
, chuyển đổi nó thành con trỏ hàm và gọi đại biểu C#.
C++ Mã
#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();
}
}
}
The Original trả lời - Tốt cho hệ thống 32-bit
Dưới đây là một giải pháp đó là dựa vào việc sử dụng IntPtr.ToInt32()
để chuyển đổi các Func đại biểu . ptr. đến số int
được trả về từ phương thức tĩnh C# EntryPoint.
(*) Lưu ý việc sử dụng GCHandle
để ngăn đại biểu không bị thu gom rác thải.
C# Mã
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++ Mã Chúng tôi sẽ cần phải chuyển đổi trở int
giá trị cho một con trỏ hàm, vì vậy chúng tôi sẽ bắt đầu bằng cách xác định một void function ptr. loại:
typedef void (*VOID_FUNC_PTR)();
Và phần còn lại của mã trông khá giống mã ban đầu của bạn, với việc bổ sung chuyển đổi và thực thi hàm 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();
}
một chút tắm
Bạn cũng có thể tận dụng các string
đầu vào trong nhằm chọn ra những phương pháp để thực hiện:
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();
}
- Bạn có thể nhận được nhiều hơn chung bằng cách sử dụng sự phản chiếu để tìm ra phương pháp đúng theo đầu vào chuỗi đã cho.
Bạn đang cố gắng tương tác với chương trình C# (ví dụ: gửi giao diện COM dưới dạng tham số hoặc lấy lại một tham số) hoặc chỉ gọi một số phương thức từ C++ đến C#? – AVIDeveloper
Tôi thực sự chỉ cần gọi một hàm C# từ C++ (và lấy lại IntPtr từ C#, không phải là int).Tôi có thể lo phần còn lại, nhưng tôi không thấy cách nào để làm điều này. – LCC
Tôi nghĩ rằng một số chi tiết hơn về những gì bạn đang làm sẽ giúp đỡ. Bạn đang cố gắng để có được mã C# để làm một điều sau đó trở lại, hoặc bạn đang mong đợi một interop liên tục hơn. Tại sao C++/CLI không phải là một khả năng nếu bạn kiểm soát cả hai bên được quản lý và không được quản lý? Cuối cùng là có bất kỳ cách nào bạn có thể xác định lại chéo qua được quản lý bắt đầu (do đó bằng cách sử dụng DllImport). – Guvante