プログラミング

WSHでWin32APIを呼び出す-その6

2005年7月1日

(←戻る)
 前回までの記事で、ANSI文字列を自作DLL内で確保したメモリ領域に構築し、そのANSI文字列に対するポインタをVBscriptに渡すことに成功した。今回はいよいよ、ユーザ定義型の構造体を引数に持つWin32APIの呼び出しである。テストとして、comdlg32.dllにある、GetOpenFileNameを呼び出してみた。このAPIはOPENFILENAME構造体を使用している。


 今回使用した自作DLLは次の通りである。
char TempStr[4096]="";
int TempStrPoint=0;

char* __stdcall DwGetAddressOfString(char* T, int Size)
{
    int i;
    //Copy T to TempStr
    for (i=0;(TempStr[TempStrPoint+i]=T[i])!=0;i++) 
        if (4095<=TempStrPoint+i) return 0;//if buffer is full

    if (0<Size) 
    {
        if (Size<i) return 0;//if size exceeds
        i=Size;
    }//else if (0==Size) do nothing

    //Check if buffer is full
    if (TempStrPoint+i<4095)
    {
        TempStrPoint+=i+1;
        return TempStr+TempStrPoint-i-1;
    } else {
        TempStrPoint=4096;
        return 0;//Return zero if buffer is full
    }
}

void __stdcall DwCopyLong(unsigned long org,long* dest)
{
    if (TempStr<=dest && dest<=TempStr+4092) *dest=org;
}

void __stdcall DwCopyInt(unsigned short org,short* dest)
{
    if (TempStr<=dest && dest<=TempStr+4094) *dest=org;
}

void __stdcall DwClearBuffer()
{
    TempStrPoint=0;
}

『DwGetAddressOfString』でメモリ領域を確保し、『DwCopyLong』と『DwCopyInt』でその領域に書き込みを行う。『DwClearBuffer』は、使用したメモリ領域を開放する時に呼び出す。これらの関数群と連携させるようにVBscript上で構築した、OPENFILENAME様のスクリプトが、次である。Visual Basicでの構造体の取り扱いと互換性を持たせるため、クラスを用いた。
Class OPENFILENAME
  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

  Public Function Structure()
    UserWrap.DwClearBuffer
    Structure=UserWrap.DwGetAddressOfString("",76) 'Length of structure is 76
    UserWrap.DwCopyLong lStructSize, Structure ' As Long
    UserWrap.DwCopyLong hwndOwner, Structure+4 ' As Long
    UserWrap.DwCopyLong hInstance, Structure+8 ' As Long
    UserWrap.DwCopyLong StrAddress(lpstrFilter), Structure+12 ' As String
    UserWrap.DwCopyLong StrAddress(lpstrCustomFilter), Structure+16 ' As String
    UserWrap.DwCopyLong nMaxCustFilter, Structure+20 ' As Long
    UserWrap.DwCopyLong nFilterIndex, Structure+24 ' As Long
    UserWrap.DwCopyLong StrAddress(lpstrFile), Structure+28 ' As String
    UserWrap.DwCopyLong nMaxFile, Structure+32 ' As Long
    UserWrap.DwCopyLong StrAddress(lpstrFileTitle), Structure+36 ' As String
    UserWrap.DwCopyLong nMaxFileTitle, Structure+40 ' As Long
    UserWrap.DwCopyLong StrAddress(lpstrInitialDir), Structure+44 ' As String
    UserWrap.DwCopyLong StrAddress(lpstrTitle), Structure+48 ' As String
    UserWrap.DwCopyLong flags, Structure+52 ' As Long
    UserWrap.DwCopyInt nFileOffset, Structure+56 ' As Integer
    UserWrap.DwCopyInt nFileExtension, Structure+58 ' As Integer
    UserWrap.DwCopyLong StrAddress(lpstrDefExt), Structure+60 ' As String
    UserWrap.DwCopyLong lCustData, Structure+64 ' As Long
    UserWrap.DwCopyLong lpfnHook, Structure+68 ' As Long
    UserWrap.DwCopyLong StrAddress(lpTemplateName), Structure+72 ' As String
  End Function

  Private Function StrAddress(str)
    Select Case VarType(str)
    Case vbString
      StrAddress=UserWrap.DwGetAddressOfString(str+"",0)
    Case Else
      StrAddress=0 'return Null string
    End Select
  End Function

End Class

このクラスには、『Structure()』という、パブリック関数を持たせてある。この関数を『xxxxx.Structure』という形で呼び出すことによって、DLL内のメモリに構築されたOPENFILENAMEへのポインタ値を得ようということである。

 さて、ここまで来ればあとはメインルーチンを書き上げるだけである。作成したスクリプト、『OpenFile.vbs』のメインの部分を下に示す。(実際にはこの記述のあとに、上記のOPENFILENAMEクラスが記述されている。)
option explicit
Dim UserWrap
Set UserWrap = CreateObject("DynamicWrapper")
UserWrap.Register "comdlg32.dll", "GetOpenFileNameA", "I=l", "f=s", "R=l"
UserWrap.Register "debug\dwtools.dll", "DwGetAddressOfString", "I=su", "f=s", "R=l"
UserWrap.Register "debug\dwtools.dll", "DwClearBuffer",  "f=s"
UserWrap.Register "debug\dwtools.dll", "DwCopyLong", "I=ll", "f=s", "R=l"
UserWrap.Register "debug\dwtools.dll", "DwCopyInt", "I=ul", "f=s", "R=l"

Dim OpenFile
Set OpenFile=New OPENFILENAME

OpenFile.lpstrInitialDir = "C:\temp"
OpenFile.lStructSize = 76
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 '読み込み専用チェックボックスを隠す
call UserWrap.GetOpenFileNameA(OpenFile.Structure)


 このスクリプトの実行結果は、次の通り。成功である。これで、ユーザ定義型の構造体を引数に持つAPIも呼び出せるようになった。一つ山を越した感がある。引き続き、『Hello world!』プログラムの検討に戻ってみよう。さらなる難関が待ち受けている…。

Hello world! にはまだ遠い…。
pop up

(ここで使用したOpenFile.vbs及びdwtools.dllはここからダウンロードできます。)

(続く)

コメント

コメントはありません

コメント送信