1.はじめに
C#のDLLをC言語から呼び出すために、C++/CLIでラッパーを作ったのですが、その場合、まっとうに配置すると、
- C言語のexe
- C++/CLIのラッパーDLL
- C#のDLL
を、同じディレクトリに配置することになります。
C++/CLIのラッパーDLLは、環境変数PATHが通ったディレクトリに配置できますが、C#のDLLはexeと同じ場所でなければなりません。
業務の都合上、これを避けるために、C++/CLIのラッパーDLLで環境変数PATHからC# DLLを検索するようにしました。
2.自前PATH検索
ハンドルのグローバル変数への保存は、C++/CLIでグローバル変数にハンドルを保存する方法 - Qiitaを使用しています。
実際に使用したコードでは、これに加えて、Win32 MutexによるflagInitializeの排他、環境変数PATHの前に呼び出し元exeのディレクトリの追加を行っています。
main.c
#include <stdio.h>
#include "dll1.h"
int main(int argc, char** argv) {
int ret = test1(2, 3);
printf("ret=%d\n", ret);
}
dll1.cpp
#define DLLMAKE
#include "dll1.h"
using namespace System;
using namespace System::IO;
using namespace System::Reflection;
using namespace System::Runtime::InteropServices;
//using namespace Test::DLL2;
#define DLL_NAME "Test.DLL2.dll"
static bool flagInitialize = false;
static GCHandle gct;
static GCHandle gcmi;
int test1(int a, int b) {
if (!flagInitialize) {
System::String^ path = Environment::GetEnvironmentVariable("PATH");
array<System::String^>^ pa = path->Split(';');
bool found = false;
for each (System::String ^ dir in pa) {
System::String^ filename = Path::Combine(dir, DLL_NAME);
if (File::Exists(filename)) {
Assembly^ a = Assembly::LoadFrom(filename);
Module^ m = a->GetModule(DLL_NAME);
Type^ t = m->GetType("Test.DLL2.TestClass1");
MethodInfo^ mi = t->GetMethod("Test1");
gct = GCHandle::Alloc(t);
gcmi = GCHandle::Alloc(mi);
found = true;
break;
}
}
if (!found) {
// エラー
}
}
Type^ t2 = safe_cast<Type^>(gct.Target);
MethodInfo^ mi2 = safe_cast<MethodInfo^>(gcmi.Target);
array<Object^>^ params = gcnew array<Object^>{2, 3};
Object^ obj = mi2->Invoke(t2, params);
int ret = (int)obj;
//return TestClass1::Test1(a, b);
return ret;
}
dll1.h
#ifndef __DLL1_H__
#define __DLL1_H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DLLMAKE
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport)
#endif
DECLSPEC int test1(int a, int b);
#ifdef __cplusplus
}
#endif
#endif
TestClass1.cs
using System;
namespace Test.DLL2
{
public class TestClass1
{
public static int Test1(int a, int b)
{
return a * b;
}
}
}