首页 目录页 热情软件屋 问专家

李海文选

如何获得Visual Basic变量的地址

李海

本文发表在《PC Computing中
文版/电子&电脑》97年10期

    Visual Basic提供了对DLL的调用,特别是对Windows API的调用,扩大了它的应用范围。但并不是所有的Windows API都可以被VB调用,不能调用的情况大概有以下三种:

    一是函数采用的不是Pascal调用方式。不同的语言采用的调用方法往往是不同的,VB采用的是Pascal调用方式,绝大多数Windows API都采用这一方式,但个别函数,如wsprintf、wvsprintf,就是采用的C语言调用方式。

    二是函数要求提供回调函数。VB不支持回调函数,所以不能直接使用这些函数。一般可以采用三种途径来解决这类问题:

  1. 利用其他API函数来替代,比如EnumWindows要求回调函数不能直接调用,可以采用GetWindow函数来替代(参见笔者在《电子与电脑》95年10期上发表的《让窗口“浮出水面”》一文);
  2. 采用VB自身提供的功能。例如,VB不支持EnumFonts函数,但可以通过Screen和Printer对象的Fonts和FontCount属性来获得所有可用字体;
  3. 编写DLL或VBX(参见笔者在《电子与电脑》97年6期上发表的 《完善VB的文件拖放功能》一文)。

    三是函数要求提供变量的地址。这是本文要解决的问题。

    VB本身采用的是传址(by refrence)调用,所以如果DLL的函数要求的是简单数据类型(象Integer、String)的地址是没有任何问题的。但如果是在结构变量中包括变量的地址,就要有麻烦了。结构变量是C语言的叫法,在VB里称为用户定义类型,即Type。举个例子来说,Windows的通用对话框(commdlg.dll)函数FindText的参数为一个FindReplace类型的结构,在C语言里是这样定义的:

    typedef struct tagFINDREPLACE

    {

    DWORD lStructSize; /* 结构的大小 */

    HWND hwndOwner; /* 父窗口的句柄 */

    HINSTANCE hInstance; /* 含有对话框模板的文件实例*/

    DWORD Flags; /* FR_??标志 */

    LPSTR lpstrFindWhat; /* 要查找的字符串的指针 */

    LPSTR lpstrReplaceWith; /* 要替换的字符串的指针 */

    UINT wFindWhatLen; /* 查找的字符串缓冲区的大小 */

    UINT wReplaceWithLen; /* 替换的字符串缓冲区的大小 */

    LPARAM lCustData; /* 传递给钩子函数的数据 */

    UINT (CALLBACK* lpfnHook)(HWND, UINT, WPARAM, LPARAM);

    /* 钩子函数的指针 */

    LPCSTR lpTemplateName; /* 定制模板的名字 */

    } FINDREPLACE;

    这里面要特别处理的两个参数是lpstrFindWhat和lpstrReplaceWith,这两个参数都是指向字符串的指针,也就是字符串变量的地址。如果直接将VB中的String型以传值的方式(即在参数前加ByVal关键字)传递给DLL,VB会将其转换为指向字符串的指针,而用户定义类型中的字符串被传递给DLL时,VB不做这种转换(关于这个问题更详细的描述参见笔者在《中国计算机用户》96年16期上发表的《VB4.0与OLE 2.0技术㈡:VB4.0与DLL间的参数传递》一文)。象这样的参数,如果不采用一点技巧是无法在VB中传递的。

    Windows API中有没有函数可以返回指向字符串的指针呢?有,而且还不少,如lstrcpy、lstrcpyn、lstrcat、AnsiNext、AnsiUpper等等。这之中最理想的是lstrcpy和lstrcpyn,它的定义是这样的:

    Declare Function lstrcpy Lib "Kernel" (lpszString1 As Any, lpszString2 As Any) As Long

    lstrcpyn的功能是将lpszString2串拷贝到lpszString1。如果拷贝成功,它返回指向lpszString1串的指针,在Windows中这种指针占四个字节,正好同Long型所占字节数相同。我们可以让lpszString1和lpszString2为同一个字符串,也就是自己拷贝自己,这样既可以返回字符串的地址,又不改变字符串的值,一举两得。下面这个函数就是利用这一思想来返回字符串变量的地址。

Function AddressOfString(s As String) As Long

    AddressOfString = lstrcpy(ByVal s, ByVal s)

End Function

    利用这一函数,我们很容易实现上面所说的FindText函数。

Type FindReplace

    lStructSize As Long

    hwndOwner As Integer

    hInstance As Integer

    Flags As Long

    lpstrFindWhat As Long

    lpstrReplaceWith As Long

    wFindWhatLen As Integer

    wReplaceWithLen As Integer

    lCustData As Long

    lpfnHook As Long

    lpTemplateName As Long

End Type

Declare Sub FindText Lib "commdlg.dll" (fr As FindReplace)

Dim fr As FindReplace

Dim strFindWhat As String * 256

Sub btnFind_Click

    strFindWhat = "What" + Chr$(0)

    fr.lStructSize = Len(fr)

    fr.hwndOwner = hWnd

    fr.lpstrFindWhat = AddressOfString(strFindWhat)

    fr.wFindWhatLen = 256

    FindText fr

End Sub

    请注意,我们在这里定义的FindReplace类型中lpstrFindWhat和lpstrReplaceWith定义为Long型,而不是String型。

    我们在此只是激活了FindText对话框,如果程序想得到用户输入的查询字符串,就要接受FindText发送的消息,这需要借助于VB Messenger (http://www.green-tree.com/downs/vbmsg20.exe)或Message Blaster等控制来完成。这部分内容超出了本文的范围,在此不加以讨论了。

    类似地,我们还可以实现ReplaceText函数、mciSendCommand函数等等。

    我们的这一方法不仅仅可以获得字符串函数的地址。我们同样可以将其应用到其他数据类型上。对于其他数据类型,我们采用lstrcpyn函数。lstrcpyn的定义是这样的:

    Declare Function lstrcpyn Lib "Kernel" (lpszString1 As Any, lpszString2 As Any, ByVal cChars As Integer) As Long

    lstrcpy和lstrcpyn的区别在于:lstrcpy直到源串中出现Chr$(0)才停止拷贝,而lstrcpyn拷贝时最多不超过cChars个字符。VB不支持C语言的变量类型强制转换,但我们在上面的lstrcpyn函数定义中,将前两个参数声明为Any类型来实现类似强制转换的效果。下面我们给出获取几种常用变量类型的地址的方法,读者可以进一步加以扩充。

Function AddressOfInteger(i As Integer) As Long

    AddressOfInteger = lstrcpyn(i, i, Len(i))

End Function

Function AddressOfLong(l As Long) As Long

    AddressOfLong = lstrcpyn(l, l, Len(l))

End Function

Function AddressOfSingle(f As Single) As Long

    AddressOfSingle = lstrcpyn(f, f, Len(f))

End Function

Function AddressOfVariant(v As Variant) As Long

    AddressOfVariant = lstrcpyn(v, v, Len(v))

End Function

    Variant类型有点特殊,在被赋值之前它的长度为0,所以不会被lstrcpyn拷贝,返回的地址为0。

    需要说明一点,本文的这种设计思想主要是针对目前广泛使用的VB 3.0和4.0版的,在新近推出的VB 5.0版中已经提供了VarPtr和AddressOf函数专门用来获得变量和函数的地址。

回到《李海文选》目录

如果您有任何建议,请给我发电子邮件:
版权所有 李海,热情软件屋 1997-2006


WU Banner from WebUnion Chinese Network