ネイティブアプリケーションからマネージドDLL(C#)を呼び出す方法(ズバリ2000P)


私にとって大きな壁に突き当たり1ヶ月が過ぎてしまいました、どなたか助けて頂けませんでしょうか。

ベストなズバリサンプルコードを頂いた方に追加で2000P差し上げたいと思います。

Mail : metatrader4.0\gmail.com (\=@)

※はてな会員以外の方でサンプルコードを頂ける場合は、paypal か 銀行振り込みで 2000P = 2000円を差し上げます。
ご連絡下さい。


質問文字数制限のためコメント欄に【本題】を記入いたします。

回答の条件
  • 1人1回まで
  • 登録:2009/05/29 15:18:50
  • 終了:2009/05/30 00:03:03

回答(1件)

id:miyamuko No.1

miyamuko回答回数29ベストアンサー獲得回数112009/05/29 21:48:56

ポイント800pt

サンプルコードではありませんが、クラッシュする原因はたぶん以下のとおりだと思います。

V. Testing the DLLs in MetaTrader 4 にも書いてありますが、

CSharpAssembly.dll と CppStdcallInerfaceWrapper.dll を experts\libraries に置いていませんか?


CSharpAssembly.dll は Terminal.exe -> CppStdcallInerfaceWrapper.dll -> CSharpAssembly.dll

と呼び出されますが、Terminal.exe は CSharpAssembly.dll を見つけることができないため

クラッシュしています。


dll を MetaTrader のインストールディレクトリ(Terminal.exe と同じ場所)に置くと動くと思います。

ちなみに、CLR は以下の順で DLL を検索します (CLR は環境変数 PATH を利用しません)。

  1. GAC(Global Assembly Cache)と呼ばれる共有スペース
  2. 構成ファイルの<codeBase>に記述されている場所
  3. アプリケーションが存在するディレクトリ以下で「アセンブリ名+.dll/.exe」

http://codezine.jp/article/detail/2236


あと、Explanations about the Step-by-Step Guide に書いてあることは要約すると以下のようになります。

  • DebugEntry は必須じゃない。でもあったらデバッグするときに便利
  • リリースビルドするときはデバッグビルドに対して行った設定をもう一度行う必要がある
  • CppStdcallInerfaceWrapper の Hello 関数で確保している char8UnmanArr 配列は GC で自動的に開放されるわけじゃないから delete する必要がある
    • サンプルでは delete までは実装していない
    • オブジェクトの開放用の関数を CppStdcallInerfaceWrapper に作成しておいて、MetaTrader 内からもう使わなくなった時点でその関数を呼び出して開放する必要がある
  • MetaTrader に char8UnmanArr とかコピーを作らずにデータを渡す別の方法としては、System.InteropeServices.GCHandle を使ってオブジェクトにピンをたてることで GC による移動や削除をできなくする方法がある
  • 他には文字列のエンコーディングも気をつける必要がある
    • .NET の文字列は Unicode だけど、MetaTrader は ASCII しかサポートしていない
id:I-_-I

ありがとうございます!!!

てっきりCppStdcallInerfaceWrapper.dllがCSharpAssembly.dllを呼び出すから同じフォルダと思い込んでいました。CLRのDLL検索順序勉強になりました。

CLRのDLL検索順序は大変勉強になりました。

本当にありがとうございます。

2009/05/29 23:59:42
  • id:I-_-I
    【本題】
    トレーディングプラットフォーム Metatrader4(※1) と言うアプリケーションは、様々な市場に対して、搭載しているMQL言語(※2)(ポインタのないCによく似たコンパイラ)を使い独自の取引ルールをシステム化して、自動取引を行えるアプリケーションです。

    この Metatrader4 MQL には外部DLLを呼び出す #import ディレクティブがあり、Windows API 等を呼び出すことが出来る仕様になっており、簡単に API を呼び出せます。
    例)
    #import "user32.dll"
    int MessageBoxA(int hWnd ,string szText, string szCaption, int nType);

    start() {
    ‥ MessageBoxA(100, "テストメッセージ" , "", 0);
    }

    この #import 文を使い C#のDLLを C++/CLIでラップしてコールしたいのです。
    MQL #import --> C++/CLIのラッパーDLL --> C#DLL

    まず Metatrader4(※1) に付属している MQLからDLLをコールするサンプル(※3)を VS2008に読み込みテストしたらうまくコールできていましたので、自分でも簡単なテストコードを書いて実行しても正常に、引数、戻り値付きでコールできることは確認しております。
    MQL #import --> C++/CLI DLL
    【サンプルDLL確認方法】参照。

    次に目的の C#DLL をコールする方法ですが、サイトを彷徨っているとこんな(※4)サイトがあったので早速チャレンジしました(英語は全く読めません(^^;)
    解説されてる方法をVS2005用のソリューションファイル [DotNetDllForMetaTrader4_demo.zip] として下の方にあったので実行してみましたが Metatrader4 がクラッシュしてしまいます。

    ここからプログラミングの深い樹海へ迷い込んでしまいました。

    今までやったことは
    [DotNetDllForMetaTrader4_demo.zip]がおかしいと思いサイトの手順通りやったがダメ。
    DLLの仕組みを調べ __declspec(dllexport) や export "C" , __stdcall(その他の呼び出し規約も) , モジュール定義ファイル(.def) この辺をいじくってもダメ。(当然正しくいじくってはいない可能性大)

    試行錯誤をしてる中でどうも C++/CLI DLL --> C#DLL 呼び出しコードを記述すると Metatrader4 がクラッシュすることに気付いた。
    調べるために、C#,C++,C の実行ファイルから DLLをコール
    C#.exe --> C++/CLI DLL Func1():unmanaged --> C++/CLI DLL Func2():C#DLLをcall
    C++.exe --> C++/CLI DLL Func1():unmanaged --> C++/CLI DLL Func2():C#DLLをcall
    C.exe --> C++/CLI DLL Func1():unmanaged --> C++/CLI DLL Func2():C#DLLをcall
    は正常にコールできた。

    次に
    MQL #import --> C++/CLI DLL Func1():unmanaged --> C++/CLI DLL Func3():C#DLLをcall
    MQL #import --> C++/CLI DLL Func1():unmanaged --> C++/CLI DLL Func2():managed APIをcall --> C++/CLI DLL Func3():C#DLLをcall
    Func1(), Func2() までは正常にコールできたが、どうしても、Func3()をコールした時点でクラッシュしてしまう。
    MQL #import --> C++/CLI DLL Func1() が呼び出せていると言う事は呼び出し規約は__stdcallで間違いない。
    Func2()にも制御が移っているので、C++/CLI内のコードには問題ない。
    Func3()だけなぜコールできないのだろう?

    (※4)サイトの下の方に Explanations about the Step-by-Step Guide 3行目に気になる表現が…でも意味が分からん(-_-)
    もしかして、ネイティブとマネージドのメモリ管理の違いかなと勝手に解釈して再び樹海へ…



    もう能力の限界と思いこちらへ来ました (- -)


    お願いは、MQL #import --> C++/CLIのラッパーDLL --> C#DLL が実行できる Visual Studio 2008(2005) 用のソリューション サンプルコード ファイルをメールで頂けませんでしょうか。

    サンプルコードには、MQL側から見て
    string DllFunc(string);
    int DllFunc(int);
    double DllFunc(double);
    bool DllFunc(bool);
    string[] DllFunc(string[]);
    string[,] DllFunc(string[,]);
    int[] DllFunc(int[]);
    int[,] DllFunc(int[,]);
    double[] DllFunc(double[]);
    double[,] DllFunc(double[,]);
    の各呼び出しサンプルを肝の部分のコメント付きで記述して頂けたらと思います。(その他サンプル追加は大歓迎)




    【サンプルDLL確認方法】
    (付属のサンプルDLLをコールしてみる)
    1、 Metatrader4(※1) をインストールする。
    2、 デモアカウント取得のダイアログが出てくるので適当な情報を書き込み取得する。
    3、 C:\Program Trade Tool\MT4\Alpari_Demo\experts\samples\libraries\ExpertSample.dll ファイルを
    ‥‥ C:\Program Trade Tool\MT4\Alpari_Demo\experts\libraries\ フォルダにコピーする。
    4、 C:\Program Trade Tool\MT4\Alpari_Demo\experts\samples\include\sampledll.mqh ファイルを
    ‥‥ C:\Program Trade Tool\MT4\Alpari_Demo\experts\include\ フォルダにコピーする。(DLLインポート用ヘッダ)
    5、 C:\Program Trade Tool\MT4\Alpari_Demo\experts\samples\ExportFunctions.mq4 ファイルを
    ‥‥ C:\Program Trade Tool\MT4\Alpari_Demo\experts\ フォルダにコピーする。(メインコード)
    6、 Metatrader4 を起動する。
    7、 メニューの[ツール] - [MetaQuotes Language Editer] をクリックして MetaEditer を起動する。
    8、 右ペインに "ExportFunctions.mq4" が表示されてるのでダブルクリックでファイルをオープンする。
    9、 メニュー下のツールボックスのアイコンから [Compile] をクリックして正常にコンパイルされたか確認して
    ‥‥ (下ペインに 0 error(s), 0 warning(s) が表示されればOk)MetaEditerをクローズする。
    10、 Metatrader4 の下ペインの [Experts] タブを選択する。
    ‥‥ (ここにメインコードに記述してある Print文 のメッセージが表示される)
    11、 Metatrader4 の左ペイン下段にある [Expert Advisors]フォルダ(青い帽子をかぶった専門相談員!?)を
    ‥‥ 展開したら "ExportFunctions" が表示されてるのでドラッグして右にあるチャートのどれでも良いので
    ‥‥ ドロップする。
    12、 ダイアログが表示されるので [OK] ボタンを押すと、下ペインExpertタブに、
    ‥‥ メインコードに記述してある Print文 のメッセージが表示される。

    (付属のVC++用サンプルをコンパイルして確認してみる)
    1、 C:\Program Trade Tool\MT4\Alpari_Demo\experts\samples\MT4DLLSample\
    2、 フォルダにに有るVC++プロジェクトをVSに読み込む。
    3、 コンパイルする。
    4、 生成されたDLLを …\libraries\ フォルダにコピーする。
    5、 後は、(付属のサンプルDLLをコールしてみる)10、からの手順と同じ


    (※1) MetaTrader 4 Client Terminal ダウンロードサイト
    ‥‥ http://www.metaquotes.net/downloads/
    ‥‥ ※Vista では C:\Program files\ フォルダにインストールするとトラブル可能性があるので違うフォルダが良い.

    (※2) MetaTrader 4 Client Terminal MQL言語リファレンス
    ‥‥ http://docs.mql4.com/

    (※3) MetaTrader 4 Client Terminal インストール後の DLL サンプルフォルダ
    ‥‥ C:\Metatrader Install Folder\experts\samples\

    (※4) metatrader4 から C# DLL 呼び出しサンプルサイト
    ‥‥ Exporting .NET DLLs with Visual Studio 2005 to be Consumed by Native Applications
    ‥‥ http://www.codeguru.com/csharp/.net/cpp_managed/windowsservices/print.php/c14735

    【環境】
    Vista(メイン)、XP もあります。
    Visual Studio 2008
    スキル:早く初心者から抜け出したい…

  • id:I-_-I
    変更し忘れました。

    C:\Program Trade Tool\MT4\Alpari_Demo\ ==> C:\metatrader インストールフォルダ\
  • id:pahoo
    下記に同様質問があります。
    http://q.hatena.ne.jp/1243530420
  • id:I-_-I
    この方も同じ悩みですね。
    ネイティブであるMT4(metatrader)から直接マネージドであるC#DLLをコールするのは大変難しそうなのでC++/CLIのラッパーを経由してコールしたいと思っています。
  • id:I-_-I
    (※4) metatrader4 から C# DLL 呼び出しサンプルサイト
    Exporting .NET DLLs with Visual Studio 2005 to be Consumed by Native Applications
    http://www.codeguru.com/csharp/.net/cpp_managed/windowsservices/print.php/c14735

    の下の方にこんな文があり気になっています。
    意味が分かる方教えて頂けませんでしょうか。

    Another way of passing data to the unmanaged MetaTrader is not by creating an unmanaged copy, but by using the System.InteropeServices.GCHandle to pin the needed managed object while using it in the unmanaged MetaTrader, so the Garbage Collector doesn't move or delete it.

    Other thing you sould take care of is the ecoding convention. .NET strings are in Unicode, whereas the calling application can support only ASCII, like with MetaTrader.
  • id:ardarim
    c#側で用意したメモリをネイティブ側(MetaTrader)に戻して参照する場合、.NET側でGCが動いてMetaTrader側から参照前(あるいは参照中)にメモリを解放してしまう可能性がある、ということです。
    クラッシュというのはこれが原因の可能性が高いですね。

    で、英語の部分ですが、ネイティブから参照したいメモリはSystem.InteropServices.GCHandleを使ってGC対象にならないように固定(pin)しておく必要がある、ということです。
  • id:I-_-I
    マネージドから抜けるとGCで回収されるのを防ぐために杭を打って固定しておくと言う事ですね。
    ありがとうございました。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません