![【MQL4】WinAPIを使ってファイル一覧を取得する。](https://celebpanda.com/wp/wp-content/uploads/2021/05/セレブパンダブログ用.jpg)
大便をしてもの凄く、力まないと出てこないから、さぞ大物が出てきたらと思ったら鹿のフン程度の時をみると、人のイメージは適当だなと思うセレブパンダだお。
MQL4でもファイルの一覧を表示することはできます。
FileFindFirst関数です。
ただ、この関数はMQL4\Filesにあるファイルまたは共通フォルダしかみてくれません。
今回やりたかったのは、MT4で保存した定型として保存したテンプレートファイルの一覧を取得したいのです。
テンプレートファイルは、MQL4と同じ場所のtemplatesというフォルダに保存されます。
先ほど、記載した通り、FileFindFirst関数では不可能なんですね(´;ω;`)
そこで、WindowsAPIを使って、指定したフォルダのファイル一覧を取得する方法があかったのでシェアします。
- MQL4でMQL4\Files以外のフォルダのファイル一覧を取得したい人。
WindowsAPIのFindFirstFileWとFindNextFileWを組み合わせるとファイルの一覧を取得できます。
FindFirstFileW
最初にヒットするファイルを取得する関数のようです。
この関数ですが、マイクロソフトの関数定義をみると構造体を引数にしています。
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);
}
}
実行結果
![](https://i.gyazo.com/9a9ddce06dcc35b347c1eba8739eb8ee.png)
実際のtemplatesフォルダの中身
![](https://i.gyazo.com/5040b84f95f6b2bf4e58534ccb7d0b55.png)
このように、指定したフォルダのファイル一覧を取得できました。
- MQL4からWinAPIを利用する際に、構造体を絶対指定する必要はない。
- 構造体の定義をみて、必要な情報のバイト数を求めるとうまくいく。
では、まただお。