【MQL4】WinAPIを使ってファイル一覧を取得する。

大便をしてもの凄く、力まないと出てこないから、さぞ大物が出てきたらと思ったら鹿のフン程度の時をみると、人のイメージは適当だなと思うセレブパンダだお。

MQL4でもファイルの一覧を表示することはできます。
FileFindFirst関数です。
ただ、この関数はMQL4\Filesにあるファイルまたは共通フォルダしかみてくれません。

今回やりたかったのは、MT4で保存した定型として保存したテンプレートファイルの一覧を取得したいのです。

テンプレートファイルは、MQL4と同じ場所のtemplatesというフォルダに保存されます。

先ほど、記載した通り、FileFindFirst関数では不可能なんですね(´;ω;`)

そこで、WindowsAPIを使って、指定したフォルダのファイル一覧を取得する方法があかったのでシェアします。

こんな方におすすめ
  • MQL4でMQL4\Files以外のフォルダのファイル一覧を取得したい人。

WindowsAPIのFindFirstFileWとFindNextFileWを組み合わせるとファイルの一覧を取得できます。

FindFirstFileW

最初にヒットするファイルを取得する関数のようです。

この関数ですが、マイクロソフトの関数定義をみると構造体を引数にしています。

FindNextFileW

HANDLE FindFirstFileW(
  [in]  LPCWSTR            lpFileName,
  [out] LPWIN32_FIND_DATAW lpFindFileData
);

LPCWSTRはMQL4ではstringで定義できます。

問題は、LPWIN32_FIND_DATAWです。

LPWIN32_FIND_DATAW

typedef struct _WIN32_FIND_DATAW {
  DWORD    dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD    nFileSizeHigh;
  DWORD    nFileSizeLow;
  DWORD    dwReserved0;
  DWORD    dwReserved1;
  WCHAR    cFileName[MAX_PATH];
  WCHAR    cAlternateFileName[14];
  DWORD    dwFileType; // Obsolete. Do not use.
  DWORD    dwCreatorType; // Obsolete. Do not use
  WORD     wFinderFlags; // Obsolete. Do not use
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;

DWORDはint、WORDはshortで置き換えできます。

むむ、FILETIME・・・。ここでも構造体が。

FILETIME

typedef struct _FILETIME {
  DWORD dwLowDateTime;
  DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;

DWORDを2つもつ構造体だとわかりました。intで代用できます。

ただ、構造体をもつ構造体で、しかもWindowsAPIの引数をもつ関数をMQL4で呼べるのかが問題となりました。

構造体を定義しなくてもできるんです。

ushortの配列を構造体のところに定義してあげればできます。

ushortは正の数値しか扱えない型です。サイズは2バイト(16ビット)です。

MQL4では以下の指定で使えるようになります。

#import "kernel32.dll"
int  FindFirstFileW(string path, ushort &Answer[]);
#import

#import "kernel32.dll"とすることで、kernel32.dllのFindFirstFileWを使いますよという宣言になります。

なぜ、構造体でないのに使えるかは、マシンが動く世界ではshortやintの型というより、バイト数が重要です。

構造体の型より、サイズが一致していれば問題ないのではないかと思っております。

MQL4からWinAPIの型のチェックまではコンパイル時にしないので、いわば盲点をついたやり方です。

FindNextFileW

FindFirstFileWの次にこの関数を使うと、まだ取得していないファイルを返すようです。

同様に定義をみると

BOOL FindNextFileW(
  [in]  HANDLE             hFindFile,
  [out] LPWIN32_FIND_DATAW lpFindFileData
);

同じ引数なので、同様に以下のようにすればMQL4で使えます。

#import "kernel32.dll"
bool FindNextFileW(int handle, ushort &Answer[]);
#import

重要なのは使い方

これで宣言部分はクリアしました。

肝心なのは、本当に使えるんかです。

ここで、構造体の定義をもう一度みてみましょう。

typedef struct _WIN32_FIND_DATAW {
  DWORD    dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD    nFileSizeHigh;
  DWORD    nFileSizeLow;
  DWORD    dwReserved0;
  DWORD    dwReserved1;
  WCHAR    cFileName[MAX_PATH];
  WCHAR    cAlternateFileName[14];
  DWORD    dwFileType; // Obsolete. Do not use.
  DWORD    dwCreatorType; // Obsolete. Do not use
  WORD     wFinderFlags; // Obsolete. Do not use
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;

ファイル名称が設定されるのは、cFileNameという変数です。

今回はushort &Answer[]の配列で定義しているから、cFileNameの位置がわかればAnswer[3]やAnswer[7]で取得可能なのです。

DWORDはintと置き換え可能と話ました。
intはshortの2倍のバイト数を持ちます。つまり4バイトです。

FILETIME構造体は

typedef struct _FILETIME {
  DWORD dwLowDateTime;
  DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;

なので、DOWRDが2つ分です。

つまり、以下のように置き換えれるはずです。

typedef struct _WIN32_FIND_DATAW {
  int    dwFileAttributes;
  int    dwLowDateTime;   ← FILETIMEの構造体
  int    dwHighDateTime; ← FILETIMEの構造体
  int    dwLowDateTime;   ← FILETIMEの構造体
  int    dwHighDateTime;  ← FILETIMEの構造体
  int    dwLowDateTime;   ← FILETIMEの構造体
  int    dwHighDateTime;  ← FILETIMEの構造体
  int    nFileSizeHigh;
  int    nFileSizeLow;
  int    dwReserved0;
  int    dwReserved1;
  WCHAR    cFileName[MAX_PATH];
  WCHAR    cAlternateFileName[14];
  DWORD    dwFileType; // Obsolete. Do not use.
  DWORD    dwCreatorType; // Obsolete. Do not use
  WORD     wFinderFlags; // Obsolete. Do not use
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;

ということは、int型が11個並んだ次がcFileNameの開始位置ということがわかります。

ushortは2バイトなので、ushortでは22個並んだ次がcFileNameの開始位置になります。

サンプル

実際にコードサンプルを記載します。

#import "kernel32.dll"
int  FindFirstFileW(string path, ushort &Answer[]);
bool FindNextFileW(int handle, ushort &Answer[]);
bool FindClose(int handle);
#import

int OnStart()
{

    ushort Buffer[300];
    // templatesフォルダのtplファイルを取得したいので、拡張子でフィルタをかける
    string path = TerminalInfoString( TERMINAL_DATA_PATH ) + "\\templates\\" +  "*.tpl";
    
    int handle = FindFirstFileW(path,Buffer);

    // ファイルが取得できる場合は0以上を返す
    if (handle > 0){
        string fileName = ShortArrayToString( Buffer, 22 );
        Print("最初にヒットしたファイル=",fileName);
        ArrayInitialize(Buffer,0);
        while(FindNextFileW(handle,Buffer)){
            fileName=ShortArrayToString(Buffer,22);
            Print("次のファイル=",fileName);
        }
        // ファイルハンドルをクローズ
        FindClose(handle);
    }
}

実行結果

実際のtemplatesフォルダの中身

このように、指定したフォルダのファイル一覧を取得できました。

まとめ
  • MQL4からWinAPIを利用する際に、構造体を絶対指定する必要はない。
  • 構造体の定義をみて、必要な情報のバイト数を求めるとうまくいく。

では、まただお。

Twitterでフォローしよう

おすすめの記事