WSHでWin32APIを呼び出す-その9
2005年7月7日
(←戻る)
前回までに製作した、dwtools.dllは、『Hello, World!』プログラムに特化したものであり、まだまだ汎用性は低い。現段階では、少なくとも次のような問題が有る。
1)ユーザ定義の構造体を引数に持つAPIを呼び出した際、そのAPIからの戻り値が構造体に収められている場合に、それを取り出す事が出来ない。
2)WndProcで、WM_PAINT, WM_DESTROY以外のメッセージをトラック出来ない。
3)WndProcで呼び出されるスクリプトのファイル名が、wndproc.vbsに限られている。
4)wndproc.vbsで、グローバル変数・スタティック変数を使用することが出来ない。
5)WndProc以外のコールバック処理(EnumXXXX等)が行えない。
これらの内、1)-3)は、解決可能である。4)については、グローバル変数の内容をファイルに保存する形にすれば解決できる(他にも手があるかもしれない)。5)については、DLLの中に新たにコールバック関数を作成するしか手がないが、よく使う『EnumWindows』あたりを実装しておけば、あとは殆ど必要無いのではなかろうか(VB ver4 以前では不可能な機能であったわけだし)。
そこでまず、1)-3)について対処することにした。
まずは構造体の値の取得から。これ用に、DLLに以下の関数を追加した。
文字列の呼び出しには、dynawrapNt.zipに含まれているReadMe.txtを参考にして、"I=r"(pass by reference)を試したのだけれど、うまく行かなかった。ReadMeの中でも書かれているがうまく行く場合と行かない場合があるらしい。そこで、DLLでは一文字ずつ取り出して、VBscriptの側でつなげて文字列を完成させる形にした。
VBscriptの側は、構造体用の宣言をなるだけ簡潔になるようにした。構造体用のクラス宣言部分は以下の通り(OPENFILENAME構造体の例)。
実際にはこのあとに、すべての構造体宣言に共通の関数群を記述したスクリプト(150行ほど)を追加して用いる。このスクリプト中では、Execute命令を用いることと、スクリプト自身をテキストファイルとして呼び出して用いることで、それぞれの構造体宣言がなるだけ簡潔になるようにした。複雑なスクリプトになってしまったが、一度うまく行けばあとは変更の必要はほとんど無いはずである。
OpenFile.Structureという形で構造体を呼び出してAPIを使用したあと、OpenFile.GetDataを行うと、構造体のメンバの値がAPIからの戻り値に対応する。これを用いてAPI『GetOpenFileName』の試験用に実行したスクリプトが以下の通り(実際にはこのあと、OPENFILENAME構造体宣言用のスクリプトが続く)。
このスクリプトの実行結果は以下の通り。
成功した。これで、構造体を引数に持つAPIの呼び出しに関しては、ほぼ完全に対応できたと思われる。
(ここで使用したDLL及びスクリプトは、ここからダウンロードできます。)
(続く)
前回までに製作した、dwtools.dllは、『Hello, World!』プログラムに特化したものであり、まだまだ汎用性は低い。現段階では、少なくとも次のような問題が有る。
1)ユーザ定義の構造体を引数に持つAPIを呼び出した際、そのAPIからの戻り値が構造体に収められている場合に、それを取り出す事が出来ない。
2)WndProcで、WM_PAINT, WM_DESTROY以外のメッセージをトラック出来ない。
3)WndProcで呼び出されるスクリプトのファイル名が、wndproc.vbsに限られている。
4)wndproc.vbsで、グローバル変数・スタティック変数を使用することが出来ない。
5)WndProc以外のコールバック処理(EnumXXXX等)が行えない。
これらの内、1)-3)は、解決可能である。4)については、グローバル変数の内容をファイルに保存する形にすれば解決できる(他にも手があるかもしれない)。5)については、DLLの中に新たにコールバック関数を作成するしか手がないが、よく使う『EnumWindows』あたりを実装しておけば、あとは殆ど必要無いのではなかろうか(VB ver4 以前では不可能な機能であったわけだし)。
そこでまず、1)-3)について対処することにした。
まずは構造体の値の取得から。これ用に、DLLに以下の関数を追加した。
char __stdcall DwGetChars(int pos, char** orgP) { char* org=*orgP; unsigned int i; if (TempStr<=org && org<=TempStr+4095) for (i=0;org[i]!=0;i++); if (pos return 0; } int __stdcall DwGetStringLength(char** orgP) { char* org=*orgP; int i=1; if (org==0) return -1;//when Null string if (TempStr<=org && org<=TempStr+4095) for (i=0;org[i]!=0;i++); return i; } void __stdcall DwGetLong(unsigned long dest,long* org) { if (TempStr<=org && org<=TempStr+4092) dest=*org; } void __stdcall DwGetInt(unsigned short dest,short* org) { if (TempStr<=org && org<=TempStr+4094) dest=*org; } void __stdcall DwGetByte(unsigned char dest,char* org) { if (TempStr<=org && org<=TempStr+4095) dest=*org; }
文字列の呼び出しには、dynawrapNt.zipに含まれているReadMe.txtを参考にして、"I=r"(pass by reference)を試したのだけれど、うまく行かなかった。ReadMeの中でも書かれているがうまく行く場合と行かない場合があるらしい。そこで、DLLでは一文字ずつ取り出して、VBscriptの側でつなげて文字列を完成させる形にした。
VBscriptの側は、構造体用の宣言をなるだけ簡潔になるようにした。構造体用のクラス宣言部分は以下の通り(OPENFILENAME構造体の例)。
Class OPENFILENAME Private Sub Class_Initialize() Execute TypeInitialize("OPENFILENAME") End Sub Public Function Structure() Execute TypeStructure End Function Public Sub GetData() Execute TypeGetData End Sub Public Length Private propertyNum, properties(), types(), struct, UserWrap Public lStructSize ' As Long Public hwndOwner ' As Long Public hInstance ' As Long Public lpstrFilter ' As String Public lpstrCustomFilter ' As String Public nMaxCustFilter ' As Long Public nFilterIndex ' As Long Public lpstrFile ' As String Public nMaxFile ' As Long Public lpstrFileTitle ' As String Public nMaxFileTitle ' As Long Public lpstrInitialDir ' As String Public lpstrTitle ' As String Public flags ' As Long Public nFileOffset ' As Integer Public nFileExtension ' As Integer Public lpstrDefExt ' As String Public lCustData ' As Long Public lpfnHook ' As Long Public lpTemplateName ' As String End Class
実際にはこのあとに、すべての構造体宣言に共通の関数群を記述したスクリプト(150行ほど)を追加して用いる。このスクリプト中では、Execute命令を用いることと、スクリプト自身をテキストファイルとして呼び出して用いることで、それぞれの構造体宣言がなるだけ簡潔になるようにした。複雑なスクリプトになってしまったが、一度うまく行けばあとは変更の必要はほとんど無いはずである。
OpenFile.Structureという形で構造体を呼び出してAPIを使用したあと、OpenFile.GetDataを行うと、構造体のメンバの値がAPIからの戻り値に対応する。これを用いてAPI『GetOpenFileName』の試験用に実行したスクリプトが以下の通り(実際にはこのあと、OPENFILENAME構造体宣言用のスクリプトが続く)。
option explicit Dim UserWrap Set UserWrap = CreateObject("DynamicWrapper") UserWrap.Register "comdlg32.dll", "GetOpenFileNameA", "I=l", "f=s", "R=l" Dim OpenFile Set OpenFile=New OPENFILENAME OpenFile.lpstrInitialDir = "C:\temp" OpenFile.lStructSize = OpenFile.Length OpenFile.hwndOwner = 0 OpenFile.lpstrFilter = "*.*" OpenFile.nFilterIndex = 1 OpenFile.lpstrFile = "Test.txt" + String(256, 0) OpenFile.nMaxFile = 256 OpenFile.lpstrDefExt = "" OpenFile.lpstrTitle = "Open File" OpenFile.flags = 4 '//hide "Read Only" checkbox msgbox OpenFile.lpstrFile Call UserWrap.GetOpenFileNameA(OpenFile.Structure) Call OpenFile.GetData msgbox OpenFile.lpstrFile
このスクリプトの実行結果は以下の通り。
成功した。これで、構造体を引数に持つAPIの呼び出しに関しては、ほぼ完全に対応できたと思われる。
(ここで使用したDLL及びスクリプトは、ここからダウンロードできます。)
(続く)