2013-05-01 10 views
9

में एक सरणी संदर्भ की प्रतिलिपि बनाएँ क्या वीबीए (या वीबी 6) में सरणी संदर्भ की प्रतिलिपि बनाने का कोई तरीका है?वीबीए

वीबीए में, सरणी मूल्य प्रकार हैं। एक सरणी चर आवंटित करने के लिए एक और प्रतिलिपि पूरे सरणी। मैं एक ही सरणी को इंगित करने के लिए दो सरणी चर प्राप्त करना चाहता हूं। क्या इसे पूरा करने का कोई तरीका है, शायद कुछ एपीआई मेमोरी फ़ंक्शन और/या फ़ंक्शन का उपयोग करके, जो वास्तव में वीबीए में एक चर के पते को वापस कर देता है?

Dim arr1(), arr2(), ref1 As LongPtr 
arr1 = Array("A", "B", "C") 

' Now I want to make arr2 refer to the same array object as arr1 
' If this was C#, simply assign, since in .NET arrays are reference types: 
arr2 = arr1 

' ...Or if arrays were COM objects: 
Set arr2 = arr1 

' VarPtr lets me get the address of arr1 like this: 
ref1 = VarPtr(arr1) 

' ... But I don't know of a way to *set* address of arr2. 

संयोग से, यह एक तरीका है की एक से अधिक पैरामीटर के लिए एक ही सरणी चर ByRef पास करके एक ही सरणी के लिए कई संदर्भ पाने के लिए संभव है:

Sub DuplicateRefs(ByRef Arr1() As String, ByRef Arr2() As String) 
    Arr2(0) = "Hello" 
    Debug.Print Arr1(0) 
End Sub 

Dim arrSource(2) As String 
arrSource(0) = "Blah" 

' This will print 'Hello', because inside DuplicateRefs, both variables 
' point to the same array. That is, VarPtr(Arr1) == VarPtr(Arr2) 
Call DuplicateRefs(arrSource, arrSource) 

लेकिन यह अभी भी बस के लिए एक की अनुमति नहीं है एक मौजूदा दायरे में एक ही दायरे में एक नया संदर्भ निर्माण।

+0

जब मैं आपके सवाल का जवाब पता नहीं है, मैं बहुत समाधान में दिलचस्पी है ... आप एक सिंगलटन वर्ग है कि आपके सरणी रखती बना सकते हैं और वर्ग के माध्यम से संदर्भ वापसी कर सकते हैं? – Marshall

+0

नहीं। किसी फ़ंक्शन या प्रॉपर्टी से किसी सरणी को लौटने से भी मान द्वारा संचालित होता है - सरणी की एक नई प्रति लौटाता है। यह वास्तव में असली मुद्दा है जिसे मैं संबोधित करने की कोशिश कर रहा हूं। –

उत्तर

12

हां, आप कर सकते हैं, यदि दोनों चर प्रकार भिन्न हैं।

यहां क्यों है: वेरिएंट प्रकार स्वयं एक रैपर है। एक संस्करण की वास्तविक बिट सामग्री 16 बाइट्स है। पहला बाइट वर्तमान में संग्रहीत वास्तविक डेटा प्रकार को इंगित करता है। मूल्य वास्तव में VbVarType enum से मेल खाता है। यानी यदि संस्करण वर्तमान में एक लंबा मूल्य धारण कर रहा है, तो पहला बाइट 0x03 होगा, vbLong का मान होगा। दूसरे बाइट में कुछ झंडे होते हैं। परीक्षा के लिए, यदि संस्करण में एक सरणी है, तो इस बाइट में 0x20 पर बिट सेट किया जाएगा।

शेष 14 बाइट्स का उपयोग संग्रहीत डेटा प्रकार पर निर्भर करता है। किसी भी सरणी प्रकार के लिए, इसमें सरणी का पता शामिल है।

अगर आप सीधे RtlMoveMemory का उपयोग कर आप प्रभाव में ओवरराइट है एक सरणी के लिए संदर्भमूल्य एक प्रकार का के ऊपर लिख इसका मतलब है कि। यह वास्तव में काम करता है!

एक चेतावनी है: जब एक सरणी चरणीय दायरे से बाहर हो जाता है, तो वीबी रनटाइम उस स्मृति को पुनः प्राप्त करेगा जो वास्तविक सरणी तत्वों में निहित है। जब आपने मैन्युअल रूप से वर्णित वेरिएंट कॉपीमेमरी तकनीक के माध्यम से एक सरणी संदर्भ मैन्युअल रूप से डुप्लिकेट किया है, तो नतीजा यह है कि रनटाइम दो बार एक ही मेमोरी को पुनः प्राप्त करने का प्रयास करेगा, जब दोनों प्रकार स्कोप से बाहर हो जाएंगे, और प्रोग्राम क्रैश हो जाएगा। इससे बचने के लिए, आपको वैरिएबल से गुजरने से पहले, 0s के साथ फिर से संस्करण को ओवरराइट करके संदर्भों में से एक को मैन्युअल रूप से "मिटाएं" की आवश्यकता है।

उदाहरण 1: यह काम करता है, लेकिन (जब उप बाहर निकलता है)

Private Declare PtrSafe Sub CopyMemory Lib "kernel32" _ 
    Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) 

Sub CopyArrayRef_Bad() 
    Dim v1 As Variant, v2 As Variant 
    v1 = Array(1, 2, 3) 
    CopyMemory v2, v1, 16 

    ' Proof: 
    v2(1) = "Hello" 
    Debug.Print Join(v1, ", ") 

    ' ... and now the program will crash 
End Sub 

उदाहरण 2 एक बार दुर्घटना होगा दोनों चर दायरे से बाहर जाना: सावधान सफाई के साथ, आप दूर से प्राप्त कर सकते हैं यह!

Private Declare PtrSafe Sub CopyMemory Lib "kernel32" _ 
    Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) 

Private Declare PtrSafe Sub FillMemory Lib "kernel32" _ 
    Alias "RtlFillMemory" (Destination As Any, ByVal Length As Long, ByVal Fill As Byte) 

Sub CopyArrayRef_Good() 
    Dim v1 As Variant, v2 As Variant 
    v1 = Array(1, 2, 3) 
    CopyMemory v2, v1, 16 

    ' Proof: 
    v2(1) = "Hello" 
    Debug.Print Join(v1, ", ") 

    ' Clean up: 
    FillMemory v2, 16, 0 

    ' All good! 
End Sub 
+3

+1 समान रेखाओं के साथ एक गैर-संस्करण सरणी एक सुरक्षित संरचना है जिसमें विभिन्न सदस्यों और इसके डेटा के लिए एक पॉइंटर भी शामिल है जिसे आप संभवतः * प्रतिलिपि बना सकते हैं और ओवरराइट कर सकते हैं। (वीबी रनटाइम varptrarray() निर्यात एक वीबीए सरणी SAFEARRAY शीर्षलेख के लिए एक सूचक देता है) –

+2

@AlexK। प्रतिभाशाली! मुझे [ऑटोमेशन एरे मैनिपुलेशन एपीआई] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms221145 (v = vs.85) .aspx) से अवगत नहीं था। मैं अनुमान लगाता हूं कि वीबी [ए] रनटाइम अपने एरे को लागू करने के लिए इस एपीआई का उपयोग करता है, इसलिए अचानक मुझे कुछ वीबी रनटाइम इंटर्नल्स में स्पष्ट दृश्य मिलता है, जिसे मैं हमेशा ढूंढ रहा हूं। –

1

इस समाधान के बारे में क्या ...

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ 
        (Destination As Any, Source As Any, ByVal Length As Long) 

Public Sub TRIAL() 
Dim myValueType As Integer 
Dim mySecondValueType As Integer 
Dim memPTR As Long 

myValueType = 67 
memPTR = VarPtr(mySecondValueType) 
CopyMemory ByVal memPTR, myValueType, 2 
Debug.Print mySecondValueType 
End Sub 

अवधारणा एक CodeProject लेख here

+0

जो अभी भी मान की प्रतिलिपि बनाता है, संदर्भ नहीं। यदि संदर्भ सफलतापूर्वक कॉपी किया गया था, तो 'mySecondValueType = 42'' को 'myValueType' के मान को भी बदल दिया जाएगा। –

+0

मैंने इसे काम करने के लिए एक तरीका निकाला था। मेरा जवाब देखें –

0

और क्या बारे में wraper बनाने के लिए से आया है? इस वर्ग मॉड्यूल 'myArray' (सरलीकृत उदाहरण) की तरह:

Private m_myArray() As Variant 

Public Sub Add(ByVal items As Variant) 
    m_myArray = items 
End Sub 

Public Sub Update(ByVal newItem As String, ByVal index As Integer) 
    m_myArray(index) = newItem 
End Sub 

Public Function Item(ByVal index As Integer) As String 
    Item = m_myArray(index) 
End Function 

तो मानक मॉड्यूल में:

Sub test() 
    Dim arr1 As MyArray 
    Dim arr2 As MyArray 

    Set arr1 = New MyArray 
    arr1.Add items:=Array("A", "B", "C") 

    Set arr2 = arr1 

    arr1.Update "A1", 0 

    Debug.Print arr1.Item(0) 
    Debug.Print arr2.Item(0) 
End Sub 

इस मदद करता है?

+0

यह एक अच्छा विचार है, लेकिन अंतर्निहित 'संग्रह' ऑब्जेक्ट पहले से ही यह प्रदान करता है। और जो आप खो देते हैं वह अन्य सभी गहरी एम्बेडेड भाषा विशेषताएं हैं जो सरणी के लिए विशिष्ट हैं। –