Visual Basic 的秘密(翻译)

原文:http://www.thevbzone.com/secrets.htm
作者:Kevin Wilson

介绍:

Visual Basic 被称为“快速应用程序开发(RAD)开发工具”,因为它旨在为你处理Windows“基础工作”,从而使你可以专注于重要的东西,如程序的功能和文档。

例如,你打开VB,新建一个项目,然后在项目中添加一个标准的“窗体”,按“F5”来执行程序,该窗体就被显示出来,我们操作起来非常简单,但是你可能不知道,VB在背后帮我们做了很多事情。VB需要调用“CreateWindow”来实际创建“窗体”,并为构成它界面的属性赋值。 然后,需要通过调用各种Win32 API来修改窗体上的字体,前景色,背景色,设备上下文(device context)等。 最后,VB需要用子类化的方式,将新创建的窗体挂钩(hook)到Windows消息流中,让窗体捕获到发送给它的Windows消息,最后使用“WindowProc”回调函数,处理每个Windows消息。 窗体的接口越复杂,窗体对象的创建和功能处理代码就越复杂。 而C和C ++程序员就没有这么轻松了,他们需要亲自编写代码,来创建所有的对象,处理消息流以及销毁对象(或者通过模板来生成代码)。

对于会正确使用VB开发工具的程序员来讲,Visual Basic能帮助你做一些类似上述“基础”的事情,是一个非常强大的功能。但是,与此同时,那些不太了解如何编程的人,也拥有了很大的能力。也正是这个原因,Visual Basic被C和C++程序员嘲笑。他们说,“任何人都可以用VB做开发,但只有真正的程序员才能使用C / C++做开发。我认为,聪明的程序员选择Visual Basic,因为VB在对象创建,消息处理,对象销毁过程中,可以帮你消除潜在的Bug。VB能提供更简单更快捷Windows事件处理,VB能为你提供更强大的界面功能,VB能让你更轻松的访问COM对象和第三方控件,VB更容易阅读,因为它非常接近阅读英语,而C/C++看上去却非常神秘。VB允许你轻松访问Win32 API (这使得程序员有驾驭Windows的强大功能的能力)。以及最最重要的是,Visual Basic可以通过组件,库,和其他用C/C++编写的代码来挂钩住(hook)C/C++的强大功能和速度。嘿。。。。。。C/C++程序员,现在怎么不继续吹牛逼了?^_^

事情就是这样。。。。。。即使是工作多年的VB程序员也没有意识到VB的真正力量,因为他们没有掌握(或意识到)VB提供的一些关键概念和功能。这些概念很少被大家知道,或者受重视的程度还远远不够,因为我将它们称之为“VB的秘密”。

在VB中使用指针:

我曾经在求职面试中被问到一个问题,现在我意识到这是一个有坑的问题。“Visual Basic 是否有或者使用‘指针’?”任何用过VB的人都会显而易见的回答“No”,在VB中,你看不到任何像C/C++中那样的指针的声明,这就是当时我认为面试官要问的点,而她对我的答案也表示认同。然而,正确的答案应该是“Yes”。

Visual Basic (就像几乎所有其他编程语言一样)确实使用指针,广泛的使用。不同的是,Visual Basic会尽可能的将它们隐藏起来,或者将它们称为不同的东西,以免给你带来负担。

接下来,我们谈谈,如何使用指针直接访问变量中的信息(VarPtr / StrPtr / ObjPtr),通过指针将信息传递给函数(ByRef / ByVal),取回和传递指向函数的指针(AddressOf)。

VarPtr,StrPtr 和 ObjPtr:

VB函数“VarPtr”(变量指针),“StrPtr”(字符串指针)和“ObjPtr”(对象指针)是无官方文档(undocumented),不支持的(unsupported)的函数,微软在VB5.0和VB6.0中引入。这些函数(跟随很多其他的函数一起)在VB.Net中不再使用。这些函数允许你获取VB变量在内存中的地址(指针),以及变量所指向的实际数据在内存中的地址。这些函数为什么作用这么大?因为,如果你知道数据的内存地址,你可以任意操控它,直接复制或者传递出去,而不需要借助VB的帮助。这样做,速度上更快,并且(在某些情况下)你可以做到VB本身做不到的事情。

下面是微软MSDN关于“VarPtr”的说明:


该函数可以用来获取一个变量或数组元素的地址。它使用变量名或数组元素作为参数,返回它的地址。然而,你需要注意的是,未锁定的动态数组可能会被Visual Basic重新分配地址,所以你当你使用VarPtr获取数组元素地址时,必须非常小心。

下面的示例获取变量的地址:

Dim lngVariableAddress As Long
Dim dblMyVariable As Double
lngVariableAddress = VarPtr(dblMyVariable)

下面的示例获取某个数组的第4个元素的地址:

Dim lngElementAddress As Long
Dim lngArrayOfLongs(9) As Long
'下面的代码将获取数组中第4个元素的地址
lngElementAddress = VarPtr(lngArrayOfLongs(3))

限制:VarPtr函数不能用来获取数组的地址。。。。。。


下面是微软MSDN关于“StrPtr”的说明:


在Visual Basic 中,字符串是以BSTR来存储的。如果你对一个字符串变量使用VarPtr,你将得到BSTR的地址,它是该字符串指针的指针。要获取字符串缓冲本身的地址,你需要使用StrPtr函数。该函数返回字符串第一个字符的地址。需要考虑到在Visual Basic中,字符串是以UNICODE来存储的。

要获取一个字符串的第一个字符的地址,请将该字符串变量传递给StrPtr函数。

示例:

Dim lngCharAddress As Long
Dim strMyVariable As String
strMyVariable = "Some String"
LngCharAddress = StrPtr(strMyVariable)

当你需要传递一个指向UNICODE字符串指针给API时,你可以使用StrPtr函数。


下面是微软MSDN关于“ObjPtr”的说明:


ObjPtr函数使用一个对象变量名作为参数,获取该对象变量所引用的接口的地址。

一种试用该函数的情况,是当你需要处理集合对象的时候。相比使用Is操作符遍历集合对象而言,通过使用对象地址作为索引关键字,你可以获取更快的访问速度。在很多情况下,对象的地址是唯一可以信赖的键值。

示例:

objCollection.Add MyObj1, CStr(ObjPtr(MyObj1))
'…
objCollection.Remove CStr(ObjPtr(MyObj1))

注意在“VarPtr”说明的底部的“限制”,它说你不能使用VarPtr获取数组的地址。在某种程度上,说得没错。你不能将变量“MyArray”传递给它(因为VB将数组保存在一个叫做“SafeArray”的OLE对象中),但是,如果你获取了数据的第一个元素“MyArray(0)”的地址,你就有了整个数组的地址,因为数组元素在内存中是连续存储的(按照数字顺序从第一个元素到最后一个元素)。所以,如果某个Win32 API 或者C / C++ 函数需要一个指向某个字节数组的指针,像这样:

Option Explicit
Private Type POINTAPI
   X As Long
   Y As Long
End Type

'BOOL Polyline(
'  HDC          hDC,    // handle of device context
'  CONST POINT *lpPT,   // address of array containing endpoints
'  int          cPoints // number of points in the array
');
Private Declare Function Polyline Lib "GDI32.DLL" (ByVal hDC As Long, _
        ByRef lpPT As Any, ByVal cPoints As Long) As Long

你可以像这样调用它:

Private Sub Form_Load()
  Dim ThePoints() As POINTAPI
  Me.AutoRedraw = True
  Me.Visible = True
  Me.Move 0, 0, Me.Width, Me.Height
  ReDim ThePoints(1 To 5) As POINTAPI
  ThePoints(1).X = 0:   ThePoints(1).Y = 0
  ThePoints(2).X = 100: ThePoints(2).Y = 0
  ThePoints(3).X = 100: ThePoints(3).Y = 100
  ThePoints(4).X = 0:   ThePoints(4).Y = 100
  ThePoints(5).X = 0:   ThePoints(5).Y = 0
  If Polyline(Me.hDC, ByVal VarPtr(ThePoints(1)), 5) = 0 Then Debug.Print "FAILED!"
  Me.Refresh
  Erase ThePoints
End Sub

提示:小心存储指向动态数组的指针,因为当重新分配,调整大小或重新编写数组时…很可能你会为实际数据提供一个全新的内存地址。

ByRef / ByVal

待续…

AddressOf 和回调

待续…

调用“隐藏的”API

待续…

作者: Hugh

Welcome to Wan's world~