LoginSignup
0
0

More than 1 year has passed since last update.

C++/CLIでC# DLLを環境変数PATHから検索

Last updated at Posted at 2021-12-15

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

3.参考

C++/CLIでメソッド名を指定して実行する - プログラムを書こう!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0