编辑
实际上,没有直接的方法来编辑内存中的范围值.感谢@AndASM的详细解答和Carl; 良好的预感,它是现货.在那一刻,我一定是纠结于所有逆转,忘记Value2
只是一个财产.
与此同时,我通过OllyDbg对其他一些测试和调试进行了深入研究,发现了一些有趣的事情:
单元布置在16 x 1024个区域中.持有这些区域的结构很可能是工作表,但我还不能确认;
每次Value
调用属性时,绝对工作表偏移(行,列)用于查找相应的区域,然后用于区域内的某些索引以获取实际值;
创建了VARIANT类型的2D SAFEARRAY;
不会在连续的块中检索值,而是单独检索.这意味着范围(row,col)中的每个"点"被发送到索引过程以返回其对应的SAFEARRAY元素的值(变体).
由于上述原因,每次检索
Range.Value2(row,col)
值时,都会对该范围内的所有值重复整个过程.想象一下,如果你在一个过程中多次这样做,或者更糟糕的是,在一个循环中,性能会受到影响.只是不要; 你最好Value2
通过索引创建一个副本并解决它;
最后,并非最不重要的是,内部值的分布
SAFEARRAY.pvData
是基于列的(col,row)
,而不是基于行的,这可能与直观相反,并且与VBA索引模式不一致,即(row,col)
.如果您需要直接在内存中访问pvData并保持维度一致性,这可能会派上用场.例如,下面的范围如下
1, 2, 3, 4 5, 6, 7, 8
将按pvData
以下顺序存储:
1, 5, 2, 6, 3, 7, 4, 8
我希望以上有所帮助.总而言之,在Excel中没有任何此类导出函数时,最好的方法是创建副本Value2
,对其进行排序/操作以获得所需结果并将其分配回范围属性.
我最近完成了QuickSort的变体,并打算在Excel中实现.该算法是有效的,并且如果不是将数组值放入范围所花费的额外时间,那么它将真正带来作为加载项的价值.转置仅适用于小于65537,而'粘贴变体阵列到范围在大型排序上需要太长时间.
因此,我编写了一些程序,允许将范围内的2D值复制到一维数组(排序需要1D)和(排序完成后)将它们放回去,所有这些都基于SAFEARRAY和MemCopy(RtlMoveMemory)和,另外,WriteProcessMemory.
就内存操作而言,一切正常: - 将范围值复制到数组(从一个SafeArray.pvData到另一个); - 数组值(运行排序算法后)成功复制到Range.Value2 SafeArray.pvData.
尽管如此,范围并没有更新,因为它似乎又回到了旧的值(更多关于下面的代码).为什么"Range.Value2 = SomeOther2dArray"工作而不直接在内存中修改数据?我有一种感觉,我在这里遗漏了一些东西.是否还需要公式排序/更新?
这是主要程序:
Public Sub XLSORT_Array2() With Application screenUpdateState = .ScreenUpdating statusBarState = .DisplayStatusBar calcState = .Calculation eventsState = .EnableEvents .ScreenUpdating = False .DisplayStatusBar = False .Calculation = xlCalculationManual .EnableEvents = False End With Dim rngSort As Range Dim arrSort() As Variant Dim arrTemp As Variant Dim i As Long Dim dblTime As Double Dim dblInitTime As Double: dblInitTime = Timer Set rngSort = Selection If Not rngSort Is Nothing Then If rngSort.Cells.Count > 1 And rngSort.Areas.Count = 1 Then dblTime = Timer ReDim arrSort(1 To rngSort.Cells.Count) Debug.Print Timer - dblTime & vbTab & "(Redim)" 'just testing Excel memory location 'Debug.Print VarPtr(rngSort.Value2(1, 1)) dblTime = Timer SA_Duplicate arrSort, rngSort.Value2 Debug.Print Timer - dblTime & vbTab & "(Copy)" dblTime = Timer SORTVAR_QSWrapper arrSort, 1, rngSort.Cells.Count Debug.Print Timer - dblTime & vbTab & "(Sort)" 'this would be the fastest method 'variants are copied to memory 'yet the range does not update with the new values SA_Duplicate rngSort.Value2, arrSort 'dblTime = Timer 'looping = too slow 'For i = 1 To rngSort.Cells.Count ' rngSort.Cells(i).Value = arrSort(i) 'Next 'this works, but it's too slow, as well 'If rngSort.Cells.Count > 65536 Then ' ReDim arrTemp(LBound(rngSort.Value2, 1) To UBound(rngSort.Value2, 1), LBound(rngSort.Value2, 2) To UBound(rngSort.Value2, 2)) ' SA_Duplicate arrTemp, arrSort ' rngSort.Value2 = arrTemp 'Else ' rngSort.Value2 = WorksheetFunction.Transpose(arrSort) ' Debug.Print "Transposed" 'End If 'Debug.Print Timer - dblTime & vbTab & "(Paste)" End If End If With Application .ScreenUpdating = screenUpdateState .DisplayStatusBar = statusBarState .Calculation = calcState .EnableEvents = eventsState End With Debug.Print VarPtr(rngSort.Value2(1, 1)) & vbTab & Mem_ReadHex(ByVal VarPtr(rngSort.Value2(1, 1)), rngSort.Cells.Count * 16) Set rngSort = Nothing Debug.Print Timer - dblInitTime & vbTab & "(Total Time)" & vbNewLine End Sub
假设范围内的值为4,3,2和1.在SA_Duplicate arrSort, rngSort.Value2
内存读取之前:
130836704 05000000 00000000 00000000 00001040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00000040 05000000 00000000 00000000 0000F03F 129997032 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
这里130836704
是Range.Value2 SafeArray.pvData
和 129997032
是SortArray SafeArray.pvData
.每个16字节批处理表示变量实际数据,从内存中读取(无LE转换,仅为十六进制),前2个字节表示VarType.在这种情况下,vbDouble.
复制之后,正如预期的那样,内存读取:
130836704 05000000 00000000 00000000 00001040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00000040 05000000 00000000 00000000 0000F03F 129997032 05000000 00000000 00000000 00001040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00000040 05000000 00000000 00000000 0000F03F
排序完成后,SortArray SafeArray.pvData读取:
129997032 05000000 00000000 00000000 0000F03F 05000000 00000000 00000000 00000040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00001040
执行后SA_Duplicate rngSort.Value2, arrSort
,内存显示Range.Value2 SafeArray.pvData已更新:
129997032 05000000 00000000 00000000 0000F03F 05000000 00000000 00000000 00000040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00001040 130836704 05000000 00000000 00000000 0000F03F 05000000 00000000 00000000 00000040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00001040
到目前为止看起来都很好,除了Debug.Print VarPtr(rngSort.Value2(1, 1)) & vbTab & Mem_ReadHex[...]
显示值翻转回初始顺序:
130836704 05000000 00000000 00000000 00001040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00000040 05000000 00000000 00000000 0000F03F
请分享您认为有效的任何想法或方法.任何帮助表示赞赏.令人沮丧的是,等待Excel大约4秒(排序1,000,000 +单元格),即使是最具挑战性的排序也要少于此.
提前致谢!