プログラミング

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に以下の関数を追加した。

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 呼び出し前

Hello World! と入力

API 呼び出し後

成功した。これで、構造体を引数に持つAPIの呼び出しに関しては、ほぼ完全に対応できたと思われる。

(ここで使用したDLL及びスクリプトは、ここからダウンロードできます。)

(続く)

コメント

コメントはありません

コメント送信