2010/08/12

ポインタを使用したネイティブコード関数呼び出し

DLLが提供するネイティブコード関数の中には、引数にポインタを指定して、返り値以外にも値を受け取れるようになっている物があります。このような場合は、引数の型を「Pointer」にしておいて、Intなどの「alloc()」メソッドで生成したバッファオブジェクトを渡してやる必要があります。

下記は、その一例です。

var GetComputerName=kernel32.proc('GetComputerNameW',[WideString,Pointer],UInt);
try{
 var buf=WChar.alloc(128);
 var size=UInt.alloc().update(127);
 println(GetComputerName(buf,size));
 println(size.item());
 println(buf.toString());
}finally{
 free(buf,size);
}

Unicode文字列バッファで値を受取るには、引数の型を「WideString」にしておいて、「WChar.alloc()」で確保したバッファを渡します。alloc()の引数には、確保する要素数を指定します。引数の型はWideStringですが、alloc()はWCharのメソッドである事に注意してください。
また、バイト配列やマルチバイト文字列の場合、引数の型を「MBString」にして、バッファの確保は「Byte.alloc()」で行います。
WideStringやMBStringは、「toString()」メソッドでゼロ終端文字列とみなしてJavaScriptの文字列値に変換できます。

「UInt.alloc()」では、1つのUIntバッファを確保しており、続く「update(127)」でバッファの内容を書き換えています。このメソッドは、メソッドを呼び出されたPointerオブジェクト自身を返します。
ポインタオブジェクトを引数として関数を呼び出した後、「item()」メソッドを使用すれば、バッファの内容を数値として取得できます。

最後に、確保したバッファを解放するため、「free()」という関数を呼び出しています。この関数は、引数がfree()メソッドを持つオブジェクトだった場合にそれを呼び出すという動作になっており、Pointerオブジェクト以外にもfree()メソッドを持つ様々なオブジェクトの解放処理をまとめて実行できます。
free()は、何らかの問題があって例外が投げられた場合などにも必ず実行されるように、try...finally文のfinally節で実行する必要があります。また、この方法を使えば、buf.toString()の値を関数の返り値として返したい場合などに、いちいち変数に代入してからbufをfree()したりする必要が無くなるという利点もあります。

なお、JavaScriptにはGCという仕組みが用意されており、文字列やオブジェクトなどの値はどこからも参照されなくなったら自動的に解放されるようになっていますが、NILScriptでは敢えてこの仕組みを使わずに、明示的に解放処理を記述するようになっています。
これは、JavaScriptには「クロージャ」という仕組みがあり、変数に代入した値がその変数を定義した関数やブロックを抜けてもずっと参照されたままの状態になってしまうことがよくあるからです。
これは、変数を参照可能な場所で生成された関数が他の場所から参照され続けていることによるものですが、イベントハンドラ関数などが多用されているNILScriptでは、この状態が発生しやすくなっています。
「buf=null」などとしてやれば参照が失われてGCの解放対象となりますが、それではfree()を呼び出すのと同じですので、より直接的なfree()による解放が採用されています。

0 件のコメント:

コメントを投稿