如何使用rtlmovememory和VB6 / VBA操作变量数组中的数据

时间:2017-06-04 13:35:04

标签: arrays vba vb6 kernel32

我编写了一个VB-Class来提供数据表,它允许您处理类似于ADO记录集的二维数组(类型变体)中的数据。

为了提高处理速度,在向数组中添加记录,对数组进行排序(即在数组中移动记录)或从数组中读取记录时,我想使用Windows API的kernel32.dll的RtlMoveMemory例程。虽然我已经能够成功地在数组中移动记录,但是将数据写入数组的特定索引或从数组的特定索引读取数据似乎在某种程度上混合了进一步处理的数据。

我已经做了很多阅读以获得以下内容:

  1. http://www.codeguru.com/vb/gen/vb_misc/algorithms/article.php/c7495/How-Visual-Basic-6-Stores-Data.htm
  2. Copy an array reference in VBA
  3. How do I slice an array in Excel VBA?
  4. 正如您将看到的,我的代码是链接号的改编。 3以上。我不是一个真正的专业人士,但我不是一个绝对的初学者,我必须错过一些东西,但我无法弄清楚是什么。

    以下是今天的代码:

    Option Explicit
    Option Base 1
    
    '#======================================================================================================================
    '# References
    '#======================================================================================================================
    #If Win64 Then
        Private Const PTR_LENGTH As Long = 8
        Private Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias "VarPtr" (ByRef Var() As Any) As LongPtr
        Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    #Else
        Private Const PTR_LENGTH As Long = 4
        Private Declare Function VarPtrArray Lib "VBE7" Alias "VarPtr" (ByRef Var() As Any) As LongPtr
        Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    #End If
    
    
    '#======================================================================================================================
    '# API Constants, Enumerations & Types
    '#======================================================================================================================
    
    'Type Declarations needed for SafeArray hacks
    
    'The bounds of the SafeArray
    Private Type SAFEARRAYBOUND
        cElements    As Long
        lLbound      As Long
    End Type
    
    Private Type SAFEARRAY1D
        cDims           As Integer
        fFeatures       As Integer
        cbElements      As Long
        cLocks          As Long
        pvData          As Long
        Bounds(0 To 0)  As SAFEARRAYBOUND
    End Type
    
    Private Type SAFEARRAY2D
        cDims           As Integer
        fFeatures       As Integer
        cbElements      As Long
        cLocks          As Long
        pvData          As Long
        Bounds(0 To 1)  As SAFEARRAYBOUND
    End Type
    
    
    '#======================================================================================================================
    '# Private Variables
    '#======================================================================================================================
    Private m_List() As Variant                     ' The list array.
    
    
    '#======================================================================================================================
    '# Test Routines
    '#======================================================================================================================
    
    Private Sub MainTest()
    
        Dim iIdx As Long
        Dim aSingleRec() As Variant
        Dim i As Long
    
        LoadRange ActiveSheet.Range("dataInput")
    
    
        DataRowMove 5, 2
    
        DebugRecord 5
    
        DebugRecord 2
    
    
        ReDim aSingleRec(LBound(m_List, 1) To UBound(m_List, 1))
    
        aSingleRec(1) = "Test Gender (m)"
        aSingleRec(2) = "Steve"
        aSingleRec(3) = "Rogers"
        aSingleRec(4) = "425 Lafayette St"
        aSingleRec(5) = 10003
        aSingleRec(6) = "New York"
    
        DataRowPush 4, aSingleRec
    
        DebugRecord 4
        DebugSingleRecord aSingleRec
    
    
        aSingleRec(1) = "Test Gener (f)"
        aSingleRec(2) = "Wanda"
        aSingleRec(3) = "Maximoff"
        aSingleRec(4) = "72 W 36th St"
        aSingleRec(5) = 10018
        aSingleRec(6) = "New York"
    
        DataRowPush 6, aSingleRec
    
        DebugRecord 6
        DebugSingleRecord aSingleRec
    
        aSingleRec = DataRowGet(7)
    
        DebugSingleRecord aSingleRec
    
        DumpToRange ActiveSheet, ActiveSheet.Cells(10, 2)
    
        Debug.Print "Done..."
    
    
    End Sub
    
    
    
    Private Sub LoadRange(rInput As Range)
    
        m_List = rInput
    
    
    End Sub
    
    
    Private Sub DumpToRange(TargetWorksheet As Worksheet, TargetCell As Range)
    
        Dim iRow As Integer: iRow = TargetCell.Row
        Dim iCol As Integer: iCol = TargetCell.Column
    
        TargetWorksheet.Cells(iRow, iCol).Resize(UBound(m_List), UBound(m_List, 2)) = m_List
    
    
    End Sub
    
    
    Private Sub DebugRecord(iIdx As Long, Optional stInProcedure = "Main")
    
        Dim i As Long
    
        Debug.Print "---------------------------"
        Debug.Print "Record " & iIdx & " (in Procedure '" & stInProcedure & "')" & vbCrLf
    
        For i = 1 To UBound(m_List, 1)
            Debug.Print vbTab & "Field " & i & "[" & TypeName(m_List(i, iIdx)) & "] -> " & m_List(i, iIdx)
        Next i
    
        Debug.Print vbCrLf
    
    
    End Sub
    
    
    Private Sub DebugSingleRecord(aRec() As Variant)
    
        Dim i As Long
    
        Debug.Print "---------------------------"
        Debug.Print "Single Record " & vbCrLf
    
        For i = 1 To UBound(aRec)
            Debug.Print vbTab & "Field " & i & "[" & TypeName(aRec(i)) & "] -> " & aRec(i)
        Next i
    
        Debug.Print vbCrLf
    
    
    
    End Sub
    
    
    '#======================================================================================================================
    '# Data Handling Routines
    '#======================================================================================================================
    
    Private Function DataRowGet(ByVal idxFrom As Long) As Variant()
    
        Dim ptrToArrayVar As LongPtr
        Dim ptrToSafeArray As LongPtr
        Dim ptrToArrayData As LongPtr
        Dim ptrToArrayData2 As LongPtr
        Dim uSAFEARRAY As SAFEARRAY1D
        Dim ptrCursor As LongPtr
        Dim cbElements As Long
        Dim atsBound1 As Long
        Dim elSize As Long
    
        Dim aSingleRec() As Variant
    
        Dim m_NumCols As Long
    
    
        m_NumCols = UBound(m_List, 1)
    
        ReDim aSingleRec(LBound(m_List, 1) To UBound(m_List, 1))
    
        'determine bound1 of source array (ie row Count)
        atsBound1 = m_NumCols
    
        'get pointer to source array Safearray
        ptrToArrayVar = VarPtrArray(m_List)
        CopyMemory ptrToSafeArray, ByVal ptrToArrayVar, PTR_LENGTH
        'get the safearray structure
        CopyMemory uSAFEARRAY, ByVal ptrToSafeArray, LenB(uSAFEARRAY)
        'get the pointer to the data elemets
        ptrToArrayData = uSAFEARRAY.pvData
        'determine byte size of source elements
        cbElements = uSAFEARRAY.cbElements
    
        'get pointer to destination array Safearray
        ptrToArrayVar = VarPtrArray(aSingleRec)
        CopyMemory ptrToSafeArray, ByVal ptrToArrayVar, PTR_LENGTH
        CopyMemory uSAFEARRAY, ByVal ptrToSafeArray, LenB(uSAFEARRAY)
        ptrToArrayData2 = uSAFEARRAY.pvData
    
        'determine elements size
        elSize = m_NumCols
        'determine start position of data in source array
        ptrCursor = ptrToArrayData + (((idxFrom - 1) * atsBound1) * cbElements)
        'Copy source array to destination array
        CopyMemory ByVal ptrToArrayData2, ByVal ptrCursor, cbElements * elSize
    
    
        DataRowGet = aSingleRec
    
        ReDim aSingleRec(0 To 0)
    
    
    End Function
    
    
    Private Sub DataRowPush(ByVal idxTo As Long, ByRef sourceArray() As Variant)
    
        Dim ptrToArrayVar As LongPtr
        Dim ptrToSafeArray As LongPtr
        Dim ptrToArrayData As LongPtr
        Dim ptrToArrayData2 As LongPtr
        Dim uSAFEARRAY As SAFEARRAY1D
        Dim ptrCursor As LongPtr
        Dim ptrCursorSource As LongPtr
        Dim cbElementsS As Long
        Dim cbElementsT As Long
        Dim atsBound1 As Long
        Dim elSize As Long
        Dim m_NumCols As Long
    
        Dim aSingleRec() As Variant
    
        aSingleRec = sourceArray
    
        m_NumCols = UBound(m_List, 1)
    
        'determine bound1 of source array (ie row Count)
        atsBound1 = m_NumCols
    
        'get pointer to target array Safearray
        ptrToArrayVar = VarPtrArray(m_List)
        CopyMemory ptrToSafeArray, ByVal ptrToArrayVar, PTR_LENGTH
        'get the safearray structure
        CopyMemory uSAFEARRAY, ByVal ptrToSafeArray, LenB(uSAFEARRAY)
        'get the pointer to the data elemets
        ptrToArrayData = uSAFEARRAY.pvData
        'determine byte size of source elements
        cbElementsT = uSAFEARRAY.cbElements
    
    
    
        'get pointer to source array Safearray
        ptrToArrayVar = VarPtrArray(aSingleRec)
        CopyMemory ptrToSafeArray, ByVal ptrToArrayVar, PTR_LENGTH
        'get the safearray structure
        CopyMemory uSAFEARRAY, ByVal ptrToSafeArray, LenB(uSAFEARRAY)
        'get the pointer to the data elemets
        ptrToArrayData2 = uSAFEARRAY.pvData
        'determine byte size of source elements
        cbElementsS = uSAFEARRAY.cbElements
    
        'determine elements size
        elSize = m_NumCols
        'determine start position of data in target array
        ptrCursor = ptrToArrayData + (((idxTo - 1) * atsBound1) * cbElementsT)
        'Copy source array to destination array
        CopyMemory ByVal ptrCursor, ByVal ptrToArrayData2, cbElementsS * elSize
    
        'Debugging only
        DebugRecord idxTo, "DataRowPush"
    
    End Sub
    
    
    Private Sub DataRowMove(ByVal idxFrom As Long, ByVal idxTo As Long)
    
        Dim ptrToArrayVar As LongPtr
        Dim ptrToSafeArray As LongPtr
        Dim ptrToArrayData As LongPtr
        Dim ptrToArrayData2 As LongPtr
        Dim uSAFEARRAY As SAFEARRAY1D
        Dim ptrCursorFrom As LongPtr
        Dim ptrCursorTo As LongPtr
        Dim cbElements As Long
        Dim atsBound1 As Long
        Dim elSize As Long
        Dim m_NumCols As Long
    
        m_NumCols = UBound(m_List, 1)
    
        'determine bound1 of source array (ie row Count)
        atsBound1 = m_NumCols
    
        'get pointer to source array Safearray
        ptrToArrayVar = VarPtrArray(m_List)
        CopyMemory ptrToSafeArray, ByVal ptrToArrayVar, PTR_LENGTH
    
        'get the safearray structure
        CopyMemory uSAFEARRAY, ByVal ptrToSafeArray, LenB(uSAFEARRAY)
    
        'get the pointer to the data elemets
        ptrToArrayData = uSAFEARRAY.pvData
    
        'determine byte size of source elements
        cbElements = uSAFEARRAY.cbElements
    
        'determine elements size
        elSize = m_NumCols
    
        'determine start position of  data source in array
        ptrCursorFrom = ptrToArrayData + (((idxFrom - 1) * atsBound1) * cbElements)
    
        'determine start position of data target in array
        ptrCursorTo = ptrToArrayData + (((idxTo - 1) * atsBound1) * cbElements)
    
        'Copy source array to destination array
        CopyMemory ByVal ptrCursorTo, ByVal ptrCursorFrom, cbElements * elSize
    
    End Sub
    

    这导致以下输出:

    ---------------------------
    Record 5 (in Procedure 'Main')
    
      Field 1[String] -> Mr
      Field 2[String] -> Peter
      Field 3[String] -> Parker
      Field 4[String] -> 401 7th Ave
      Field 5[Double] -> 10001
      Field 6[String] -> New York
    
    
    ---------------------------
    Record 2 (in Procedure 'Main')
    
      Field 1[String] -> Mr
      Field 2[String] -> Peter
      Field 3[String] -> Parker
      Field 4[String] -> 401 7th Ave
      Field 5[Double] -> 10001
      Field 6[String] -> New York
    
    
    ---------------------------
    Record 4 (in Procedure 'DataRowPush')
    
      Field 1[String] -> Test Gender (m)
      Field 2[String] -> Steve
      Field 3[String] -> Rogers
      Field 4[String] -> 425 Lafayette St
      Field 5[Integer] -> 10003
      Field 6[String] -> New York
    
    
    ---------------------------
    Record 4 (in Procedure 'Main')
    
      Field 1[String] -> Test Gender (m)
      Field 2[String] -> Steve
      Field 3[String] ->  Field 
      Field 4[String] ->  Field 4[String
      Field 5[Integer] -> 10003
      Field 6[String] -> New York
    
    
    ---------------------------
    Single Record 
    
      Field 1[String] -> Test Gender (m)
      Field 2[String] -> Steve
      Field 3[String] -> Rogers
      Field 4[String] -> 425 Lafayette St
      Field 5[Integer] -> 10003
      Field 6[String] -> New York
    
    
    ---------------------------
    Record 6 (in Procedure 'DataRowPush')
    
      Field 1[String] -> Test Gener (f)
      Field 2[String] -> Wanda
      Field 3[String] -> Maximoff
      Field 4[String] -> 72 W 36th St
      Field 5[Integer] -> 10018
      Field 6[String] -> New York
    
    
    ---------------------------
    Record 6 (in Procedure 'Main')
    
      Field 1[String] -> Test Gener (f)
      Field 2[String] ->  Field 
      Field 3[String] -> Maximoff
      Field 4[String] -> 72 W 36th St
      Field 5[Integer] -> 10018
      Field 6[String] -> New York
    
    
    ---------------------------
    Single Record 
    
      Field 1[String] -> Test Gener (f)
      Field 2[String] -> Wanda
      Field 3[String] -> Maximoff
      Field 4[String] -> 72 W 36th St
      Field 5[Integer] -> 10018
      Field 6[String] -> New York
    
    
    ---------------------------
    Single Record 
    
      Field 1[String] -> Mr
      Field 2[String] -> Bruce 
      Field 3[String] -> Banner
      Field 4[String] -> 222 W 51st St
      Field 5[Double] -> 10019
      Field 6[String] -> New York
    
    
    Done...
    

    我的主要问题是'DataRowPush',它在程序本身内似乎工作正常但是一旦程序返回到调用过程,目标数组的内容似乎就会改变。您可以在上面的记录4和6的调试输出中看到这一点。

    类似地,当使用'DataRowGet'读取数据时,目标单维数组被正确填充,但似乎m_List(二维数组)中的原始数据似乎也被改变。用'DataRowGet'读取数据后记录没有。 7读作

    Mr
    Resize
    6
    Field 6
    10019
    Field 6[
    

    在m_List中。感谢任何帮助如何更改我的代码以防止更改数据。

0 个答案:

没有答案