VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)

VB爱好者乐园(VBGood)

 找回密码
 立即注册
搜索
查看: 6006|回复: 4

[经验技巧] 通过伪造数组在VB中使用"指针"

[复制链接]
 楼主| 发表于 2012-2-6 20:00:39 | 显示全部楼层 |阅读模式
如果你发现以下代码运行结果为FLASE,那么后面的内容全都不用看了,因为都是错的

  1. Dim s() As String
  2. ReDim s(1000)
  3. Dim i As Long
  4. For i = 0 To 1000
  5. s(i) = i
  6. Next
  7. Dim j As Long
  8. j = StrPtr(s(0))
  9. ReDim Preserve s(500)
  10. ReDim Preserve s(1000)
  11. Dim k As Long
  12. k = StrPtr(s(0))
  13. MsgBox j = k
复制代码
如果以上代码运行结果为TRUE,那么我们能得到一个结论:
string数组在用Preserve重定义大小时,不会拷贝整个字符串,他只是拷贝了一个指针
稍后我会讲解BSTR的结构,你就知道怎么回事了

如同标题说的,我们需要伪造一个数组来实现类似指针的功能
例如dim a() as long
赋予他一个地址后x,我们可以通过下标来访问对应位置的内容a(1)=5 '地址为x+4开始的4字节被修改
但是a()并不是一个真正的指针,中间还是有些区别
这样使用只是为了处理大片连续的数据不需要反复copymemory而已

要想伪造一个数组,就必须清楚数组真实的结构(这里特指动态数组)
vb的数组(例如dim a() as long),a存的其实是一个地址,而这个地址的位置存着一个SAFEARRAY结构

  1. Private Type SAFEARRAY
  2.     cDims As Integer         '这个数组有几维?
  3.     fFeature As Integer      '这个数组有什么特性?
  4.     cbElements As Long       '数组的每个元素有多大?
  5.     cLocks As Long           '这个数组被锁定过几次?
  6.     pvData As Long           '这个数组里的数据放在什么地方?
  7.     'rgsabound() As SFArrayBOUND
  8. End Type
复制代码
而SAFEARRAY的末尾有若干个SFArrayBOUND结构,视数组维度而定

  1. Private Type SFArrayBOUND
  2.     cElements As Long        '这一维有多少个元素?
  3.     lLbound As Long          '它的索引从几开始?
  4. End Type
复制代码
而真正指向数据缓冲区的是SAFEARRAY里的pvData (如图)

要伪造一个数组,就必须伪造SFArrayBOUND和SFArrayBOUND结构

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

点评

低级错误:FLASE  发表于 2012-2-7 10:34
平时没啥用,这个是我上次翻译C++代码用的,昨天发现有BUG,所以改了重新贴下...  发表于 2012-2-7 09:51
怎么现在你们都跟指针较上劲了?根据我的大量VB算法编程经验,使用一维静态数组+数组下标来山寨指针+开启所有高级优化,运行速度至少比模拟指针快4倍  发表于 2012-2-6 23:52

评分

参与人数 1人气 +1 收起 理由
xumaopeng123 + 1 赞一个!

查看全部评分

 楼主| 发表于 2012-2-6 20:01:02 | 显示全部楼层
本帖最后由 仙剑魔 于 2012-2-6 20:28 编辑

伪造数组并不难,难的是维护伪造出来的数据
直接声明dim temp as SAFEARRAY是不现实的,因为出了作用域就被释放了
HeapAlloc之类的固然可以,但是我不喜欢用太多API
于是就要用到开头的那个结论了
把数据存在字符串里,哈哈
vb的string其实存的也是个地址,地址位置的内容是一个BSTR结构,头部是4字节表示长度,后面跟着缓冲区

所以preserve的时候,VB没有去动BSTR的数据,而是直接拷贝了地址
代码如下:
使用方法

  1. Option Explicit

  2. Private Sub Form_Load()
  3. Dim a() As Long
  4. Dim b As Long

  5. Dim c() As Long
  6. Dim d As Long
  7. Call CallPtrProc(AddressOf SetPtrProc, a, VarPtr(b), LenB(b), &H7FFFFFFF)
  8. Call CallPtrProc(AddressOf SetPtrProc, c, VarPtr(d), LenB(d), &H7FFFFFFF)
  9. a(0) = 6

  10. Call CallPtrProc(AddressOf ClearPtrProc, a, 1)
  11. c(0) = 7
  12. Call CallPtrProc(AddressOf ClearPtrProc, c, 1)

  13. End Sub
复制代码
模块代码

  1. Option Explicit

  2. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
  3. Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (dest As Any, ByVal numBytes As Long)
  4. Public Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Var() As Any) As Long

  5. 'Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  6. Public Declare Function CallPtrProc Lib "user32" Alias "CallWindowProcA" (ByVal Func As Long, ByRef PPtr() As Any, Optional ByVal Param1 As Long = 0, Optional ByVal Param2 As Long = 0, Optional ByVal Param3 As Long = 0) As Long


  7. Private Type SAFEARRAY_KAI   'SAFEARRAY_改
  8.     cDims As Integer         '这个数组有几维?
  9.     fFeature As Integer      '这个数组有什么特性?
  10.     cbElements As Long       '数组的每个元素有多大?
  11.     cLocks As Long           '这个数组被锁定过几次?
  12.     pvData As Long           '这个数组里的数据放在什么地方?
  13.     'rgsabound() As SFArrayBOUND
  14.     cElements As Long        '这一维有多少个元素?
  15.     lLbound As Long          '它的索引从几开始?
  16.    
  17.     hHandle As Long
  18. End Type

  19. Private Buffer() As String
  20. Private Count As Long

  21. Public Sub SetPtrProc(ByVal PPtr As Long, _
  22.                       ByVal Address As Long, _
  23.              Optional ByVal ElementSize As Long = 4, _
  24.              Optional ByVal ElementCount As Long = &H7FFFFFFF)
  25.     Dim SA As SAFEARRAY_KAI
  26.     Count = Count + 1
  27.     With SA
  28.         .cDims = 1
  29.         .fFeature = 0
  30.         .cbElements = ElementSize
  31.         .cLocks = 0
  32.         .pvData = Address
  33.         .cElements = ElementCount
  34.         .lLbound = 0
  35.         .hHandle = Count
  36.     End With
  37.     ReDim Preserve Buffer(0 To Count)
  38.     Buffer(Count) = String(Len(SA) \ 2, 0)
  39.     Call CopyMemory(ByVal StrPtr(Buffer(Count)), SA, Len(SA))
  40.     Call CopyMemory(ByVal PPtr, StrPtr(Buffer(Count)), 4)
  41. End Sub

  42. Public Sub ClearPtrProc(ByVal PPtr As Long, _
  43.                Optional ByVal RemoveAll As Long = 0, _
  44.                Optional ByVal Reserve1 As Long = 0, _
  45.                Optional ByVal Reserve2 As Long = 0)
  46.     'Debug.Print StrPtr(Buffer(1))
  47.     Dim SA As SAFEARRAY_KAI
  48.     Dim PSA As Long
  49.     Dim i As Long
  50.     Call CopyMemory(PSA, ByVal PPtr, 4)
  51.     Call CopyMemory(SA, ByVal PSA, Len(SA))
  52.     i = SA.hHandle
  53.     Call CopyMemory(SA, ByVal StrPtr(Buffer(Count)), Len(SA))
  54.     SA.hHandle = i
  55.     Call CopyMemory(ByVal StrPtr(Buffer(Count)), SA, Len(SA))
  56.     Call SwapString(Buffer(i), Buffer(Count))
  57.     Count = Count - 1
  58.     Call ZeroMemory(ByVal PPtr, 4)
  59.    
  60.     If RemoveAll Then
  61.         'Dim SA As SAFEARRAY_KAI
  62.         'Dim i As Long
  63.         'Dim Offset As Long
  64.         'Offset = VarPtr(SA.hHandle) - VarPtr(SA)
  65.         'For i = Count To 1 Step -1
  66.         '    Call CopyMemory(SA, ByVal StrPtr(Buffer(i)), Len(SA))
  67.         '    If SA.hHandle = PPtr Then
  68.         '        Call SwapString(Buffer(i), Buffer(Count))
  69.         '        Count = Count - 1
  70.         '    End If
  71.         'Next
  72.         ReDim Preserve Buffer(0 To Count)
  73.     End If
  74. End Sub

  75. Public Sub IncPtrProc(ByVal PPtr As Long, _
  76.              Optional ByVal Reserve1 As Long = 0, _
  77.              Optional ByVal Reserve2 As Long = 0, _
  78.              Optional ByVal Reserve3 As Long = 0)
  79.     Dim SA As SAFEARRAY_KAI
  80.     Dim PSA As Long
  81.     Call CopyMemory(PSA, ByVal PPtr, 4)
  82.     Call CopyMemory(SA, ByVal PSA, Len(SA))
  83.     SA.pvData = SA.pvData + SA.cbElements
  84.     Call CopyMemory(ByVal PSA, SA, Len(SA))
  85. End Sub


  86. Private Sub SwapString(ByRef s1 As String, ByRef s2 As String)
  87.     Dim t As Long
  88.     Call CopyMemory(t, ByVal VarPtr(s1), 4)
  89.     Call CopyMemory(ByVal VarPtr(s1), ByVal VarPtr(s2), 4)
  90.     Call CopyMemory(ByVal VarPtr(s2), t, 4)
  91. End Sub
复制代码
整合一个新结构SAFEARRAY_KAI,所有的数据都以他为模板
每次申请新指针,buffer的下标count都+1
释放时交换释放位置和末尾的string(其实换的是指针)
至于为什么用CallWindowProc ?
好吧,那是为了用any绕过类型检查。。。
PS:
IncPtrProc是把指针往后移...
ElementCount 用&h7fffffff是为了避免VB的数组越界检查,当然你也可以为了防止指针越界自己定个大小

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

发表于 2012-2-6 20:50:57 | 显示全部楼层
我是这样子定义的 把他们绑一堆去
Public Type Plong
    pData() As long
    safearray As rawSAFEARRAY
End Type
直接 pData 指向 safearray
这样子safearray生命周期跟随pData,管理比较方便...

点评

刚看了你的代码,我们都是对safearray进行操作实现,你的看起来更好维护些。。。。你比我多想一步,变化起来更舒服。。。。  发表于 2012-2-6 20:55
也行,我这个主要是调用的时候比较丑。。。  发表于 2012-2-6 20:53
回复 支持 反对

使用道具 举报

发表于 2012-2-6 23:04:42 | 显示全部楼层
水很深,学习
回复 支持 反对

使用道具 举报

发表于 2012-2-8 09:16:55 | 显示全部楼层
我是这样构想的:

Private Declare Function VarPtrArray Lib "msvbvm60" Alias "VarPtr" (Ptr() As Any) As Long
Private Declare Sub MemCopy Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Public Pot0 As Integer, Pot1 As Integer, Pot2 As Long, Pot3 As Long, Pot As Long  'Pot用来指示目标地址
Public Pws As Long, Pot6 As Long, Pot7 As Long, Pot8 As Long, Pw() As Byte
'Pws用来设置需要多少个字节 Pw()用来覆盖目标内存
'使用的时候:Pot=Varptr(AnyAddress): Pws=xxx: Pw(Idx)=nnn

'====== 下面开始的2个函数 是做 指针变量 需要的 ================
Private Sub LinkPot2Pw() '构造一个指针变量 (Pot 和 Pw()) 配对
    ReDim Pw(0)
    MemCopy Pot7, VarPtrArray(Pw), 4&
    MemCopy Pot8, ByVal Pot7, 4&
    MemCopy Pot0, ByVal Pot8, 24&
    MemCopy ByVal Pot7, VarPtr(Pot0), 4&
End Sub

Private Sub FreePot2Pw() '释放 (Pot 和 Pw())
    MemCopy ByVal Pot7, Pot8, 4&
End Sub '====== 指针变量 结束 =================================

点评

这个有内存混乱的风险,某些语言可能叫做“对齐”  发表于 2012-2-8 18:27
构造其实你这样也是可以的,但是对开多个指针的支持就不是很好了...  发表于 2012-2-8 09:28
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

文字版|手机版|小黑屋|VBGood  

GMT+8, 2022-7-4 22:47

VB爱好者乐园(VBGood)
快速回复 返回顶部 返回列表