編集: 64ビット値を渡すためのコードを含めるように回答を更新することを約束したので、ここに行きます。
- 誰かが32ビットシステムのそれほど複雑でないソリューションに興味があるなら、私は元の答えを残しました。
注: .net 4.0では廃止されたを使用しCorBindToRuntimeEx
ているため、古き良きWin32APIを使用した.net2.0準拠のソリューションを想定します。
したがって、C#とC ++(この場合IntPtr
はデリゲートの)の間でデータを渡すために、2つの簡単なメソッドを使用してSharedMemという名前の小さなWin32DLLプロジェクトを作成します。
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ビット値を共有しているだけですが、これはC#とC++の間でメモリを共有する一般的な方法であることに注意してください。必要に応じて他のデータ型を共有するために、SHARED_MEM_SIZEを拡大したり、関数を追加したりしてください。
これが上記のメソッドを使用する方法です。デリゲートを64ビット値としてSetSharedMem()
設定するためにC#側で使用します(コードが32ビットシステムで実行されているか64ビットシステムで実行されているかに関係なく)。IntPtr
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 ++側では、を使用して64ビット値を抽出しGetSharedMem()
、それを関数ポインターに変換して、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ビットシステムに適しています
IntPtr.ToInt32()
これは、デリゲート関数を変換するために使用することに基づくソリューションです。ptr。int
静的C#EntryPointメソッドから返されるに。
(*)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
値を関数ポインターに変換する必要があるため、まずvoid関数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();
}
- 与えられた文字列入力に従って正しいメソッドを見つけるためにリフレクションを使用することで、さらに一般的なものを得ることができます。