
f()10分でできるMQL5 のためのDLL (パート II): Visual Studio 2017で作成
イントロダクション
この記事は、Visual Studio 2005/2008 でDLLを作成する、以前に公開された 記事 のアイデアをもとにしています。 元の基本となる記事との関連性は失われていませんが、このトピックに興味がある場合は、まず最初の記事を読んでください。 しかし、前回の記事から時間が経過しているので、現在の Visual Studio 2017 には、更新されたインターフェイスがあります。 また、MetaTrader5プラットフォームにも新しい機能が追加されました。 明らかに、最新の情報を更新し、新機能を検討する必要があります。 この記事では、Visual Studio 2017 のDLLプロジェクトの開発から、ターミナルへの接続とその使用に至るまでのすべての段階を扱います。
この記事は、C++ ライブラリをターミナルに作成して接続する方法を学習したい初心者を対象とします。
そもそも、なぜDLLをターミナルに接続するのでしょうか。
一部の開発者は、このような接続は必須ではなく、必要な関数をMQLで実装できるため、ライブラリをターミナルに接続する必要はないと考えるかもしれません。 この意見はある意味妥当です。 ライブラリを必要とするタスクはほとんどありません。 必要なタスクのほとんどは MQL ツールを使用して解決することができます。 さらに、ライブラリを接続する際には、このライブラリが使用されている エキスパートアドバイザー やインジケータがそのDLL なしでは動作できないことを理解しておく必要があります。 そのようなアプリケーションをサードパーティに転送する必要がある場合は、2つのファイル (アプリケーション自体とライブラリ) を転送する必要があります。 場合によっては、不便を通り越して不可能なこともあります。 もう1つの弱点は、ライブラリは安全ではなく、有害なコードを含む可能性があるという点です。
しかし、それを差し置いても、ライブラリには利点があり、間違いなくデメリットを上回ります。 例:
- ライブラリは MQLでは解決できない問題を解決するのに役立ちます。 たとえば、添付ファイル付きのメールを送信する必要がある場合です。 Dll は Skype に書き込むことができます。 エトセトラ
- いくつかのMQLで実装できるタスクは、ライブラリより速く、より効率的に実行することができます。 これには、HTML ページの解析と正規表現が含まれます。
このように複雑なタスクを解決するには、自分のスキルを習得し、ライブラリを作成して接続する方法を適切に学習する必要があります。
今回のプロジェクトでは、DLL の使用の "長所" と "短所 " を検討しました。 ここで、Visual Studio 2017 を使用してDLL を作成するステップを段階的に検討してみましょう。
シンプルなDLL の作成
全体のプロセスは、すでに元の記事で説明されていました. ここでは、ソフトウェアの更新と変更を考慮して繰り返します。
Visual Studio 2017 を開き、[ファイル]-> [新規] → [プロジェクト] に移動します。 新しいプロジェクトウィンドウの左の部分で、Visual C++ の一覧を展開し、そこから Windows デスクトップを選択します。 中央部の Windows デスクトップウィザードの行を選択します。 下の部分のインプットフィールドを使用して、プロジェクト名を編集することができます (意味がわかる名前を設定することをお勧めします) 。それから、プロジェクトの場所を設定します (提案どおりのままにすることをお勧めします)。 [OK] をクリックし、次のウィンドウに進みます。
ドロップダウンリストからダイナミックリンクライブラリ (DLL) を選択し、「エクスポートシンボル 」をチェックします。 この項目の確認はオプションですが、初心者の方にお勧めです。 この場合、プロジェクトファイルにデモコードが追加されます。 このコードは、表示してから削除またはコメントすることができます。 "OK " をクリックすると、プロジェクトファイルが作成され、編集できるようになります。 ただし、最初にプロジェクトの設定を考慮する必要があります。 まず、MetaTrader5 は64ビットライブラリでしか動作しないことを覚えておいてください。 32ビットのDLL を接続しようとすると、次のメッセージが表示されます。
'E:\...\MQL5\Libraries\Project2.dll' is not 64-bit version
Cannot load 'E:\MetaTrader5\MQL5\Libraries\Project2DLL' [193]
したがって、このライブラリを使用することはできません。
その反対の制限は、MetaTrader4 のDLL にもあります。64 ビットDLL は接続できませんが、32ビットのライブラリのみが使用できます。 このことを念頭に置いて、プラットフォームに適したバージョンを作成してください。
次に、プロジェクト設定に進みます。 "プロジェクト " メニューの [名前のプロパティ... "、ここで " Name "は作成段階で開発者が指定したプロジェクト名です。 これより、さまざまな設定のウィンドウが開きます。 まず、Unicode を有効にする必要があります。 ウィンドウの左の部分では、 "一般 " を選択します。 右側の部分では、最初の列のヘッダ行を選択します: "文字セット "。 ドロップダウンリストが2番目の列で使用できるようになります。 このリストから「Unicode 文字セットを使用する」を選択します。 場合によっては、Unicode サポートは必要ありません。 そのようなケースについては後で説明します。
プロジェクトプロパティのもう1つの便利な (ただし必要ではない) 変更: 完成したライブラリを、ターミナルの "Library" フォルダにコピーします。 元の記事では、プロジェクトの "一般" 要素の同じウィンドウにある "Output ディレクトリ " パラメータを変更することによってこれを行いました。 Visual Studio 2017 で行う必要はありません。 このパラメータは変更しないでください。 ただし、 "ビルドイベント " 項目に注意してください: "ポストビルドイベント " サブ要素を選択する必要があります。 "コマンドライン " パラメータは、右側のウィンドウの最初の列に表示されます。 これを選択すると、2番目の列に編集可能なリストが開きます。 Visual Studio 2017 がライブラリをビルドした後に実行するアクションのリストである必要があります。 次の行をこのリストに追加します。
xcopy "$(TargetDir)$(TargetFileName)" "E:\...\MQL5\Libraries\" /s /i /y
...の代わりに、適切なターミナルフォルダへの完全パスを指定する必要があります。 ライブラリのビルドが成功すると、指定したフォルダにDLL がコピーされます。 "出力ディレクトリ " 内のすべてのファイルは、この場合には保存されますが、さらなるバージョン管理の開発にとって重要です。
直近の重要なプロジェクトのセットアップステップは、次のとおりです。 ライブラリが既にビルドされており、ターミナルで使用できる1つの関数が含まれていることを想像してください。 この関数に次のシンプルなプロトタイプがあるとします。
int fnExport(wchar_t* t);
この関数は、次のようにターミナルスクリプトから呼び出すことができます。
#import "Project2.dll" int fnExport(string str); #import
ただし、この場合、次のエラーメッセージが返されます。
この状況を解決する方法 ライブラリコードの生成中に、Visual Studio 2017 は次のマクロを形成しました。
#ifdef PROJECT2_EXPORTS #define PROJECT2_API __declspec(dllexport) #else #define PROJECT2_API __declspec(dllimport) #endif
目的の関数の完全なプロトタイプは次のようになります。
PROJECT2_API int fnExport(wchar_t* t);
ライブラリのコンパイル後にエクスポートテーブルを表示する:
これを表示するには、[トータルコマンダー] ウィンドウでライブラリファイルを選択し、[F3] を押します。 エクスポートされた関数の名前に注意してください。 では、上記のマクロを編集してみましょう (元の記事でどのように行われたか):
#ifdef PROJECT2_EXPORTS #define PROJECT2_API extern "C" __declspec(dllexport) #else #define PROJECT2_API __declspec(dllimport) #endif
Here
extern "C"
これは、オブジェクトファイルを受信するときに、シンプルな関数シグネチャ生成 (C 言語スタイルで) を使用することを意味します。 特に、DLL へのエクスポート時に追加の文字を持つ関数名の "デコレート" から C++ コンパイラを禁止します。 エクスポートテーブルを再コンパイルして表示します。
エクスポートテーブルの変更は明白であり、スクリプトから関数を呼び出すときにエラーは発生しません。 ただし、このメソッドには、コンパイラによって作成されたスクリプトを編集する必要があるという欠点があります。 しかし、同じことを実行するより安全な方法がありますが、少し長くなります。
定義ファイル
通常、プロジェクト名に一致する名前を持つ.def 拡張子を持つプレーンテキストファイルです。 今回の場合、ファイル Project2.def になります。 このファイルは、通常のメモ帳で作成されます。 Word または類似のエディタを使用しないでください。 ファイルの内容は次のようになります。
;PROJECT2:def のモジュールパラメータを宣言します。 LIBRARY "PROJECT2" DESCRIPTION 'PROJECT2 Windows Dynamic Link Library' EXPORTS ; Explicit exports can go here fnExport @ 1 fnExport2 @ 2 fnExport3 @ 3 ....
ヘッダの後には、エクスポートされた関数のリストが続きます。 文字 @ 1、@ 2 などは、ライブラリ内の関数の必要なオーダーを示します。 このファイルをプロジェクトフォルダに保存します。
次に、このファイルを作成してプロジェクトに接続します。 [プロジェクトプロパティ] ウィンドウの左の部分で、 "リンカー " 要素とその "Input " サブ要素を選択します。 次に、右側の部分の "モジュール定義ファイル " パラメータを選択します。 前のケースと同様に、編集可能なリストにアクセスし、ファイル名: "Project2.def" を追加します。 [OK] をクリックし、コンパイルを繰り返します。 この結果は、前のスクリーンショットと同じです。 名前は装飾されず、スクリプトによって関数が呼び出されたときにエラーは発生しません。 これで、プロジェクト設定を分析しました。 さて、ライブラリコードの記述を始めましょう。
ライブラリとDllMain の作成
元の記事で、データ交換とDLL からのさまざまな関数呼び出しに関連する問題の包括的な説明を提供していますので、ここでこだわることはありません。 特定の関数を表示するために、ライブラリ内のシンプルなコードを作成してみましょう。
1. エクスポートする次の関数を追加します (定義ファイルを編集することを忘れないでください)。
PROJECT2_API int fnExport1(void) { return GetSomeParam(); }
2. Header1 ヘッダファイルを作成してプロジェクトに追加し、別の関数を追加します。
const int GetSomeParam();3. Dllmain.cpp ファイルを編集します。
#include "stdafx.h" #include "Header1.h" int iParam; BOOL APIENTRYDLLMain (HMODULE hModule、 DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { caseDLL_PROCESS_ATTACH: iParam = 7; break; case DLL_THREAD_ATTACH: iParam += 1; break; case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } const int GetSomeParam() { return iParam; }
コードの目的が明確である必要があります: 変数がライブラリに追加されます。 その値はDllMain 関数で計算され、fnExport1 関数を使用して使用できます。 スクリプト内の関数を呼び出してみましょう。
#import "Project2.dll" int fnExport1(void); #import ... void OnStart() { Print("fnExport1: ",fnExport1() );
次のインプットが出力されます。
fnExport1: 7
DllMain コードのこの部分が実行されないことを意味します。
case DLL_THREAD_ATTACH: iParam += 1; break;
これは重要でしょうか? 私の意見では、開発者がここでライブラリ初期化コードの一部を追加し、ライブラリをストリームに接続するときに実行されることを期待しているので、重要です。 しかし、エラーは返されないため、問題を発見することは困難になります。
文字列
文字列を使用した操作については、元の記事で説明します。 このタスクは難しいことではありません。 しかし、以下の点を明確にしたいと思います。
ライブラリにシンプルな関数を作成してみましょう (そして、定義ファイルを編集します)。
PROJECT2_API void SamplesW(wchar_t* pChar) { size_t len = wcslen(pChar); wcscpy_s(pChar + len, 255, L" Hello from C++"); }スクリプトでこの関数を呼び出します。
#import "Project2.dll" void SamplesW(string& pChar); #import void OnStart() { string t = "Hello from MQL5"; SamplesW(t); Print("SamplesW(): ", t);
次の予期されるメッセージが受信されます。
SamplesW(): Hello from MQL5 Hello from C++
次に、関数呼び出しを編集します。
#import "Project2.dll" void SamplesW(string& pChar); #import void OnStart() { string t; SamplesW(t); Print("SamplesW(): ", t);
今度は、エラーメッセージが表示されます。
Access violation at 0x00007FF96B322B1F read to 0x0000000000000008
ライブラリ関数に渡される文字列を初期化し、スクリプトの実行を繰り返します。
string t="";
エラーメッセージは受信されないので、予想される出力を取得します。
SamplesW(): Hello from C++
上記のコードは、次のことを示唆しています。ライブラリによってエクスポートされた関数に渡される文字列は初期化する必要があります!
さて、ユニコードの使用に戻りましょう。 (直近の例で示されているように) 文字列をDLL に渡す予定がない場合は、Unicode サポートは必要ありません。 しかし、どんな場合でもユニコードのサポートを有効にすることをお勧めします、エクスポートされた関数は変更できるので、新しい関数を追加することができ、開発者は Unicode サポートがないことを無視することができます。
シンボル配列は、元の記事で説明されている一般的な方法で渡され、受け取っています。 したがって、これについて再度考察する必要はありません。
構造
ライブラリとスクリプトで最もシンプルな構造を定義してみましょう。
//Dll の場合: typedef struct E_STRUCT { int val1; int val2; }ESTRUCT, *PESTRUCT; //MQL スクリプトで: struct ESTRUCT { int val1; int val2; };
構造体をライブラリに対して操作するための関数を追加します。
PROJECT2_API void SamplesStruct(PESTRUCT s) { int t; t = s->val2; s->val2 = s->val1; s->val1 = t; }
コードからわかるように、関数は独自のフィールドを交換します。
スクリプトからこの関数を呼び出します。
#import "Project2.dll" void SamplesStruct(ESTRUCT& s); #import .... ESTRUCT e; e.val1 = 1; e.val2 = 2; SamplesStruct(e); Print("SamplesStruct: val1: ",e.val1," val2: ",e.val2);
このスクリプトを実行し、期待どおりの結果を取得します。
SamplesStruct: val1: 2 val2: 1
このオブジェクトは参照によって呼び出された関数に渡されました。 この関数はオブジェクトを処理し、呼び出し元のコードに返しました。
しかし、多くの場合、より複雑な構造が必要です。 タスクを複雑にしてみましょう。構造に異なる型を持つもう1つのフィールドを追加します。
typedefstructE_STRUCT1 { int val1; char cval; int val2; }ESTRUCT1, *PESTRUCT1;
また、操作する関数を追加します。
PROJECT2_API void SamplesStruct1(PESTRUCT1 s) { int t; t = s->val2; s->val2 = s->val1; s->val1 = t; s->cval = 'A'; }
前の例のように、この関数は int 型のフィールドを交換し、' char ' 型フィールドに値を代入します。 (前の関数とまったく同じ方法で) スクリプト内でこの関数を呼び出します。 ただし、今回の結果は次のようになります。
SamplesStruct1: val1: -2144992512 cval: A val2: 33554435
Int 型の構造フィールドに間違ったデータがあります。 例外ではありませんが、ランダムな誤ったデータです。 これはどうしたのでしょうか。 理由は 整列によるものです! 整列(アライメント)は複雑な概念ではありません。 構造に関連するドキュメントセクションパックは、アライメントの詳細な説明を提供します。 Visual Studio C++ では、配置に関連する包括的なマテリアルも提供します。
この例では、ライブラリとスクリプトのアラインメントが異なるためにエラーが発生しました。 この問題を解決するには2つの方法があります。
- スクリプトで新しいアライメントを指定します。 これは、pack(n) 属性を使用して行うことができます。 最大のフィールド、すなわちintに従って構造体を整列させてみましょう。
struct ESTRUCT1 pack(sizeof(int)){ int val1; char cval; int val2; };
スクリプトの実行を繰り返してみましょう。 ログのインプットが変更されました: SamplesStruct1: val1: 3 cval: val2: 2 。 したがって、エラーは解決されました。
- 新しいライブラリの配置を指定します。 MQL 構造のデフォルトのアライメントはパック (1) です。 ライブラリにも同じように適用します。
#pragma pack(1) typedefstructE_STRUCT1 { int val1; char cval; int val2; }ESTRUCT1, *PESTRUCT1; #pragma pack()
このライブラリをビルドしてスクリプトを実行します。その結果は正しく、最初のメソッドと同じです。
#pragma pack(1) typedef struct E_STRUCT2 { E_STRUCT2() { val2 = 15; } int val1; char cval; int val2; }ESTRUCT2, *PESTRUCT2; #pragma pack()この構造体は、次の関数で使用します。
PROJECT2_API void SamplesStruct2(PESTRUCT2 s) { int t; t = s->val2; s->val2 = s->val1; s->val1 = t; s->cval = 'B'; }スクリプトで適切な変更を行います。
struct ESTRUCT2 pack(1){ ESTRUCT2() { val1 = -1; val2 = 10; } int val1; char cval; int f() { int val3 = val1 + val2; return (val3);} int val2; }; #import "Project2.dll" void SamplesStruct2(ESTRUCT2& s); #import ... ESTRUCT2 e2; e2.val1 = 4; e2.val2 = 5; SamplesStruct2(e2); t = CharToString(e2.cval); Print("SamplesStruct2: val1: ",e2.val1," cval: ",t," val2: ",e2.val2);
f() メソッドが構造に追加されているため、ライブラリ内の構造との差が大きくなることに注意してください。 このスクリプトを実行します。 次のインプットがジャーナルに書き込まれます。 SamplesStruct2: val1: 5 cval: B val2: 4この実行は正しいと言えます! コンストラクタの存在と、構造内の追加のメソッドは、結果に影響を与えませんでした。
直近の実験 データフィールドのみを残しながら、スクリプト内の構造からコンストラクタとメソッドを削除します。 このライブラリ内の構造は変更されないままです。 スクリプトの実行によって、正しい結果が生成されます。 最終的な結論に達します。構造体に追加されたメソッドは、結果に影響を与えません。
Visual Studio 2017 と MetaTrader5 スクリプト用のこのライブラリプロジェクトは、以下に添付されています。
できないこと
関連ドキュメントで説明されているDLL の操作には、一定の制限があります。 ここでは繰り返しません。 次に例を示します。
structBAD_STRUCT { string simple_str; };
この構造体をDLL に渡すことはできません。 構造体にラップされた文字列です。 より複雑なオブジェクトは、例外を取得せずにDLL に渡すことはできません。
できない場合、何をすべきか
多くの場合、許可されていないオブジェクトをDLL に渡す必要があります。 ダイナミックオブジェクト、ギヤ配列、等が付いている構造を含んでいます。 この場合、何ができるでしょうか。 ライブラリコードにアクセスすることなく、このソリューションを使用することはできません。 コードへのアクセスは、問題の解決に役立ちます。
データデザインの変更は、利用可能な手段を使用して解決し、例外を回避する必要があるため、考慮しません。 ある程度の明確化が必要です。 この記事は経験豊富なユーザー向けのものではないため、問題の解決方法についてのみ概説します。
- StructToCharArray()関数の使用法。 これは、スクリプトで次のコードを使用できるようにする良い機会であると思われます。
structStr { ... }; Str s; uchar ch[]; StructToCharArray(s,ch); SomeExportFunc(ch);
Code in the cpp library file:#pragma パック (1) typedef struct D_a { ... }Da, *PDa; #pragma pack() void SomeExportFunc(char* pA) { PDa = (PDa)pA; ...... }
セキュリティと品質の問題に加えて、このアイデアは役に立ちません。StructToCharArray() は、追加の変換なしでライブラリに渡すことができる POD structures でのみ動作します。 実際のコードでこの関数の動作をテストしていません。
- ライブラリーに渡すことができるオブジェクトに、独自のパッカー/アンパッカー構造体を作成します。 このメソッドは可能ですが、複雑で、リソースと時間がかかります。 ただし、この方法で、完全に許容できるソリューションが提案されます。
- ライブラリに直接渡すことができないすべてのオブジェクトは、スクリプト内の JSON 文字列にパッケージ化し、ライブラリ内の構造体にアンパックする必要があります。 逆もしかりです。 利用可能なツールがあります: JSON のパーサーは C++、C# および MQL に利用できます。 このメソッドは、オブジェクトのパッキング/アンパックに時間がかかる場合に使用できます。 しかし、明らかな時間の損失とは別に、利点があります。 このメソッドは、複雑な構造 (および他のオブジェクト) での操作を可能にします。 さらに、既存のパッカー/アンパッカーを最初から書くのではなく絞り込むことができます。
そのため、複雑なオブジェクトをライブラリに渡したり受信したりする可能性があることに注意してください。
実用化
では、便利なライブラリを作成してみましょう。 このライブラリは電子メールを送信します。 以下の点にご注意ください。
- このライブラリはスパムメール送信には使用できません。
- ライブラリは、ターミナルの設定で指定されたもの以外のアドレスとサーバーからメールを送信することができます。 また、ターミナルの設定でメールの使用を無効にすることもできますが、ライブラリの操作には影響しません。
そして直近のもの。 C++ コードのほとんどは、私のものではありませんが、マイクロソフトのフォーラムからダウンロードされました。 古いもので実績のある例であり、その亜種は VBS でも使用できます。
さて、始めましょう。 Visual Studio 2017 でプロジェクトを作成し、記事の冒頭で説明したように設定を変更します。 定義ファイルを作成し、プロジェクトに接続します。 エクスポートされる関数は1つだけです。
SENDSOMEMAIL_API bool SendSomeMail(LPCWSTR addr_from,
LPCWSTR addr_to,
LPCWSTR subject,
LPCWSTR text_body,
LPCWSTR smtp_server,
LPCWSTR smtp_user,
LPCWSTR smtp_password);
引数の意味は明確であるため、ここでは簡単な説明を示します。
- addr_from、addr_to - 送信者と受信者の電子メールアドレス。
- subject,text_body — 件名と電子メールの著書文。
- smtp_server、smtp_user、smtp_password —サーバーの SMTPサーバーアドレス、ユーザーログイン、およびパスワード。
次に注意を払います。
- 引数の説明からわかるように、メールを送信するには、メールサーバーにアカウントを持っていて、そのアドレスを知っている必要があります。 したがって、送信者を匿名にすることはできません。
- このポート番号は、ライブラリでハードコーディングされています。 標準ポート番号25です。
- このライブラリは、必要なデータを受信し、サーバーに接続し、電子メールを送信します。 1回の呼び出しでは、電子メールは1つのアドレスにのみ送信できます。 さらに送信するには、新しいアドレスで関数呼び出しを繰り返します。
ここでは C++ コードを提供しません。 このコードとプロジェクト全体は、添付の SendSomeMail.zip プロジェクトで以下に提供されています。 使用する CDO オブジェクトには多くの関数があるため、ライブラリの開発と改善に使用する必要があります。
このプロジェクトに加えて、ライブラリ関数を呼び出す簡単なスクリプトを記述します (添付されている SendSomeMail ファイルにあります)。
#import "SendSomeMail.dll" bool SendSomeMail(string addr_from,string addr_to,string subject,string text_body,string smtp_server,string smtp_user,string smtp_password); #import //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { bool b = SendSomeMail("XXX@XXX.XX", "XXXXXX@XXXXX.XX", "hello", "hello from me to you","smtp.XXX.XX", "XXXX@XXXX.XXX", "XXXXXXXXX"); Print("Send mail: ", b); }
X 文字ではなく、独自のアカウントの詳細を追加します。 したがって、この開発は完了です。 自身の詳細を追加し、必要とするかもしれないコードに任意の追加を行い、ライブラリが使用できるようになります。
結論
元の記事を使用して、この記事に記載されているアカウントの更新を行うと、誰でもすぐに基本を習得し、より複雑で興味深いプロジェクトに進むことができます。
特定の状況下で重要なことができるもう一つの興味深い事実にこだわりたいと思います。 どのようにDLL コードを保護すればいいでしょうか。 標準的な解決策は、パッカーを使用することです。 異なるパッカーがたくさんあり、その多くは良い保護レベルを提供しています。 私は2つのパッカーを使っています。Themida 2.4.6.0 と VMProtect Ultimate v. 3.0.9 .です。 これらを使用して、最初のシンプルな Project2.dll を、各パッカー用の2つの亜種にパックしてみましょう。 その後、既存のスクリプトを使用してターミナル内のエクスポートされた関数を呼び出します。 すべてうまく動作します! このターミナルはそのようなライブラリを扱うことができます。 しかし、他のパッカーによって保護されているライブラリの正常な動作は保証しません。 2つのメソッドでパックされた Project2.dll は、Project2_Pack.zip で使用できます。
こんなところです。 さらなる開発・発展を頑張ってください。
本稿で使用したプログラム
# | 名称 |
タイプ |
詳細 |
---|---|---|---|
1 | Project2.zip | アーカイブ |
シンプルなDLL プロジェクト |
2 |
Project2.mq5 |
スクリプト |
DLL を使用した操作のスクリプト |
3 | SendSomeMail.zip | アーカイブ | メール終了DLL プロジェクト |
4 | SendSomeMail.mq5 | スクリプト |
SendSomeMail ライブラリDLL を使用した操作用のスクリプト |
5 | Project2_Pack.zip | アーカイブ | Project2.dll は、Themida と VMProtect で保護されています。 |
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/5798





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索