2015-12-01 10 views
10

संपादितRange.Value2 SafeArray.pvData सफल करने के लिए एक सरणी प्रतिलिपि बनाई जा रही है, लेकिन एक्सेल अद्यतन करने के लिए

दरअसल विफल रहता है, वहाँ स्मृति में सीमा मूल्यों संपादन का कोई सीधा रास्ता है। विस्तृत उत्तर और कार्ल के लिए धन्यवाद @AndASM; अच्छा हंच, यह स्पॉट-ऑन था। मुझे उस पल में सभी उलटाई में बहुत उलझन में होना चाहिए, Value2 भूलना सिर्फ एक संपत्ति है।

इस बीच, मैं OllyDbg के साथ कुछ अन्य परीक्षण और डिबगिंग के साथ एक सा गहरा delved और कुछ दिलचस्प बातें पाया:

  1. प्रकोष्ठों 16 x 1024 क्षेत्रों में व्यवस्थित कर रहे हैं। क्षेत्रों को रखने वाली संरचना वर्कशीट बहुत अच्छी तरह से हो सकती है, लेकिन मैं अभी तक पुष्टि नहीं कर सकता;
  2. हर बार Value संपत्ति लागू की जाती है, पूर्ण शीट ऑफ़सेट (पंक्ति, कॉलम) का उपयोग इसी क्षेत्र और को वास्तविक मूल्य प्राप्त करने के लिए क्षेत्र के अंदर कुछ अनुक्रमण के लिए किया जाता है;
  3. प्रकार का एक 2 डी सुरक्षितता VARIANT बनाया गया है;
  4. मूल्य एक संगत ब्लॉक में, लेकिन व्यक्तिगत रूप से पुनर्प्राप्त नहीं किए जाते हैं। इसका मतलब है कि इंडेक्सिंग प्रक्रिया में प्रत्येक "बिंदु" को संबंधित SAFEARRAY तत्व के लिए मान (संस्करण, स्पष्ट रूप से) वापस करने के लिए भेजा गया है;
  5. उपर्युक्त के परिणामस्वरूप, प्रत्येक बार जब आप Range.Value2(row,col) के माध्यम से कोई मान पुनर्प्राप्त करते हैं, तो संपूर्ण प्रक्रिया को सभी सीमाओं के मानों के लिए दोहराया जाता है। यदि आप इस प्रक्रिया में कई बार एक प्रक्रिया के अंदर या इससे भी बदतर, एक लूप के अंदर प्रदर्शन प्रदर्शन की कल्पना कीजिए। बस मत करो; आप इंडेक्सिंग के जरिए Value2 और की प्रतिलिपि बनाने से बेहतर हैं;
  6. अंतिम, अभी तक कम से कम नहीं, SAFEARRAY.pvData अंदर मानों का वितरण स्तंभ आधारित (col,row), नहीं पंक्ति के आधार पर, पाया जा सकता है जिससे उसे प्रति सहज और VBA अनुक्रमण मोड है, जो (row,col) है के साथ अंतर पर है। यदि आपको को स्मृति में सीधे पीवीडेटा तक पहुंचने और आयाम समेकन रखने की आवश्यकता है तो यह आसान हो सकता है। उदाहरण के लिए, नीचे

    1, 2, 3, 4 
        5, 6, 7, 8 
    

    एक निम्न क्रम में pvData में संग्रहीत किया जाएगा की तरह एक सीमा:

    1, 5, 2, 6, 3, 7, 4, 8 
    

मुझे आशा है कि इसके बाद के संस्करण में मदद करता है। एक्सेल में ऐसे किसी भी निर्यात किए गए फ़ंक्शन की अनुपस्थिति में, Value2 की प्रतिलिपि बनाने के लिए सबसे अच्छा तरीका है, वांछित परिणाम की ओर सॉर्टिंग/मैनिपुलेट करना और इसे रेंज प्रॉपर्टी पर वापस असाइन करना।


मैंने हाल ही में क्विकॉर्ट का एक बदलाव पूरा किया है और एक्सेल के लिए लागू करने का इरादा रखता है। एल्गोरिदम प्रभावी है और वास्तव में एक ऐड-इन के रूप में मूल्य लाएगा, यदि सरणी मानों को सीमा में डालने पर अतिरिक्त समय के लिए नहीं किया गया है। ट्रांसपोज़िंग केवल 65537 से कम के लिए काम करता है, जबकि 'पेस्ट वेरिएंट-सरणी रेंज को बड़े प्रकार पर बहुत लंबा लगता है।

इसलिए, मैंने कुछ प्रक्रियाएं लिखीं जो 2 डी सरणी को 1 डी सरणी (सॉर्टिंग के लिए 1 डी की आवश्यकता है) में कॉपी करने की अनुमति देगी और (सॉर्टिंग के बाद) उन्हें वापस रखकर, सभी सुरक्षित और मेमकोपी पर आधारित हैं (RtlMoveMemory) और, वैकल्पिक रूप से, WriteProcessMemory।

सभी काम अच्छी तरह से काम करते हैं, जहां तक ​​मेमोरी ऑपरेशंस का संबंध है: - रेंज मान सरणी में कॉपी किए जाते हैं (एक SafeArray.pvData से दूसरे तक); - सरणी मान (सॉर्टिंग अलगो चलाने के बाद) सफलतापूर्वक रेंज में कॉपी किए जाते हैं। Value2 SafeArray.pvData।

फिर भी, सीमा अद्यतन नहीं होती है, क्योंकि यह पुराने मानों (नीचे दिए गए कोड में अधिक) पर वापस जाने लगता है। "रेंज.वेल्यू 2 = कुछ अन्य 2 डीएरे" क्यों काम करेगा और सीधे मेमोरी में डेटा को संशोधित नहीं करेगा? मुझे एहसास है कि मुझे यहां कुछ याद आ रही है। एक फॉर्मूला सॉर्ट/अपडेट की आवश्यकता है, साथ ही?

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 और 129997032SortArray SafeArray.pvData है। प्रत्येक 16-बाइट बैच वेरिएंट वास्तविक डेटा का प्रतिनिधित्व करता है, जैसा मेमोरी (कोई LE अनुवाद, केवल हेक्स में) से पढ़ा जाता है, वाराटाइप को इंगित करने वाले पहले 2 बाइट्स के साथ। इस मामले में, 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 

कोई विचार या तरीकों आप प्रभावी पाया साझा करें। किसी भी मदद की सराहना की है। एक्सेल के लिए 4 सेकंड (1,000,000 + कोशिकाओं को सॉर्ट करना) की प्रतीक्षा करना निराशाजनक है, जब भी सबसे चुनौतीपूर्ण प्रकार उससे कम लेता है।

अग्रिम धन्यवाद!

+0

मुझे लगता है कि यह जानना अंतर्दृष्टिपूर्ण हो सकता है कि SA_Duplicate क्या करता है, लेकिन जब से इसे रेंज करने के लिए सड़क को नीचे दिया जाता है। Value2 बस काम करता है, मुझे लगता है कि नया डेटा सही है? इसलिए मुझे संदेह है कि Excel आंतरिक रूप से किसी अन्य संरचना में सुरक्षित रूप से डेटा को रखता है, और वाल्यू /। वैल्यू 2 गुण केवल गोले हैं जो अनुवाद करते हैं; इसका मतलब यह होगा कि वेरिएंट की डायरेक्ट मेमोरी मैनिपुलेशन इन गुणों की वापसी व्यर्थ है। –

उत्तर

1

ठीक है, आपने कई महत्वपूर्ण भागों, विशेष रूप से SA_Duplicate का एक कार्यान्वयन प्रदान नहीं किया है, इसलिए यह ज्यादातर अनुमान लगाया जाता है। लेकिन, मुझे लगता है कि जवाब काफी सरल है।

Range.Value2 एक संपत्ति है, एक चर नहीं। तो दृश्यों के पीछे यह वास्तव में दो कार्य है, उन्हें Range.let_Value2 और Range.get_Value2 पर कॉल करने दें।

उस ने कहा, आप काम करने के लिए SA_Duplicate rngSort.Value2, arrSort पर कॉल की अपेक्षा कैसे करते हैं? क्योंकि मैं जो देखता हूं वह SA_Duplicate rngSort.get_Value2, arrSort है।मुझे लगता है कि rngSort.get_Value2 एक नया सेफअरे बना रहा है, फिर Excel की आंतरिक डेटा संरचनाओं से डेटा को उस सुरक्षितअरे में कॉपी कर रहा है। यदि मैं सही हूं, तो आप अपने डेटा को अस्थायी बफर में लिख रहे हैं कि वीबीए बाद में त्याग देता है और एक्सेल पहले से ही भूल गया है।

आपको rngSort.let_Value2 arrSort का उपयोग करने की आवश्यकता है जिसे आमतौर पर rngSort.Value2 = arrSort कहा जाता है।

एक साइड नोट के रूप में, यदि get_Value2 मुझे लगता है कि यह एक नई सरणी आवंटित करता है, तो SA_Duplicate कॉल अनावश्यक हैं और आप बदले गए सरणी को जगह में सॉर्ट करने में सक्षम हो सकते हैं। जब आप पूरा कर लें तो सरणी चर को वापस संपत्ति द्वारा असाइन करके let_Value2 पर पास करना याद रखें।

संबंधित मुद्दे