DLLを使ったインターネット接続

DLL(ダイナミックリンクライブラリ)は、汎用的な処理が書かれたプログラムの部品のようなもの。
MQLでは、PCにあるDLLファイルを使う事ができる(C++でDLLファイルを自作することもできる)。
DLLの中には関数等が書かれていて、MQLからそれを読み込んで利用する。

EAやカスタムインジケータで利用頻度が高いインターネットアクセスを行うためのコーディングをDLLを使用して行う。
ちなみにEAで使えるWebRequest関数が用意されているが、カスタムインジケータからインターネットアクセスを行う場合はDLLを使用することになる。
今回は、指定したURLのページ内容を取得することを目的にする。

インターネット機能は、WinINetを使う。DLLファイルは、wininet.dll。
WindowsのDLLファイルは、Windowsがインストールされている場所のSystemフォルダ(またはSystem32フォルダ)に格納されている。
フォルダを開くと沢山のDLLファイルが用意されていることが分かるが、その中でもwininet.dllはそこそこ大きめのファイルサイズになっていて、このファイルに沢山の関数が記述されていることが伺える。

Visual StudioのDeveloper Command Promptを立ち上げ、DLLファイルが格納されているフォルダまで移動して「dumpbin /exports wininet.dll」と打ち込んでみると、wininet.dllで提供されている関数の一覧が確認できるが、面倒なので普通に公式のドキュメントページで確認しよう。

InternetAttemptConnect関数

InternetAttemptConnect関数で、インターネットが使用可能かどうか確認する。

まずはDLLファイルをインポートして、InternetAttemptConnect関数を使用する。
引数は常に”0”を指定する。

#property copyright "nisai"
#property link     "https://nisaifx.com"
#property description ""

//wininet.dllのインポート宣言(使用する関数を全てリストする)
#import "wininet.dll"
int InternetAttemptConnect(int r);
#import

int OnInit(){
   //インターネット接続の有無を確認
   if(InternetAttemptConnect(0)!=0){
      //戻り値が0で無ければ失敗
      return INIT_FAILED;
   }
   return INIT_SUCCEEDED;
}

上記EAを実行すると、DLLファイル内の関数が呼ばれ、正常に動くことがわかる。

InternetOpen関数

InternetOpen関数で、インターネット接続の準備(初期化)を行う。
この関数で取得できるハンドルをこれ以降の関数で使用するので変数に受け取っておく。

なお、InternetOpenA/InternetOpenWのように、”A”と”W”の2つの関数があるが、機能としてはどちらも同じ。
”W”はワイド文字の事で、Unicode対応だそう。以降、基本的にWを使っていく。

InternetOpen関数に渡す引数では、ユーザーエージェントの指定やインターネットアクセスの方法を指定する。
ユーザーエージェントは任意の文字列で良い。
インターネットアクセスの方法は、直接アクセスかプロキシアクセスがある。レジストリの設定に従うようにするために、アクセスタイプはINTERNET_OPEN_TYPE_PRECONFIG(中身は0)を使用する。
プロキシを使っていなければ、プロキシとプロキシバイパスはNULLで良い。

初期化が失敗した場合は”0”が返される。

#property copyright "nisai"
#property link     "https://nisaifx.com"
#property description ""

//InternetOpenに渡すAccess Type
#define INTERNET_OPEN_TYPE_PRECONFIG 0

//wininet.dllのインポート宣言(使用する関数を全てリストする)
#import "wininet.dll"
int InternetAttemptConnect(int r);
int InternetOpenW(string userAgent,int accessType,string proxyName,string proxyByPass,int flags);
#import

int OnInit(){
   //インターネット接続の有無を確認
   if(InternetAttemptConnect(0)!=0){
      //戻り値が0で無ければ接続が確認できない
      return INIT_FAILED;
   }
   //WinINet関数のためのハンドルを取得(初期化)
   int openHandle=InternetOpenW("User Agent",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
   if(openHandle<=0){
      //初期化失敗
      return INIT_FAILED;
   }
   //
   return INIT_SUCCEEDED;
}

InternetOpenUrl関数

InternetOpenUrl関数は、InternetOpen関数で取得したハンドルを使ってURLを開き、データ読み込みの準備をしてハンドルを返す。

この関数で実際に接続したいURLを指定する。
WebRequest関数と異なり、MT4/5のエキスパートアドバイザ設定タブのURL許可リストへの登録は不要。

他の引数としてリクエストヘッダの指定が可能だが、認証などが不要なら特に指定しないで良い。

接続が失敗した場合は”0”が返される。

InternetReadFile関数

InternetReadFile関数で、接続先のデータを読み込む。

データを受け取るためのバッファを用意して、読み込んだデータ量が格納される変数の中身が0になるまでループさせる。
バッファはuchar型なので、CharArrayToStringを使って文字列に変換する。

#property copyright "nisai"
#property link     "https://nisaifx.com"
#property description ""

//InternetOpenに渡すAccess Type
#define INTERNET_OPEN_TYPE_PRECONFIG 0

//wininet.dllのインポート宣言(使用する関数を全てリストする)
#import "wininet.dll"
int InternetAttemptConnect(int r);
int InternetOpenW(string userAgent,int accessType,string proxyName,string proxyByPass,int flags);
int InternetOpenUrlW(int hInternet,string url,string header,int headerLength,int flags,int context);
int InternetReadFile(int hFile,uchar &lpBuffer[],int dwNumberOfBytesToRead,int &lpdwNumberOfBytesRead);
#import

int OnInit(){
   //インターネット接続の有無を確認
   if(InternetAttemptConnect(0)!=0){
      //戻り値が0で無ければ接続が確認できない
      return INIT_FAILED;
   }
   //WinINet関数のためのハンドルを取得(初期化)
   int openHandle=InternetOpenW("User Agent",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
   if(openHandle<=0){
      //初期化失敗
      return INIT_FAILED;
   }
   //目的のURLに接続
   int openUrlHandle=InternetOpenUrlW(openHandle,"https://google.com",NULL,0,0,0);
   if(openUrlHandle<=0){
      //接続失敗
      return INIT_FAILED;
   }
   //データを読み込む
   uchar buffer[100];
   string response="";
   int readSize=0;
   while(InternetReadFile(openUrlHandle,buffer,100,readSize)){
      if(readSize<=0)
         break;
      response=response+CharArrayToString(buffer,0,readSize);
   }
   Print(response);

   return INIT_SUCCEEDED;
}

上記を実行すると、https://google.com のページのソースコードが取得できていることがわかる。

文字列は頑張って処理しよう。
JSONなら楽。HTMLをパースするのは大変。

InternetCloseHandle関数

InternetCloseHandle関数で、不要になった接続は切断処理をしてあげるのがベター。遊び終わったオモチャを片付けるのと同じ。

#property copyright "nisai"
#property link     "https://nisaifx.com"
#property description ""

//InternetOpenに渡すAccess Type
#define INTERNET_OPEN_TYPE_PRECONFIG 0

//wininet.dllのインポート宣言(使用する関数を全てリストする)
#import "wininet.dll"
int InternetAttemptConnect(int r);
int InternetOpenW(string userAgent,int accessType,string proxyName,string proxyByPass,int flags);
int InternetOpenUrlW(int hInternet,string url,string header,int headerLength,int flags,int context);
int InternetReadFile(int hFile,uchar &lpBuffer[],int dwNumberOfBytesToRead,int &lpdwNumberOfBytesRead);
int InternetCloseHandle(int handle);
#import

int OnInit(){
   //インターネット接続の有無を確認
   if(InternetAttemptConnect(0)!=0){
      //戻り値が0で無ければ接続が確認できない
      return INIT_FAILED;
   }
   //WinINet関数のためのハンドルを取得(初期化)
   int openHandle=InternetOpenW("User Agent",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
   if(openHandle<=0){
      //初期化失敗
      return INIT_FAILED;
   }
   //目的のURLに接続
   int openUrlHandle=InternetOpenUrlW(openHandle,"https://google.com",NULL,0,0,0);
   if(openUrlHandle<=0){
      //接続失敗
      InternetCloseHandle(openHandle);
      return INIT_FAILED;
   }
   //データを読み込む
   uchar buffer[100];
   string response="";
   int byteSize=0;
   while(InternetReadFile(openUrlHandle,buffer,100,readSize)){
      if(readSize<=0)
         break;
      response=response+CharArrayToString(buffer,0,readSize);
   }
   Print(response);

   InternetCloseHandle(openHandle);
   InternetCloseHandle(openUrlHandle);
   
   return INIT_SUCCEEDED;
}

4件のフィードバック

  1. お世話になります。

    wininet.dllを明示的に使わずに(ひょっとしたら内部的には使用しているのかも)、MT4上のEAからWebRequest関数で特定のURLにリクエストするためには、事前準備として、MT4の[ツール]-[オプション]-[エキスパートアドバイザ]-[WebRequestを許可するURLリスト] に、リクエストしたいURLの登録が必要でした。

    本wininet.dllを仕様する場合は、リストにURL登録せずともMT4からWebRequestできるのでしょうか?
    よろしければご教示いただければと存じます。よろしくお願い致します。

    • こんにちは。
      ご認識の通りです!WebRequest関数を使わずwininet.dllを使用する場合、リストへのURL登録は不要です。
      ただし、[ツール]-[オプション]-[エキスパートアドバイザ]-[DLLの使用を許可する]を有効にする必要があります(逆にWebRequest関数を使う場合は不要)。
      (または、EAをチャートに適用する際に表示されるポップアップの[全般]-[セイフティー]-[DLLの使用を許可する]を有効にします)

  2. nisai様
    ご回答頂き、ありがとうございます。
    他のサイトを見ても、本関連の情報を見かけませんので、大変助かりました!
    サイト応援しています^ ^
    引き続きよろしくお願い致します。

    • こちらこそありがとうございます。
      お役に立てたようで何よりです!
      最近全く更新していないので殆ど忘れていましたが、検索等から辿り着いて見てくださったんだなあとホッコリしましたw
      今後ともよろしくお願いします。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

コメントする