正文

巧用Win32 API函数增强VB位操作功能(1)2006-05-23 22:47:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/stef/14755.html

分享到:

摘要:Visual Basic 的位操作功能较弱,甚至连最常用的移位运算都不支持,因此在使用VB开发诸如数据加密、压缩、通信之类的程序时往往困难重重。针对这一问题,本文详细地阐释了位操作的本质,并利用Win32 API函数实现了整型变量的拆分、合并、移位等VB不支持的位操作功能。一 引言  笔者在编程实践中发现,VB对位操作的支持仅限于AND、OR、XOR几种位运算,远远不如其他的开发工具那样全面(如Visual C++、C++Builder、Delphi等开发工具都提供了整形变量的移位、拆分、合并的运算),因此在使用VB编写诸如加密之类的通用数据处理程序时往往困难重重。为了使以后的开发工作不再陷入僵局,我开始寻求增强VB位操作功能的通用方法,以达到一劳永逸的效果。  VB的数据类型不够丰富,整形数只包括Byte、Integer、Long三种类型,分别对应C++中的 unsigned char、short 和 long 类型,而我们常用的二字节无符号整形unsigned short(也叫“字”、Word)、四字节无符号整形unsigned long(也叫“双字”、DWord)在VB中却没有被支持。 但好在无符号数和有符号数在二进制的层次上没有任何差别,不同之处仅在于编译器对变量的理解。在进行位操作时我们只关心变量的二进制位,因此VB中的Integer类型可以当作Word类型使用,Long类型则对应DWord。(此后文中提及的Integer类型均指VB Integer类型,Long类型均指VB Long类型,Word 、DWord类型则是不依赖于特定编译器的对二字节、四字节整形值的通用称呼)再来看位运算方面,可以看出VB不支持整型变量的左移、右移、拆分、合并等操作。  经过上述的分析之后,已经确定了工作的可行性和工作目标,于是笔者决定开发一个通用模块来增强VB的位操作功能,这个模块是可重用的,只要把这个模块加入工程中,就可以象使用VB的内置函数一样透明的使用模块中的函数,非常方便。如果使用大量的可重用模块来开发程序,则开发周期短,代码可读性好,易于维护,不容易出错。 二设计思路1. 实现整形变量的拆分、合并  整型变量的拆分、合并是经常要用到的操作,比如IP地址就是一个四字节的双字,有时候为了以点分十进制的方式显示IP地址,就需要单独取出每个字节的值,而有时候为了把点分十进制的IP地址转换为计算机内部的双字,又需要把四个字节组合成一个双字。VB没有提供这样的功能,所以整型变量的拆分、合并也是我们这次要实现的功能。另外整型变量的拆分、合并也是实现Integer、Long类型变量移位的前提条件(后面“分而治之策略”将会提到),只要实现了整型变量的拆分合并,移位问题就完全解决了。方法1:利用API函数Copymemory实现  在这里笔者利用Win32 API 函数CopyMemory实现了整形变量的拆分、合并操作。在VB中使用API函数必须要声明,CopyMemory函数的声明代码如下: Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _(Destination As Any, Source As Any, ByVal Length As Long) 方法2:利用安全数组借用内存的方法实现  方法1虽然用起来简单方便,但是要执行API函数调用,函数调用时要保存现场、恢复现场,时间开销很大,效率太低,因此不适合大数据量密集运算的场合。笔者在开发加密软件时曾使用方法1来处理文件数据,效果很不理想,速度奇慢。其实有一种方法可以巧妙的骗过VB,让一个数组直接访问其他变量的内存空间,从而达到拆分、合并整形变量的目的。由于这种方法省去了API函数调用,因此效率非常高。下面就让我们认识一下VB中的安全数组。VB中的安全数组与C语言中的数组有很大的差别,虽然在VB和C语言中数组变量都是指针,但C语言中的数组变量直接指向数组元素,而 VB中的数组变量却是指向一个SafeArray结构,这个SafeArray结构中的pvData域才指向数组元素。  那么这个SafeArray结构是做什么用的呢?它存储着数组的上界、下界、维数、元素大小等一系列的信息,正是SafeArray结构的存在,使得VB程序能够对数组的访问做越界检查,这就是为什么VB中的数组叫做安全数组的原因,而C语言中的数组显然不具备越界检查的能力。当然安全数组的缺点就是没有C语言的数组灵活,但尽管如此,我们还是有办法操纵它,通过对安全数组的操纵,可以让它访问任意的内存位置,甚至包括其他变量的内存空间。对于一维数组来说,它的SafeArray结构如下: Type SafeArray1d '1维数组的 SafeArray 定义cDims As Integer '维数fFeatures As Integer '标志cbElements As Long '单个元素的字节数clocks As Long '锁定计数pvData As Long '指向数组元素的指针cElements As Long '维定义,该维的元素个数Lbound As Long '该维的下界End Type   如果显式的给一个数组变量赋值,让它指向我们自己创建的SafeArray结构,就可以通过设置SafeArray结构的pvData域来访问任意内存位置。请看示例代码: Public Declare Function VarPtrArray Lib "msvbvm60.dll" _Alias "VarPtr" (ptr() As Any) As LongPrivate Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)Private Sub Command2_Click()Dim pBytesInLong() As ByteDim SA1D As SafeArray1dDim i As LongWith SA1D.cDims = 1.fFeatures = 17.cbElements = 1.clocks = 0.pvData = VarPtr(i) '使数组的数据指针指向长整形变量 i.cElements = 4.Lbound = 0End With'使数组变量(其实就是个指针)指向我们自己创建的 SafeArray1d 结构CopyMemory ByVal VarPtrArray(pBytesInLong), VarPtr(SA1D), 4i = &HFFFFFFFFMsgBox pBytesInLong(1) '访问长整形变量的第2个字节(从低处开始数)pBytesInLong(3) = 0 '把全部数组元素设为0pBytesInLong(2) = 0pBytesInLong(1) = 0pBytesInLong(0) = 0MsgBox i '你会发现 i 也变成了 0'把数组变量(其实就是个指针)指向 0,既 C 语言中的 NULLCopyMemory ByVal VarPtrArray(pBytesInLong), 0&, 4End Sub   从代码中可以看到我们用一个字节数组借用了长整形变量i的地址空间,从而可以通过数组元素访问变量i的各个字节。这样也实现了拆分、组合整形变量的目的,和方法1殊途同归,但很显然方法2不需要函数调用,不需要数据复制,因此效率非常高。用这种方法,我专门构筑了一个模块:FastBitEx模块,实现了方法1中提及的6个函数的Fast版本,代码很长,不在这里给出,请读者参阅代码。   其中的参数Destination是目标内存的第一个字节地址,参数Source是被复制内存的第一个字节地址,参数Length是需要复制的字节数。  实现原理很简单:要实现拆分,就用CopyMemory函数把一个整形变量的一部分拷贝到另一个更小的整形变量的地址空间中;而实现合并,则利用CopyMemory函数把两个待合并的小变量拷贝到另一个大整形变量的地址空间中。见示例代码: Public Function Hi(ByVal Word As Integer) As Byte'取一个字(Word)的高字节(Byte)'INPUT-------------------------------------------'Word 字(Word)'OUTPUT------------------------------------------'返回值 Word参数的高字节'Last updated by Liu Qi 2004-3-20。Dim bytRet As ByteCopyMemory bytRet, ByVal VarPtr(Word) + 1, 1’把Word的高字节的内容烤入bytRet的地址处Hi = bytRet’返回结果End Function   根据数据类型的不同需要,笔者共设计了6个函数,HI()函数用来获得一个单字的高字节,LO()函数获得单字的低字节,HIWORD()函数获得双字的高位字,LOWORD()函数双字的低位字。CON()函数把两个字节组合成字,CONWORD()函数把两个字组合成双字。只要把这6个函数组合应用就可以随意的拆分、组合各种整型变量。例如前面提到的IP地址,IP地址是用一个DWORD类型变量存储的,在VB中则对应Long类型变量,假设一个IP地址存储在长整型变量中,就可以这样提取一个IP地址的最高字节:HI(HIWORD(lngIP))。  由于代码较长,故没有在文章中写出全部代码。

阅读(4281) | 评论(1)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册