95154


## 実現したいこと

アンマネージドアプリの内蔵言語からC#で作成したツール(DLL)を呼び出すこと。

**最低限の目標**

  * アンマネージド用に DllExport した`Funcs.dll` 内にある公開用`static`関数から複数のC#マネージドDLLからなるプログラムを稼働させたい。

**理想の目標**

  * 最低限の目標
  * 内蔵言語(mql5)からはDll内classのインスタンスを取得したいができない(イベントも取得できない)のでDLL内にインスタンスを保持して内蔵言語(mql5)からポーリングで結果取得したい。  
※ アンマネージドアプリの内蔵言語(mql5)からはDll内classのインスタンスを取得できない現象は知識不足による誤った見解の可能性があります。

**解決できなければ**

  * 仕様や制限などで解決できない又は自分にとって難易度が高い場合は名前付きパイプかWebSocketでの実装を考えています。
  * できない原因が特定できれば今後に生かせると思っています。

**スキル**

  * C言語は一時ワンチップマイコンPicで何度か使った程度で知識は浅いです。
  * Visual Studio IDE の力によりC#で動くレベル（エラー処理が貧弱な）のアプリケーションをいくつか作った経験があります。
  * プログラミング初心者から脱皮したいと努力中です。

## 解決したい困ってる現象

説明で使用する用語です。

[mql5](https://www.mql5.com/ja/docs) :
アプリケーションの内蔵言語、C言語と似ているがポインタが無いなど細部の仕様が異なるコンパイラ言語。  
[DllExport](https://github.com/3F/DllExport) :
C#で作成したソース内の関数をUnmanagedから呼び出せるように変更してくれる。  
Funcs.dll : Funcs.csprojで作成。mql5側でimportするDllExportした公開する関数用のDLL。  
公開関数 : `Funcs.dll`内にあるDllExport属性がついたUnmanagedから呼び出せる関数。  
Example.dll : Example.csprojで作成。公開関数から呼ばれるプログラム本体がある。  
本体 : `Example.csproj`内にあるプログラムの本体。NuGetから取得したライブラリなど。マネージドC#で作成。

  * `mql5`で`Funcs.dll`の`公開関数`内で完結する関数は実行できます。
  * `Funcs.dll`内でも`公開関数`外でインスタンス化したオブジェクト内の関数を実行するとエラーになります。
  * `本体`を呼び出す`公開関数`はエラーになります。

## コード

`Funcs.csproj` .Net Framework 4.8.1 x64でビルド

[code]

    namespace NameFuncs
    {
        public class Funcs
        {
            // 以下、公開関数内で完結する関数テスト
            static InnerExample innerExample = new InnerExample();
    
            [DllExport]
            public static int Add_InnerExample_Static(int a, int b) => InnerExample.Addd(a, b);
    
            [DllExport]
            public static int Add_InnerExample_Local(int a, int b)
            {
                var localInnerExample = new InnerExample();
                return localInnerExample.Add(a, b);
            }
    
            [DllExport]
            public static int Add_InnerExample(int a, int b) => innerExample.Add(a, b);
    
    
            // 以下、本体を呼び出す関数テスト
            static OuterExample outerExample = new OuterExample();
    
            [DllExport]
            public static int Add_OuterExample_Static(int a, int b) => OuterExample.Addd(a, b);
    
            [DllExport]
            public static int Add_OuterExample_Local(int a, int b)
            {
                var localOuterExample = new OuterExample();
                return localOuterExample.Add(a, b);
            }
    
            [DllExport]
            public static int Add_OuterExample(int a, int b) => outerExample.Add(a, b);
        }
    
        public class InnerExample
        {
            public int Add(int a, int b) => a + b;
    
            public static int Addd(int a, int b) => a + b;
        }
    }
    
[/code]

`OuterExample.csproj` .Net Framework 4.8.1 x64でビルド

[code]

    namespace NameOuterExample
    {
        public class OuterExample
        {
            public int Add(int a, int b) => a + b;
    
            public static int Addd(int a, int b) => a + b;
        }
    }
    
[/code]

`mql5`

[code]

    #import "Funcs.dll"
       int Add_InnerExample_Static(int a,int b);
       int Add_InnerExample_Local(int a,int b);
       int Add_InnerExample(int a,int b);
    
       int Add_OuterExample_Static(int a,int b);
       int Add_OuterExample_Local(int a,int b);
       int Add_OuterExample(int a,int b);
    #import
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
    {
        Print(Add_InnerExample_Static(2,3));     // 5
        Print(Add_InnerExample_Local(20,30));    // 50
        Print(Add_InnerExample(200,300));        // error
    
        Print(Add_OuterExample_Static(2,3));     // error
        Print(Add_OuterExample_Local(20,30));    // error
        Print(Add_OuterExample(200,300));        // error
    }
    
[/code]

## `mql5` エラーメッセージ

[code]

    TestScript            Unhandled exception 0xE0434352
    TestScript    crash -->  00007FF8E264536C 0F1F440000        nop        [rax+rax+0x0]
    TestScript               00007FF8E2645371 488B8C24C0000000  mov        rcx, [rsp+0xc0]
    TestScript               00007FF8E2645379 4833CC            xor        rcx, rsp
    TestScript               00007FF8E264537C E85F290500        call       0x7ff8e2697ce0
    TestScript               00007FF8E2645381 4881C4D8000000    add        rsp, 0xd8
    TestScript               00007FF8E2645388 C3                ret        
    TestScript  
    TestScript               00007FF8E2645389 CC                int3       
    
    00: 0x00007FF8E264536C
    01: 0x0000006C00000000
    02: 0x0000006C463FBD10
    03: 0x0000006C463FC4D0
    04: 0x00007FF8B6356EAE
    05: 0x00000081E0434352
    
[/code]

## 試したこと

  * ビルドをAnyCPUやx86に変えてビルドしましたがすべてエラーが出ました。
  * 上記コードにないパターンで試行錯誤をやってみましたがうまくいきませんでした。
  * 上記コードは最終的にわかりやすいように最低限のパターンに絞り込みました。
  * Google検索でも解決の糸口を見つけきれませんでした。
  * ChatGPTにプロンプトを変えながら問い合わせましたが全く違う回答ばかりでした。

※上記試行は自身のスキルの低さが原因の可能性があります。

## 環境

Windows 11 HOME 22H2 x64  
Visual Studio 2022 Community Version 17.6.2  
Meta Trader 5 x64  
[MQL5 リファレンス](https://www.mql5.com/ja/docs)  
[DllExport の GitHub](https://github.com/3F/DllExport)

* * *

# radianさんから教えていただいたUnmanagedCallersOnlyを使った解決方法を後学のために残しておきたいと思います。

MainFuncs.csproj

[code]

    namespace NameMainFuncs
    {
        public class MainFuncs
        {
            public int Add(int a, int b) => a + b;
        }
    }
    
[/code]

UnmanagedFuncs.csproj

[code]

    using NameMainFuncs;
    using System.Runtime.InteropServices;
    
    namespace NameExportFuncs
    {
        public class ExportFuncs
        {
            static MainFuncs Funcs = new MainFuncs();
    
            [UnmanagedCallersOnly(EntryPoint = "Add")]
            public static int Add(int a, int b) => Funcs.Add(2, 3);
        }
    }
    
[/code]

UnmanagedFuncs.csproj をエディタで以下を追加

[code]

      <PropertyGroup>
        ...
        <PublishAot>true</PublishAot>
      </PropertyGroup>
    
[/code]

プロジェクト`UnmanagedFuncs`を発行すると２つのDLLが作成される。  
`UnmanagedFuncs.dll`  
`MainFuncs.dll`

main.c

[code]

    #include <stdio.h>
    #include <Windows.h>
    
    typedef int (*Add)(int a, int b);
    
    int main()
    {
        HMODULE hModule = LoadLibrary(L"UnmanagedFuncs.dll");
        if (hModule == NULL)
        {
            printf("Failed to load library.\n");
            getchar();
            return -1;
        }
    
        Add add = (Add)GetProcAddress(hModule, "Add");
        if (add == NULL)
        {
            printf("Failed to get function.\n");
            getchar();
            return -1;
        }
    
        printf("%d\n", add(2, 3));
    
        FreeLibrary(hModule);
        getchar();
        return 0;
    }
    
[/code]

で確認すると 「5」 が表示されました (^^)/

radianさんありがとうございました。


CC BY-SA 4.0
2023-06-09T00:08:58.740
95153
2023-06-11T05:45:02.683
2023-06-11T05:45:02.683
19605
19605
question
c# c dll
アンマネージドアプリから C#で作成したDLLを呼び出すと失敗する場合がある


推測ですが、ネイティブ関数呼び出しではstaticメンバの初期化が動作せず、innerExample
とouterExampleがnullのままの可能性があります。  
別途、staticメンバ初期化用のメソッドを用意して呼び出すなどしてみてはどうでしょう。

OuterExampleは別DLLになっているんでしょうか？  
DllExportがどういう仕組みになっているかは判りませんが、OuterExampleのアセンブリがメモリに読み込まれていないから、インスタンスを作れないのかもしれません。[Assembly.Load](https://learn.microsoft.com/ja-
jp/dotnet/api/system.reflection.assembly.load?view=net-7.0)で動的にアセンブリを読み込んだりすれば解決するかもしれませんが、断言できません。

いずれにせよ、DllExportは.NETの標準の仕組みではないので、普通にC#を使ってる場合には発生しない予測不可能な不具合を踏む可能性はあります。DllExportのリポジトリ自体も3年以上更新されていないので、メンテナンス面も不安が残ります。不具合が発生したときに自力で原因を切り分けて解決出来る能力が無ければ、手を出さない方がいい気もします。  
名前付きパイプ等で解決出来るのであれば、その方が無難でしょう。

ちなみに、.NET7ではネイティブ関数のエクスポートが出来るようになったようです。私は試してませんが、こちらでやってみるのも手かもしれません。

[C#で書いたライブラリをPHPやGoから呼び出す](https://qiita.com/Gaku_Ishii/items/ad4abb1e0073519877d7)

[UnmanagedCallersOnlyAttribute クラス](https://learn.microsoft.com/ja-
jp/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-7.0)


CC BY-SA 4.0
2023-06-09T01:11:06.833
95154
2023-06-09T05:30:45.160
2023-06-09T05:30:45.160
41943
41943
95153
answer