UWSC DEF_DLL書き方
過去にこんな記事を書いた。
UWSCのDEF_DLLについて - じゅんじゅんのきまぐれ
これは、自分用のメモとなっているため、思考の多くが省略されています。
自分としては自明なため、書いてないことを含めて、DEF_DLLの書き方を記載しようと思います。
今さらではありますが。
重要な話
まあ、落ち着いて。本当にDLLを呼ぶ必要はあるの?
PowerShellでいいんじゃない?PowerShellで。PowerShellで!!!
というのは、おいといて。
ポインターは何か?
すみません、それは別で勉強してください。
それは(なんとなく)分かってるけど、UWSCのDEF_DLLがよくわからん、という人が本記事の対象です。
その上で、
varをつけるとポインターになりますが、DEF_DLLの{}や引数に配列を渡す場合も、必ずポインターになります。
そして、{}や配列の引数にvarをつけても、ポインターのポインターにはなりません。(意味なし)
文字列は、文字の配列です。
この辺が前提知識。
そして、もう一つ。
引数を伴った関数コール、というものは、アセンブリにはない、ということ。
ないのでどうするか。
呼び出し側・呼び出される側、双方でルールを決めて、メモリーを用意してジャンプしよう、となりました。
なので、引数を伴った関数コール、とは、メモリーを確保してジャンプ、ということになります。
メモリーがあれば良い、ということです。
変数の数は重要ではないのです!
さて、実践。
引数が実値
SendMessageWというWin32APIがある。
LRESULT SendMessageW( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
これは簡単。
例えば、
DEF_DLL SendMessageW(HWND, DWORD, DWORD, DWORD): DWORD: user32
SendMessageは、送るMsgによりパラメーターや戻り値の意味が変わります。
このため、それにあわせて適切に定義した方が使いやすいです。
ここで、一歩進めます。
SendMessageはパラメーターが4つ。
しかし、先ほど書いたように、メモリーを確保してジャンプ、です。
HWNDもDWORDもUWSCでは4byteなので、16byteのメモリーを確保し、4byteが返ってきます。
なので、これでも動作します。
DEF_DLL SendMessageW(longlong, longlong): bool: user32
longlongは8byteなので、これも16byteのメモリーを確保し、4byteが返ってくる定義。
ポイント:メモリーを意識すること
引数が構造体
意外と難しいのがこれ。
HMONITOR MonitorFromPoint(POINT pt, DWORD dwFlags); typedef struct tagPOINT { LONG x; LONG y; } POINT, *PPOINT;
構造体だし、こうでしょ、と書きがち。
DEF_DLL MonitorFromPoint({long,long}, DWORD): DWORD: user32
前の記事でも書いたけど、これははずれ。
ここまでで書いているように、「{}」はポインターになります。
このため「{long,long}」はポインターの先に8byteのメモリーを確保したポインター(4byte)が渡されます。
ポインターとDWORDで8byteを渡しています。
しかし、MonitorFromPointが欲しいのは、POINT構造体(8byte)とDWORD(4byte)の12byte。
なので、これが正解。
DEF_DLL MonitorFromPoint(long,long,dword): dword: user32
ポイント:構造体はメンバーをそのまま書く
引数が実値のポインター
DWORD GetWindowThreadProcessId( HWND hWnd, LPDWORD lpdwProcessId );
「LPDWORD」はDWORDへのポインター。
DEF_DLL GetWindowThreadProcessId(HWND,var DWORD): DWORD: user32
ポイント:実値のポインターは、varをつければOK
引数が構造体のポインター
BOOL GetLastInputInfo( PLASTINPUTINFO plii ); typedef struct tagLASTINPUTINFO { UINT cbSize; DWORD dwTime; } LASTINPUTINFO, *PLASTINPUTINFO;
DEF_DLL GetLastInputInfo({DWORD, DWORD}): BOOL: user32
構造体の場合と、構造体のポインターの場合の違いに注意。
構造体の場合は、その構造体のサイズのメモリーを確保してやる必要があります。
構造体のポインターの場合は、ポインターの先にメモリーを確保したポインターを渡す。
ポイント:構造体のポインターは、{}でメンバーをかけばOK
ここでもう一つ。
ポインターの先にメモリーを確保したポインターを渡す方法は、
「{}」を使う方法と配列を使う方法があります。
配列も同じようにできます。
DEF_DLL GetLastInputInfo(DWORD[]): BOOL: user32
これに、数値二つの配列を渡す。(数値一つの配列だとメモリーが壊れるので注意)
これが横着定義。
ポイント:ポインターを渡す場合は、配列でもOK(ただし数に注意)
応用
GetMonitorInfoWをDEF_DLLにせよ!
第二引数には、MONITORINFOEXWを指定するものとする。
まあ、答えは前回書いているわけですが。
BOOL GetMonitorInfoW( HMONITOR hMonitor, LPMONITORINFOEXW lpmi ); typedef struct tagMONITORINFO { DWORD cbSize; RECT rcMonitor; RECT rcWork; DWORD dwFlags; WCHAR szDevice[CCHDEVICENAME]; } MONITORINFOEXW, *LPMONITORINFOEXW; typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT, *NPRECT, *LPRECT;
CCHDEVICENAMEは32です。
第一引数は、HMONITOR。これは4byteの数値。
DWORDで良いでしょう。
第二引数は構造体のポインターです。
構造体のポインターは、「{}」で書く、ですね。
DEF_DLL GetMonitorInfoW(DWORD,{long[],wstring}): BOOL: user32
お、横着定義ですね。
RECTはlongが4つ。4byteの数値が4つ。
そのRECTが2つと、DWORD・これも4byteの数値が2つ。
すなわち、4byteの数値が10個。
あと、WCHARが32個。
全部で、4*10+2*32=104byte。
なので、long10個の配列とstringに32文字確保して渡せばOK。
そう考えますよね。
はい、残念。不正解。
これは、配列と文字列がポインターになっていることを失念しています。
(DEF_DLLでは、wstringはwcharと同じです)
この場合、第二引数のポインターの先には、ポインターが二つあるだけ(8byte)で、
そのそれぞれの先に、40byteと64byteが別々に存在します。
104byteへのポインターになっていないのでダメです。
正解は、「long」のところにはlongを10個。
「wchar[]」のところにはwcharを32個書きましょう。
これでいける、、、んじゃないかなー、と思います。
それなら、104byteを確保したポインターになります。
(引数の数が多すぎで上手くいかない気もしますが、、、)
書きたくないので、検証もしてません。
さてさて。
個人的に、こんな面倒なことはできません。
横着定義に限ります。
「ポインターを渡す場合は、配列でもOK(ただし数に注意)」
ですね。
104byteを確保したポインターを渡せば良い。
104/4=26。
そうこれでOKです。
DEF_DLL GetMonitorInfoW(DWORD,long[]): BOOL: user32
で、26個渡す。
MONITORINFO構造体部分の数値は取り出しやすいけど、szDeviceはどうやるのよ、というのは
前回の記事を読んでください。
工夫して取り出せます。