def_dllにもいくつかの新機能が追加されています。
エイリアス機能
呼び出したいdll関数に任意の名前を付けることができるようになりました。
// 別名:DLL関数名 と記述することで別名で呼び出せるようになる
// MessageBoxWをMessageBoxという名前で呼べるようにする
def_dll MessageBox:MessageBoxW(hwnd, wstring, wstring, uint):int:user32.dll
print MessageBox(0, "別名呼び出しサンプル", "テスト", 0)
// これによりgetkeystate問題も解消される
def_dll GetKeyStateWin32:GetKeyState(int):word:user32
print GetKeyStateWin32(VK_RETURN) // Win32のGetKeyStateが呼ばれる
print getkeystate(VK_RETURN) // 組み込み関数が呼ばれる
配列サイズの明記
UWSCでは引数の型が配列だった場合に型名[]
という記述ができましたが、それに加えて型名[サイズ]
と書けるようになりました。サイズを指定した場合、異なるサイズの配列を引数として渡すとエラーになります。サイズ未指定時はUWSCと同等の動作(のはず)です。
// UWSC名物、配列による構造体の雑定義
def_dll GetWindowPlacement(hwnd, var int[]):bool:user32.dll
// 引数として渡す配列のサイズに気を付ける必要があった
dim wp[10]
GetWindowPlacement(idtohnd(id), wp)
for v in wp
print v
next
// UWSCRならサイズを明記することで安全に書ける
def_dll GetWindowPlacement(hwnd, var int[11]):bool:user32.dll
また、UWSCではどうにもならなかったサイズ指定のあるbyte
やchar
を持つ構造体にも対応できるようになりました。
def_dll WSAStartup(word, {word, word, char[257], char[129]}):long:ws2_32.dll
def_dll WSACleanup():long:ws2_32.dll
dim wVersion, wHighVersion, szDescription, szSystemStatus
WSAStartup($202, wVersion, wHighVersion, szDescription, szSystemStatus)
WSACleanup()
print wVersion
print wHighVersion
print szDescription
print szSystemStatus
追加された型定義
- handle: 引数がハンドル値であることを示す
- pointer: 引数がポインタであることを示す
- struct: 引数がユーザー定義構造体であることを示す
- callback: 引数がコールバック関数であることを示す (後述します)
- void: 戻り値がないことを明示するために使います
handle
はhwnd
と大差ないのですが、hwnd
じゃないのにhwnd
って書くの気持ち悪いな…という思いから生まれました。pointer
は構造体の生ポインタを得たい場合なんかに使うことがあります。ちなみにこれらはアーキテクチャによってサイズが可変なので注意してください。
UWSCのヘルプを見ると型のサイズ説明で
dword=uint=hwnd
と書かれていましたが、UWSCRではx64版があるためその限りではありません。x86版であればギリギリOKですが、hwnd
であるべきところをdword
と書くみたいなのはなるべくやめましょう。
struct
はユーザー定義構造体を受ける引数を示します。そう、UWSCRではついにCライクな構造体を定義できるようになったのです。冗長かつ複雑な{}
展開表記から開放されます!
ユーザー定義構造体については翌日(12/13)の記事で詳しく解説します。
woid
は戻り型専用です。戻り値がないことを明示します。
// これらは同じ
def_dll hoge():hoge.dll
def_dll hoge():void:hoge.dll
コールバック
UWSCでは不可能だったコールバック関数に対応しました。
callback(引数型):戻り型
のように記述することでコールバック関数の引数と戻り値の型を定義し、それに合わせたユーザー定義関数を渡すことでそのユーザー定義関数がコールバック関数として呼び出されるようになります。
// コールバック関数を定義
function MonitorEnumProc(hmonitor, hdc, prect, lparam)
print hmonitor
result = TRUE
fend
// 第三引数はコールバック関数定義
def_dll EnumDisplayMonitors(handle, pointer, callback(handle, handle, pointer, pointer):bool, pointer):bool:user32.dll
// callbackにはコールバック関数として呼ばれるユーザー定義関数を渡す
EnumDisplayMonitors(null, null, MonitorEnumProc, 0)
例に挙げたEnumDisplayMonitors
であれば第四引数に渡したポインタがそのままコールバック関数の第四引数(ここではlparam
)に渡ります。ユーザー定義構造体を利用することで構造体から得たポインタを渡してコールバック関数内でポインタから構造体の実態を得るといったことができるようになります。詳しくは翌日(12/13)の記事で解説します。
と、ここまで書いてみたのですがまたしても0.14.0
では動作しなくなっていました…STATUS_HEAP_CORRUPTION
で落ちているようです。これも原因を確認して0.15.0
で修正します。
UWSCとの実装の差異について
DLL関数によってはUWSCと同様の定義では動作しない場合がありえます。
例えば以下はUWSCでは正常動作しますがUWSCRでは動作しません。
def_dll WNetGetUniversalNameA(string, long, {var string}, var long):long:mpr
buf = format(NULL, 260)
len = lengthb(buf)
// 第二引数の 1 は UNIVERSAL_NAME_INFO_LEVEL
print WNetGetUniversalNameA("Z:", 1, buf, len)
print buf
UWSCRでは以下のような記述をすれば一応動作はします。
def_dll WNetGetUniversalNameA(string, long, {pointer, char[260]}, var long):long:mpr
dim buf, ptr = 0, size = 260
print WNetGetUniversalNameA("Z:", 1, ptr, buf, size)
print buf
今のところWNetGetUniversalName
でしかこのようなケースは見たことがないのですが、正直こういったケースにどう対応すればいいのか?が現時点では分からず手詰まり状態です。UNIVERSAL_NAME_INFOA
構造体が
typedef struct _UNIVERSAL_NAME_INFOA {
LPSTR lpUniversalName;
} UNIVERSAL_NAME_INFOA, *LPUNIVERSAL_NAME_INFOA;
なので{string}
だけで良さそうなんですけどなんでなんですかね?
詳しい方がご覧になってたらコメントしていただけるとありがたいです…
また上記にも関連するのかもしれませんが、文字列型(string
, pchar
, wstring
, pwchar
)の実装はUWSCと同じようにできている自信がまったくありません。何かまだ理解が及んでいない部分があるのではないか…という懸念があります。おかしな動作が見受けられた場合はgithubでご一報ください。