Thật vậy, không có cách nào trực tiếp chỉnh sửa các giá trị phạm vi trong bộ nhớ. Cảm ơn @AndASM về câu trả lời chi tiết và Carl; tốt linh cảm, nó đã được tại chỗ. Tôi hẳn đã quá rối trong tất cả sự đảo chiều tại thời điểm đó, quên rằng Value2
chỉ là một tài sản.
Trong khi đó, tôi nghiên cứu sâu hơn một chút với một số xét nghiệm khác và gỡ lỗi với OllyDbg và tìm thấy một vài điều thú vị:
- Các tế bào được sắp xếp theo 16 x 1024 khu vực này. Cấu trúc giữ các khu vực có thể rất tốt là trang tính, nhưng tôi chưa thể xác nhận;
- Mỗi khi thuộc tính
Value
được gọi, bảng tuyệt đối khoảng cách (hàng, cột) được sử dụng để tìm khu vực tương ứng và sau đó để lập chỉ mục bên trong khu vực để nhận giá trị thực; - Một SAFEARRAY 2D của loại VARIANT được tạo;
- Giá trị không được truy lục trong một khối liền kề, nhưng riêng lẻ. Điều này có nghĩa là mỗi "điểm" trong một phạm vi (hàng, col) được gửi đến thủ tục lập chỉ mục để trả lại giá trị (biến thể, hiển nhiên) cho phần tử SAFEARRAY tương ứng của nó;
- Do đó, mỗi khi bạn truy lục giá trị qua
Range.Value2(row,col)
, toàn bộ quá trình được nhắc lại cho tất cả các giá trị trong phạm vi. Hãy tưởng tượng hit hiệu suất nếu bạn làm điều này nhiều lần trong một thủ tục hoặc thậm chí tệ hơn, bên trong một vòng lặp. Chỉ cần không; bạn nên tạo một bản sao củaValue2
và giải quyết nó bằng cách lập chỉ mục; cuối, nhưng không kém phần quan, sự phân bố của các giá trị bên trong
SAFEARRAY.pvData
là cột trụ sở(col,row)
, không chèo dựa trên, mà có thể được tìm thấy phản trực giác và mâu thuẫn với chế độ VBA indexing , đó là(row,col)
. Điều này có thể hữu ích nếu bạn cần truy cập pvData trực tiếp trong bộ nhớ và duy trì tính nhất quán về kích thước. Như một ví dụ, một loạt như hình dưới đây1, 2, 3, 4 5, 6, 7, 8
sẽ được lưu trữ trong
pvData
theo thứ tự sau đây:1, 5, 2, 6, 3, 7, 4, 8
Tôi hy vọng giúp trên. Để tổng hợp, nếu không có bất kỳ chức năng được xuất trong Excel, cách tốt nhất là tạo bản sao Value2
, sắp xếp/thao tác nó theo kết quả mong muốn và gán nó trở lại thuộc tính phạm vi.
Gần đây tôi đã hoàn thành một biến thể của QuickSort và dự định triển khai trong Excel. Thuật toán có hiệu quả và thực sự sẽ mang lại giá trị như một bổ trợ, nếu không phải dành thêm thời gian cho việc đưa các giá trị mảng vào phạm vi. Việc chuyển đổi chỉ hoạt động với ít hơn 65537, trong khi 'dán biến thể mảng thành phạm vi mất quá nhiều thời gian trên các loại lớn.Vì vậy, tôi đã viết một vài thủ tục cho phép sao chép các giá trị 2D từ phạm vi vào một mảng 1D (1D là cần thiết để phân loại) và (sau khi sắp xếp xong) đưa chúng trở lại, tất cả dựa trên SAFEARRAY và MemCopy (RtlMoveMemory) và, luân phiên, WriteProcessMemory.
Tất cả hoạt động tốt, theo như hoạt động bộ nhớ có liên quan: - giá trị dải ô được sao chép vào mảng (từ một SafeArray.pvData sang giá trị khác); - giá trị mảng (sau khi chạy bản đồ sắp xếp) được sao chép thành công vào Range.Value2 SafeArray.pvData.
Tuy nhiên, phạm vi không cập nhật, vì nó dường như lật ngược trở lại các giá trị cũ (nhiều hơn về điều đó trong mã bên dưới). Tại sao "Range.Value2 = SomeOther2dArray" hoạt động và không sửa đổi dữ liệu trực tiếp trong bộ nhớ? Tôi có cảm giác tôi đang thiếu điều gì đó ở đây. Là một công thức sắp xếp/cập nhật cần thiết, là tốt?
Dưới đây là thủ tục chính:
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
Hãy nói rằng các giá trị trong phạm vi 4, 3, 2, và 1. Trước SA_Duplicate arrSort, rngSort.Value2
bộ nhớ đọc:
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
nơi 130836704
là Range.Value2 SafeArray.pvData
và 129997032
là SortArray SafeArray.pvData
. Mỗi lô 16 byte đại diện cho biến thể dữ liệu thực tế, như đọc từ bộ nhớ (không có dịch LE, chỉ trong hex), với 2 byte đầu tiên cho biết VarType. Trong trường hợp này, vbDouble.
Sau khi sao chép, như mong đợi, bộ nhớ đọc:
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
Sau khi loại xong, SortArray SafeArray.pvData đọc:
129997032 05000000 00000000 00000000 0000F03F 05000000 00000000 00000000 00000040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00001040
Sau khi thực hiện SA_Duplicate rngSort.Value2, arrSort
, bộ nhớ cho thấy Range.Value2 SafeArray.pvData đã được cập nhật:
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
Tất cả trông vẫn khoẻ mạnh cho đến nay, ngoại trừ việc Debug.Print VarPtr(rngSort.Value2(1, 1)) & vbTab & Mem_ReadHex[...]
cho thấy rằng các giá trị lật trở lại trật tự ban đầu:
130836704 05000000 00000000 00000000 00001040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00000040 05000000 00000000 00000000 0000F03F
Hãy chia sẻ bất kỳ suy nghĩ hay phương pháp bạn thấy hiệu quả. Bất kỳ trợ giúp được đánh giá cao. Đó là bực bội phải chờ đợi cho Excel khoảng 4 giây (sắp xếp 1.000.000 + tế bào), ngay cả khi loại khó khăn nhất mất ít hơn thế.
Cảm ơn trước!
Tôi nghĩ rằng nó có thể được sâu sắc để biết những gì SA_Duplicate hiện, nhưng kể từ khi chỉ định nó là kết quả xuống đường để range.Value2 chỉ hoạt động, tôi giả định dữ liệu mới là chính xác? Vì vậy, tôi nghi ngờ Excel nội bộ giữ dữ liệu của nó trong một số cấu trúc khác hơn là an toàn, và các thuộc tính .Value/.Value2 chỉ là các trình bao thực hiện bản dịch; điều này có nghĩa là thao tác bộ nhớ trực tiếp của biến thể những thuộc tính này trở lại là vô nghĩa. –