指针是c语言的概念,VB、VB.NET没有指针,除了指针以外也很少有人能玩到你的编程深度,关键看不懂呢。
成都创新互联公司于2013年成立,先为镇原等服务建站,镇原等地企业,进行企业商务咨询服务。为镇原企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
???就是子程序的名称,比如每次点击c后就自动调动相对应的子程序,如子程序是
Private Sub my_do ()
........
End Sub
相应的AddHandler c.Click, AddressOf my_do
实际上是个委托
调用函数指针,在Delphi,VC及汇编中,是非常简单的事情,所以,如果你不是VB程序员,请不要浪费你的时间,不要再看这篇文章了。。
在VB中就不能直接调用了,VB唯一与之有点关系的就是AddressOf 操作符,所有VB程序都知道,它能取得模块内的函数地址,看到这里,相信很多VB程序员马上就说,调用函数指针,用 CallWindowProc 不就可以啦,还有什么可以讲的,呵呵,不错,你掌握了这个技巧!是的,这确实是利用这个API可以调用函数指针,不过呢,它只支持4个参数,给你的应用带来很多不便。又有程序员要讲了,四个参数我可以扩展到任意功能呀,比如将其中一个参数传入的是一个结构块的指针,在结构中我可以随意的定义数据呀。鼓掌!鼓掌!鼓掌!很不错!是的,你完全可以这样做。不过呢,如果有一个函数不是你写的呢,你了解这个函数的所有参数及意义,也得到了它的指针,你怎么调用呀?惨了!呵呵。不过不被困难吓倒的你可能又说,呵呵,有了,在《高级VB编程》这本被称为VB程序员的圣经的书中有解决方案。鼓掌!鼓掌!鼓掌!不过,太累了!太累了,我对这本书的评价就是,它确实是一本高深,很值得读的书。不过,毕竟作者的宗旨是基于COM为出发点,再来解决问题的。所以难免很多事情复杂化了,(题外话,很多朋友同我讲,说这本书很难看懂,呵呵,如果你研究一下COM,你就很容易读懂这本书了!)
下面,我所提供的方法就是,利用嵌入一段汇编代码,借助 CallWindowProc 函数实现调用任意个数的参数(当然参数类型也任意啦)的函数指针的能力。
有关CallWindowProc 调用函数指针,在这我就不重复介绍了,毕竟掌握的人很多,网上资料也一大把的。我就省了它吧
Private Declare Function CallAsmCode Lib "user32" Alias "CallWindowProcA" (lpPrevWndFunc As Long, _ ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, lParam As Long) As Long
'------------------------------------------------------
' 功能:借用API调用任意个数参数的函数的纯VB实现版.
' 此函数内部自带SEH错误处理机制,但并不保证任意
' 错误调用均不会让您的VB崩溃(比如破坏栈平衡后返回)
' 作者:阿国哥 hackor@yeah.net
'------------------------------------------------------
Private Function CallAnyFunc(ByVal pFn As Long, ByVal pParam As Long, ByVal Count As Long) As Long
Dim CallAnyFuncCode(34) As Long, lRet As Long
CallAnyFuncCode(0) = H53EC8B55
CallAnyFuncCode(1) = HE8
CallAnyFuncCode(2) = HEB815B00
CallAnyFuncCode(3) = H1000112C
CallAnyFuncCode(4) = H114A938D
CallAnyFuncCode(5) = H64521000
CallAnyFuncCode(6) = H35FF
CallAnyFuncCode(7) = H89640000
CallAnyFuncCode(8) = H25
CallAnyFuncCode(9) = H8B1FEB00
CallAnyFuncCode(10) = HE80C2444
CallAnyFuncCode(11) = H0
CallAnyFuncCode(12) = H53E98159
CallAnyFuncCode(13) = H8D100011
CallAnyFuncCode(14) = H119791
CallAnyFuncCode(15) = HB8908910
CallAnyFuncCode(16) = H33000000
CallAnyFuncCode(17) = H558BC3C0
CallAnyFuncCode(18) = H104D8B0C
CallAnyFuncCode(19) = HEB8A148D
CallAnyFuncCode(20) = HFC528D06 CallAnyFuncCode(21) = HB4932FF
CallAnyFuncCode(22) = H8BF675C9
CallAnyFuncCode(23) = HD0FF0845
CallAnyFuncCode(24) = H58F64
CallAnyFuncCode(25) = H83000000
CallAnyFuncCode(26) = H4D8B04C4
CallAnyFuncCode(27) = H5B018914
CallAnyFuncCode(28) = H10C2C9
CallAnyFuncCode(29) = H58F64
CallAnyFuncCode(30) = H83000000
CallAnyFuncCode(31) = HC03304C4
CallAnyFuncCode(32) = H89144D8B
CallAnyFuncCode(33) = HC2C95B21
CallAnyFuncCode(34) = H90900010
CallAnyFunc = CallAsmCode(CallAnyFuncCode(0), pFn, pParam, Count, lRet)
If CallAnyFunc lRet Then
CallAnyFunc = 0 '这里表示出现严重错误,你应当再了解目的函数的使用方法
Debug.Assert False '因为你的参数传递问题,导致程序已出现了非法操作。
End If
End Function
下面介绍一下如何使用这个函数
参数一(pFn ):函数指针
参数二(pParam ):参数指针,指向一个连续的内存块,比如目的函数有三个参数,分别为A,B,C。你可以定义一个结构,结构体为A,B,C(每个参数均为4字节长),然后传这个结构的地址。
参数三(Count ):参数个数。
返回:目的函数的返回值。(你可以修改成其它类型的返回值)
数组好像没有快速赋值吧,或者你再把问题再清楚一点。
语句执行时间倒是可以做到。在语句前
Dim oldTime As Date = Now
在过程语句后加
Dim newTime As Date = Now
Dim differenceInSeconds As Long = DateDiff(DateInterval.Second, oldTime, newTime)
textbox1.text= "共用了:" differenceInSeconds "秒!"
例1:以下程序我们申请几个指向不同类型的指针:
’使用StructLayout(LayoutKind.Sequential)属性告诉net编译器:结构的元素在内存中按其出现的顺序排列
StructLayout(LayoutKind.Sequential) _
Public Structure DEFUDT_Test
Public bytb As Byte
Public i32a As Int32
End Structure
Public Function fnGetIntptr1() As IntPtr
’取得一个4字节数组指针
Dim tabytTest(3) As Byte
’以下语句告诉net垃圾回收进程不对tabytTest进行处理,也就是说tabytTest占用的内存区域固定不变。
Dim thObject As GCHandle = GCHandle.Alloc(tabytTest, GCHandleType.Pinned)
Dim tpObject As IntPtr = thObject.AddrOfPinnedObject() ’取得指向字节数组的指针
’取得一个指向32位内存数据的指针,
’由于使用gchandle取指针的方法只能对引用的对象有效,
’所以对如int32等值类型必须使用将其封装成为一个对象的方法以变为引用类型
Dim ti32Test As Object = Convert.ToInt32(0)
’以下语句告诉net垃圾回收进程不对ti32test进行处理,也就是说ti32Test的内存位置固定不变。
Dim thObject1 As GCHandle = GCHandle.Alloc(ti32Test, GCHandleType.Pinned)
Dim tpObject1 As IntPtr = thObject1.AddrOfPinnedObject() ’取得ti32Test的首地址
Dim tudtTest1 As DEFUDT_Test
’由于结构是一种值类型变量,为保证指针申请方便,我们申请
’取得一个和结构tudtTest1大小一致的字节数组指针,只要空间占用长度和结构一样就可以了
’由于net在结构封装中会插入额外的数据位,所以一定要用sizeof方法得到结构在非托管使用时的实际大小
Dim tudtTest(Marshal.SizeOf(tudtTest1)) As Byte
Dim thObject2 As GCHandle = GCHandle.Alloc(tudtTest, GCHandleType.Pinned)
Dim tpObject2 As IntPtr = thObject2.AddrOfPinnedObject() ’取得指向结构的指针
’在这儿你可以写对指针处理的任意代码(在例2中会给予补充)……
’在使用完毕后一定要释放指针指向的内存块,让垃圾回收器可对这个内存块回收处理
If thObject.IsAllocated Then
thObject.Free()
End If
If thObject1.IsAllocated Then
thObject1.Free()
End If
If thObject2.IsAllocated Then
thObject2.Free()
End If
End Function
上例中指针流程处理可以归纳为:
1、 定义一个具有合适内存长度的引用变量(关于引用变量和值变量的差异可以参观VB.NET的书籍)
2、使用GCHandle.Alloc方法将变量的内存区域固定下来。
3、使用GCHandle对象的AddrOfPinnedObject取得该内存区域的首地址并赋值给指针变量.
4、对指针进行操作
5、使用GCHandle对象的free方法释放指针指向的内存区域以便net垃圾回收器可以回收这个内存空间
2、指针所指向数据的存取
在.net中,对指针指向数据的存储函数都封装在marshal类中,主要的函数包括:Copy、PtrToStringUni 、PtrToStructure 、OffsetOf、WriteXXX,RreadXXX等,其中WriteXXX的表示向指针所表示的地址中写入XXX类型的数据,而ReadXXX中作用就是将指针所在地址的数据以XXX类型方式读出。看例程2,我们使用这些方法演示对例1那几个指向不同类型数据的指针作数据存/取操作。
例2:演示向例1申请得到的几个指针执行写入及读取数据的操作.
Marshal.WriteInt32(tpObject1, 0, Convert.ToInt32(77)) ’向ti32Test变量指向的地址写入32位整数77
MsgBox("现在ti32Test的值为:" ti32Test) ’因为变量存储地址的数据已改为77,所以显示为77
’以下这句之所以可行,因为ti32Test是32位整数,而tpObject指向的tabytTest数组刚好有4个元素
’而每一个byte元素都占用8位,合起来就是32位,和ti32Test占用的空间一样。这就印证了前面提’
’到的net中指针没有指向类型的说明。
Marshal.WriteInt32(tpObject, 0, ti32Test)
’以下代码再将tabytTest字节数组的内容理解为一个int32整数,
’并将值赋值给tudtTest结构中的int32元素
’我们使用Marshal.OffsetOf(GetType(DEFUDT_Test), "i32a").ToInt32以取得i32a元素在结构中的内存偏移位置
’所以New IntPtr(tpObject2.ToInt32 + Marshal.OffsetOf(GetType(DEFUDT_Test), "i32a").ToInt32)就临时产生了
’一个指针并指向i32a所在的内存地址(, 这个方法也说明了指针可以以字节为单位进行加减计算以指向合适的变量。
’Marshal.ReadInt32的作用是从指针中读取一个32整数。
Marshal.WriteInt32(New IntPtr(tpObject2.ToInt32 + Marshal.OffsetOf(GetType(DEFUDT_Test), "i32a").ToInt32), _
0, Marshal.ReadInt32(tpObject))
’这儿可以将字节数组的内容复制到真正的结构中
MsgBox(Marshal.OffsetOf(tudtTest1.GetType, "i32a").ToInt32)
tudtTest1 = CType(Marshal.PtrToStructure(tpObject2, GetType(DEFUDT_Test)), DEFUDT_Test)
MsgBox("结构tidtTest1中i32a元素的值为:" tudtTest1.i32a) ’此处将显示刚赋的值77
当然可以的,需要System.Runtime.InteropServices 命名空间中的 Marshal 类
Imports System.Runtime.InteropServices '这里一定要有
Public Class Form1
Public Structure m_Point
Dim x As Integer
Dim y As Integer
End Structure
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer = 50
Dim ai() As Integer = {1, 2, 3, 4, 5}
Dim pi As IntPtr = GCHandle.Alloc(i, GCHandleType.Pinned).AddrOfPinnedObject() '取得整形变量的指针
Dim pai As IntPtr = GCHandle.Alloc(ai, GCHandleType.Pinned).AddrOfPinnedObject() '取得整形数组首地址指针
MsgBox(Marshal.ReadInt32(pi, 0)) '读回整形变量指针指向的值
MsgBox(Marshal.ReadInt32(pai, 0 * 4)) '读回数组的第一个元素
MsgBox(Marshal.ReadInt32(pai, 1 * 4)) '读回数组的第二个元素
MsgBox(Marshal.ReadInt32(pai, 2 * 4)) '读回数组的第三个元素
'-----下面是结构--------------------------
Dim m_p As New m_Point
m_p.x = 100
m_p.y = 50
Dim pm_p As IntPtr = GCHandle.Alloc(m_p, GCHandleType.Pinned).AddrOfPinnedObject() '取得结构首地址指针
MsgBox(Marshal.ReadInt32(pm_p, 0 * 4)) '读回结构的第一个值
MsgBox(Marshal.ReadInt32(pm_p, 1 * 4)) '读回结构的第二个值
End Sub
End Class