EDYCJAKopiowanie tablicę do Range.Value2 SafeArray.pvData udane, ale Excel nie zaktualizować
Rzeczywiście, nie ma bezpośredni sposób edytowania wartości klasy w pamięci. Dzięki @AndASM za szczegółową odpowiedź i Carl; dobre przeczucie, było trafne. W tym momencie musiałem być zbyt splątany, zapominając, że Value2
to tylko własność.
Tymczasem ja zagłębił nieco głębiej z innych testów i debugowania OllyDbg i znalazłem kilka ciekawych rzeczy:
- Komórki są rozmieszczone w 16 x 1024 dziedzinach. Struktura zawierająca obszary może równie dobrze być arkuszem roboczym, ale nie mogę jeszcze potwierdzić;
- każdym razem właściwość
Value
wywoływana jest bezwzględne arkusz przesunięcia (wiersz, kolumna) wykorzystywane są do znalezienia odpowiedniego obszaru i następnie przez pewien indeksowania wewnątrz obszaru uzyskać rzeczywistą wartość; - Tworzy się SAFEARRAY 2D typu VARIANT;
- Wartości nie są pobierane w sąsiednim bloku, ale pojedynczo. Oznacza to, że każdy "punkt" w zakresie (wiersz, kol.) Jest wysyłany do procedury indeksowania , aby zwrócić wartość (wariant, oczywiście) dla odpowiadającego jej elementu SAFEARRAY o wartości ;
- W związku z powyższym, za każdym razem, gdy pobierzesz wartość przez
Range.Value2(row,col)
, cały proces zostanie powtórzony dla wszystkich wartości w zakresie. Wyobraź sobie wydajność, jeśli robisz to kilka razy w trakcie procedury lub, co gorsza, w pętli. Po prostu nie rób; lepiej jest utworzyć kopięValue2
i adresując go poprzez indeksowanie; końcu, a zwłaszcza rozkład wartości wewnątrz
SAFEARRAY.pvData
jest kolumnowe(col,row)
nie wierszowa, które można znaleźć intuicyjne i w sprzeczności z trybu VBA indeksowania , która(row,col)
. Może się to przydać, jeśli potrzebujesz uzyskiwania dostępu do danych pvData bezpośrednio w pamięci i zachowania spójności wymiarów. Jako przykład zakres jak poniżej1, 2, 3, 4 5, 6, 7, 8
byłyby przechowywane w
pvData
w następującej kolejności:1, 5, 2, 6, 3, 7, 4, 8
Mam nadzieję, że powyższe pomaga. Podsumowując, w przypadku braku takiej wyeksportowanej funkcji w programie Excel najlepszym sposobem jest utworzenie kopii Value2
, sortowanie/manipulowanie nią w kierunku pożądanego wyniku i przypisanie jej z powrotem do właściwości zakresu.
Niedawno zakończyłem odmianę QuickSort i zamierzam wprowadzić ją w programie Excel. Algorytm jest skuteczny i naprawdę przyniósłby wartość jako dodatek, gdyby nie dodatkowy czas poświęcony na umieszczenie wartości tablicowych w zakresie. Transpozycja działa tylko dla mniej niż 65537, podczas gdy "tablica wariantów wklejania do zakresu zajmuje zbyt wiele czasu na dużych sortach.
Tak, napisałem kilka procedur, które pozwoliłyby skopiować wartości 2D z zakresu do tablicy 1D (1D jest potrzebne do sortowania) i (po sortowaniu jest zrobione) odkładając je, wszystko oparte na SAFEARRAY i MemCopy (RtlMoveMemory) i, na przemian, WriteProcessMemory.
Wszystko działa dobrze, jeśli chodzi o operacje związane z pamięcią: - wartości zakresu są kopiowane do tablicy (z jednej karty SafeArray.pvData na drugą); - wartości tablic (po uruchomieniu sortowania) zostaną pomyślnie skopiowane do pliku Range.Value2 SafeArray.pvData.
Mimo to zakres nie aktualizuje się, ponieważ wydaje się, że cofa się do starych wartości (więcej o tym w poniższym kodzie). Dlaczego "Range.Value2 = SomeOther2dArray" działa i nie modyfikuje danych bezpośrednio w pamięci? Mam przeczucie, że czegoś tu brakuje. Czy potrzebne jest również sortowanie/aktualizacja formuły?
Oto główny procedura:
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
Powiedzmy wartości w przedziale są 4, 3, 2 i 1. Przed SA_Duplicate arrSort, rngSort.Value2
pamięć brzmi:
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
gdzie 130836704
to Range.Value2 SafeArray.pvData
i 129997032
jest SortArray SafeArray.pvData
. Każda 16-bajtowa partia reprezentuje wariant rzeczywistych danych, odczytanych z pamięci (bez tłumaczenia LE, tylko w języku heksagonalnym), z pierwszymi 2 bajtami wskazującymi typ VarType. W tym przypadku vbDouble.
Po kopii, zgodnie z oczekiwaniami, pamięć brzmi:
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
Po sortowania jest kompletny, SortArray SafeArray.pvData brzmi:
129997032 05000000 00000000 00000000 0000F03F 05000000 00000000 00000000 00000040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00001040
Po wykonaniu SA_Duplicate rngSort.Value2, arrSort
pamięć pokazuje, że Range.Value2 SafeArray.pvData została zaktualizowana:
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
Wszystko w porządku patrząc tak daleko, poza tym, że Debug.Print VarPtr(rngSort.Value2(1, 1)) & vbTab & Mem_ReadHex[...]
pokazuje, że wartości przerzucony z powrotem do pierwotnej kolejności:
130836704 05000000 00000000 00000000 00001040 05000000 00000000 00000000 00000840 05000000 00000000 00000000 00000040 05000000 00000000 00000000 0000F03F
Proszę podzielić jakieś przemyślenia lub metod, które znalazłeś skuteczne. Każda pomoc jest doceniana. To frustrujące, że trzeba czekać na Excela około 4 sekund (sortowanie 1 000 000 + komórek), gdy nawet najbardziej wymagający sort zajmuje mniej.
Z góry dzięki!
Myślę, że to może być wnikliwe wiedzieć, co robi SA_Duplicate, ale ponieważ po prostu przypisanie jego wyniku w dół do zakresu.Value2 po prostu działa, zakładam, że nowe dane są poprawne? Podejrzewam, że Excel wewnętrznie przechowuje dane w innej strukturze niż zabezpieczenie, a właściwości .Value/.Value2 są po prostu powłokami, które wykonują tłumaczenie; oznaczałoby to, że bezpośrednia manipulacja pamięcią wariantu powrotu tych właściwości jest bezcelowa. –