在Basic语言演变成QBasic,然后到Visual Basic之前,VarPtr函数就已经存在了。开始,这个函数存在于VB运行库1.0版中。通过声明可以调用这个函数:
Declare Function VarPtr Lib "vbrun100.dll" (Var As Any) As Long
数年之后,vbrun100.dll变成了msvbvm50.dll,但该函数的入口点却还在那儿。为了获取变量的地址,只须将变量名传递给该函数就行了。例如:
Dim l As Long
Debug.Print VarPtr(l)
类似地,为了获取字符串的指针,而非保存字符串的变量的指针,只须在变量名前加上ByVal即可。如:
Debug.Print VarPtr(s),VarPtr(ByVal s)
在VB3之前,用这种方法来获取字符串缓冲的指针是非常普遍的。但在VB4却遇到了一点麻烦。
ANSI/UNIDCODE问题
随着32位世界和VB4的到来,我们迈进了一半是UNICODE,一半是ANSI的Windows世界。而在此之前,是ANSI一统天下。在VB中,所有字符串按UNICODE保存,但所有的API调用却仍使用ANSI字符串。这就要求在调用API函数之前,将字符串从UNICODE转换成ANSI,函数执行结束后,将返回的字符串从ANSI转换成UNICODE。虽然大多数时候这种转换对用户来说是透明的,但这就使利我们不能将一个字符串类型的参数以UNICODE方式从VB传递给DLL。类似地,任何包含有字符串的结构在执行API调用时,也必须经过这种双重转换。
这种差异是如何影响VarPtr函数的呢?当一个字符串传递给VarPtr函数时,函数执行后所返回的地址是保存临时ANSI字符串的临时ANSI字符串或变量的地址。换句话说,这个地址并不是你声明的变量的真正地址。因此,对于字符串变量以及包括字符串的结构来讲,这个函数一点用也没有。
VB5来解决问题
为了能VarPtr能重新发挥作用,VB5(及Office97)加入了三个针对VBA类型库的入口点。这些入口点为VarPtr函数提供了内置的声明。这三个函数的作用是:
VarPtr:返回变量地址
StrPtr:返回真正的UNICODE字符串缓冲区的地址
ObjPtr:返回任何对象变量引用的地址
VarPtr其函数原型如下,用OleView打开c:\windows\system32\msvbvm60.dll可知:
[entry(0x60000006), hidden]
long _stdcall VarPtr([in] void* Ptr);
请看下两个例子:
例一:
Dim s As Long:s=-11 ‘为非指针型变量均可,如Byte等,会转化成Long。
MsgBox VarPtr(s) ‘得到变量s的地址
MsgBox VarPtr(-11) ‘得到临时变量的地址
MsgBox VarPtr(ByVal s) ‘不用建立临时变量,结果为-11。如果VarPtr为取址,肯定不会为-11;此处不取址了,仅转化值。
例二:
Dim s As string:s=”asdfg”
MsgBox VarPtr(s) ‘得到变量s的地址
MsgBox VarPtr(“asdfg”) ‘得到临时变量的地址
MsgBox VarPtr(ByVal s) ‘得到字符串”asdfg”的地址
上面两例的结果均为Long型,且前两个地址数值相近,因均属变量型,而最后一个为常量。
VB不鼓励使用指针,比如MsgBox s,单独用变量名s,就是无法得地址。VarPtr的最大作用,当然是突破限制、“抠”出了变量的指针数值。那“数值转化”有什么用呢?其实没什么特别。我们来自己定义个VarPtr,在例一中加上Private Declare Function VarPtr Lib "msvbvm60.dll" (x As Any) As Integer,会进行Long与Integer的转换。
再看例三,三个MsgBox的结果相同:
例三:
Dim s As Long:s = -11
MsgBox VarPtr(s)
MsgBox VarPtr(ByVal VarPtr(s)) ‘不可省去ByVal,那会是临时变量的地址
Dim t As Long
t = VarPtr(s)
MsgBox VarPtr(ByVal t)
End
可以看出,实参中ByVal VarPtr(x)的搭配和(ByRef) x一样;VarPtr(ByVal x)和x一样。