2012-01-13 16 views
11

का उपयोग कर एक्सेल में VBProject.VBComponents में किए गए परिवर्तनों को फ्लश करना, मैं एक्सेल में कुछ अजीब quirks का अनुभव कर रहा है जबकि प्रोग्रामिंग रूप से मॉड्यूल को हटा रहा है और फिर उन्हें फ़ाइलों से reimporting। असल में, मेरे पास वर्जनकंट्रोल नामक एक मॉड्यूल है जो मेरी फ़ाइलों को एक पूर्वनिर्धारित फ़ोल्डर में निर्यात करना है, और मांग पर उन्हें पुनः आयात करना है। जबकि कुछ डुप्लिकेट (जैसे mymodule और mymodule1) है,वीबीए

Dim i As Integer 
Dim ModuleName As String 
Application.EnableEvents = False 
With ThisWorkbook.VBProject 
    For i = 1 To .VBComponents.Count 
     If .VBComponents(i).CodeModule.CountOfLines > 0 Then 
      ModuleName = .VBComponents(i).CodeModule.Name 
      If ModuleName <> "VersionControl" Then 
       If PathExists(VersionControlPath & "\" & ModuleName & ".bas") Then 
        Call .VBComponents.Remove(.VBComponents(ModuleName)) 
        Call .VBComponents.Import(VersionControlPath & "\" & ModuleName & ".bas") 
       Else 
        MsgBox VersionControlPath & "\" & ModuleName & ".bas" & " cannot be found. No operation will be attempted for that module." 
       End If 
      End If 
     End If 
    Next i 
End With 

इस चलाने के बाद, मैंने देखा है कि कुछ मॉड्यूल अब दिखाई नहीं देते: यह reimporting (समस्या के साथ यह नीचे वर्णित है) के लिए कोड है । कोड के माध्यम से कदम उठाने के दौरान, यह स्पष्ट हो गया कि कुछ मॉड्यूल अभी भी Remove कॉल के बाद रेंगते हैं, और उन्हें अभी भी परियोजना में रहते हुए पुनः आयात किया जाना है। कभी-कभी, यह परिणामस्वरूप मॉड्यूल को 1 से चिपक गया, लेकिन कभी-कभी मेरे पास मूल और प्रति दोनों ही होते थे।

क्या Remove और Import पर कॉल को फ़्लश करने का कोई तरीका है, इसलिए वे स्वयं को लागू करते हैं? यदि अनुप्रयोग ऑब्जेक्ट में कोई है, तो मैं प्रत्येक के बाद Save फ़ंक्शन को कॉल करने के बारे में सोच रहा हूं, हालांकि आयात के दौरान चीजें गलत होने पर यह नुकसान हो सकती है।

विचार?

संपादित करें: synchronization से version-control में टैग बदल दिया गया।

+0

+1 चालाक थोड़ा कुछ घर का बना संस्करण नियंत्रण करने के लिए तरीका है। मुझे ऐसा कुछ करना चाहिए। –

+0

यह इस सवाल से प्रेरित था [http://stackoverflow.com/questions/131605/best-way-to-do-version-control-for-ms-excel) यहां StackOverflow पर - मेरा संस्करण केवल एक मामूली है रीमेक। – CamilB

+1

मैंने इस तरह कुछ भी नहीं बनाया है, लेकिन जो चीजें मैं कोशिश करूँगा वह होगा: किसी अन्य कार्यपुस्तिका/एडिन से कॉल करना; पहले कार्यपुस्तिका का बैक अप लेना, सभी हटाने को एक बार में करना, सहेजना, सभी को एक साथ आयात करना। आप रॉब बोवी के कोड क्लीनर के COM संस्करण के साथ भी गड़बड़ कर सकते हैं। आप इसका संदर्भ सेट कर सकते हैं और आयात, निर्यात और अन्य कार्यों तक पहुंच सकते हैं। मुझे यह देखने में दिलचस्पी होगी कि आपको क्या पता चल रहा है। –

उत्तर

12

यह एक लाइव सरणी है, आप पुनरावृत्ति के दौरान वस्तुओं को जोड़ और निकाल रहे हैं जिससे सूचकांक संख्या बदल रही है। सरणी को पीछे की ओर संसाधित करने का प्रयास करें। यहाँ किसी भी त्रुटि से निपटने के बिना मेरे समाधान है:

Private Const DIR_VERSIONING As String = "\\VERSION_CONTROL" 
Private Const PROJ_NAME As String = "PROJECT_NAME" 

Sub EnsureProjectFolder() 
    ' Does this project directory exist 
    If Len(Dir(DIR_VERSIONING & PROJ_NAME, vbDirectory)) = 0 Then 
     ' Create it 
     MkDir DIR_VERSIONING & PROJ_NAME 
    End If 
End Sub 

Function ProjectFolder() As String 
    ' Ensure the folder exists whenever we try to access it (can be deleted mid execution) 
    EnsureProjectFolder 
    ' Create the required full path 
    ProjectFolder = DIR_VERSIONING & PROJ_NAME & "\" 
End Function 

Sub SaveCodeModules() 

    'This code Exports all VBA modules 
    Dim i%, sName$ 

    With ThisWorkbook.VBProject 
     ' Iterate all code files and export accordingly 
     For i% = 1 To .VBComponents.count 
      ' Extract this component name 
      sName$ = .VBComponents(i%).CodeModule.Name 
      If .VBComponents(i%).Type = 1 Then 
       ' Standard Module 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".bas" 
      ElseIf .VBComponents(i%).Type = 2 Then 
       ' Class 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".cls" 
      ElseIf .VBComponents(i%).Type = 3 Then 
       ' Form 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".frm" 
      ElseIf .VBComponents(i%).Type = 100 Then 
       ' Document 
       .VBComponents(i%).Export ProjectFolder & sName$ & ".bas" 
      Else 
       ' UNHANDLED/UNKNOWN COMPONENT TYPE 
      End If 
     Next i 
    End With 

End Sub 

Sub ImportCodeModules() 
    Dim i%, sName$ 

    With ThisWorkbook.VBProject 
     ' Iterate all components and attempt to import their source from the network share 
     ' Process backwords as we are working through a live array while removing/adding items 
     For i% = .VBComponents.count To 1 Step -1 
      ' Extract this component name 
      sName$ = .VBComponents(i%).CodeModule.Name 
      ' Do not change the source of this module which is currently running 
      If sName$ <> "VersionControl" Then 
       ' Import relevant source file if it exists 
       If .VBComponents(i%).Type = 1 Then 
        ' Standard Module 
        .VBComponents.Remove .VBComponents(sName$) 
        .VBComponents.Import fileName:=ProjectFolder & sName$ & ".bas" 
       ElseIf .VBComponents(i%).Type = 2 Then 
        ' Class 
        .VBComponents.Remove .VBComponents(sName$) 
        .VBComponents.Import fileName:=ProjectFolder & sName$ & ".cls" 
       ElseIf .VBComponents(i%).Type = 3 Then 
        ' Form 
        .VBComponents.Remove .VBComponents(sName$) 
        .VBComponents.Import fileName:=ProjectFolder & sName$ & ".frm" 
       ElseIf .VBComponents(i%).Type = 100 Then 
        ' Document 
        Dim TempVbComponent, FileContents$ 
        ' Import the document. This will come in as a class with an increment suffix (1) 
        Set TempVbComponent = .VBComponents.Import(ProjectFolder & sName$ & ".bas") 

        ' Delete any lines of data in the document 
        If .VBComponents(i%).CodeModule.CountOfLines > 0 Then .VBComponents(i%).CodeModule.DeleteLines 1, .VBComponents(i%).CodeModule.CountOfLines 

        ' Does this file contain any source data? 
        If TempVbComponent.CodeModule.CountOfLines > 0 Then 
         ' Pull the lines into a string 
         FileContents$ = TempVbComponent.CodeModule.Lines(1, TempVbComponent.CodeModule.CountOfLines) 
         ' And copy them to the correct document 
         .VBComponents(i%).CodeModule.InsertLines 1, FileContents$ 
        End If 

        ' Remove the temporary document class 
        .VBComponents.Remove TempVbComponent 
        Set TempVbComponent = Nothing 

       Else 
        ' UNHANDLED/UNKNOWN COMPONENT TYPE 
       End If 
      End If 
      Next i 
     End With 

End Sub 
+2

यदि संभव हो तो मैं +2 दे दूंगा। सबसे पहले, आपने त्रुटि का सही कारण प्रकट किया: लाइव सरणी संपादित करना; यह मेरी एक बहुत बेवकूफ गलती थी, मुझे पूरा यकीन नहीं था कि फिर उन सरणी कैसे काम करते थे।बाहर निकलता है वे वास्तव में 'संग्रह' वस्तुओं हैं। दूसरा, आपका कोड ठीक से मॉड्यूल_ के अलग-अलग प्रकारों को संभालता है और उन्हें उचित फ़ाइल एक्सटेंशन के साथ सहेजता है। मैंने कुछ समय पहले ऐसा करने के लिए अपना कोड बदल दिया, यह वास्तव में काफी महत्वपूर्ण है; अपना कोड दिखाने के लिए +1 जो ऐसा करता है, इसलिए लोगों को पता चलेगा। – CamilB

1

ओपी यहां ... मैं इस अजीब मुद्दे के आसपास काम करने में कामयाब रहा, लेकिन मुझे एक सच्चा समाधान नहीं मिला है। मैंने जो किया है वह यहाँ है।

  1. प्रश्न पोस्ट करने के बाद मेरा पहला प्रयास यह (स्पॉइलर: यह लगभग काम किया): था

    रखें आयात करने से अलग हटाने, लेकिन एक ही प्रक्रिया में। इसका मतलब है कि मेरे पास 3 लूप थे - एक मॉड्यूल नामों (सादा स्ट्रिंग्स के रूप में) की एक सूची को स्टोर करने के लिए, मॉड्यूल को हटाने के लिए और दूसरा फाइलों से मॉड्यूल आयात करने के लिए (उपर्युक्त सूची में संग्रहीत नामों के आधार पर) ।

    समस्या: निष्कासन लूप समाप्त होने पर कुछ मॉड्यूल अभी भी प्रोजेक्ट में थे। क्यूं कर? मै समझा नही सकता। मैं इसे बेवकूफ समस्या संख्या के रूप में चिह्नित करूंगा। 1। मैंने फिर Remove को प्रत्येक मॉड्यूल के लिए एक लूप के अंदर कॉल करने का प्रयास किया जो उस एकल मॉड्यूल को हटाने की कोशिश करता रहा जब तक कि वह इसे प्रोजेक्ट में नहीं ढूंढ पाता। यह एक निश्चित मॉड्यूल के लिए एक अनंत लूप में फंस गया - मैं यह नहीं बता सकता कि उस विशेष के बारे में इतना खास क्या है।

    मुझे अंत में पता चला कि एक्सेल को केवल अपने विचारों को साफ़ करने के लिए कुछ समय मिलने के बाद ही वास्तव में हटा दिया गया था। यह Application.Wait() के साथ काम नहीं किया। वर्तमान में चल रहे वीबीए कोड को वास्तव में ऐसा होने के लिए समाप्त करने की आवश्यकता है। अजीब।

  2. दूसरा काम के आसपास का प्रयास (स्पॉइलर: फिर, यह लगभग काम किया):

    एक्सेल इन्हें हटाने के बाद साँस लेने के लिए आवश्यक समय देने के लिए, मैं बिना किसी बटन को क्लिक हैंडलर के अंदर हटाने के पाश (रखा "जब तक यह चला गया है तब तक हटाएं" लूप), और किसी अन्य बटन के क्लिक हैंडलर में आयात लूप। बेशक, मुझे मॉड्यूल नामों की सूची चाहिए, इसलिए मैंने इसे तारों की वैश्विक सरणी बना दी। इसे हटाने वाले लूप से पहले क्लिक हैंडलर में बनाया गया था, और इसे आयातित लूप द्वारा एक्सेस किया जाना था। काम करना चाहिए था, है ना?

    समस्या: आयातित लूप प्रारंभ होने पर उपर्युक्त स्ट्रिंग सरणी खाली थी (अन्य क्लिक हैंडलर के अंदर)। यह निश्चित रूप से वहां था जब हटाने लूप समाप्त हो गया - मैंने इसे डीबग.प्रिंट के साथ मुद्रित किया। मुझे लगता है कि इसे हटाने (डी?) द्वारा आवंटित किया गया है। यह बेवकूफ समस्या संख्या होगी। 2। मॉड्यूल नाम वाले स्ट्रिंग सरणी के बिना, आयात करने वाले पाश ने कुछ भी नहीं किया, इसलिए यह कार्य-आसपास विफल रहा।

  3. अंतिम, कार्यात्मक कामकाज। यह एक काम करता है।

    मैंने वर्क-आस-पास नंबर 2 लिया और स्ट्रिंग सरणी में मॉड्यूल नामों को संग्रहीत करने के बजाय, मैंने उन्हें एक सहायक शीट की पंक्ति में संग्रहित किया (मैंने यह शीट "डेवेल" कहा)।

यह था। अगर कोई बेवकूफ समस्या संख्या समझा सकता है। 1 और बेवकूफ समस्या संख्या। 2, मैं आपसे विनती करता हूं, ऐसा करो। वे शायद बेवकूफ नहीं हैं - मैं अभी भी वीबीए के साथ शुरुआत में हूं, लेकिन मेरे पास अन्य (सनी और आधुनिक) भाषाओं में प्रोग्रामिंग का ठोस ज्ञान है।

मैं बेवकूफ समस्या संख्या को चित्रित करने के लिए कोड जोड़ सकता हूं। 2, लेकिन यह उत्तर पहले से ही लंबा है। अगर मैंने जो किया वह स्पष्ट नहीं था, तो मैं इसे यहां रखूंगा।

+1

शुद्ध अटकलें: मुझे लगता है कि एक्सेल का वीबीए इंजन (संभवतः एक संकलित) वीबीए फ़ंक्शन के मध्य में हटाने वाले मॉड्यूल को संभाल नहीं सकता है। (यह क्या करना है, वीबीए को फिर से सम्मिलित करें और नौकरी खत्म करने के लिए इसे प्रतिस्थापित करें? बहुत मुश्किल।) जब तक आप वीबीए फ़ंक्शन से बाहर न हों, तब तक प्रतीक्षा करें। –

+0

@ todda.speot.is: यह समझ में आएगा, लेकिन लूप के दौरान कुछ मॉड्यूल हटा दिए गए थे (जिसे मैं जानता हूं, कम से कम, यह अन्य लोगों के साथ भी अजीब व्यवहार कर सकता था, इससे पहले कि मैं समझ रहा था कि क्या हो रहा था पर)। वीबीए केवल एक विशिष्ट मॉड्यूल पर फंस गया, और बाद में आगे बढ़ने से इंकार कर दिया। – CamilB

+1

अधिक अटकलें: कुछ मॉड्यूल में वैश्विक चर थे और अन्य नहीं थे। बिना उन्हें हटाया जा सकता है, क्योंकि अन्य मॉड्यूल को पुन: संकलित करने की कोई आवश्यकता नहीं है। वैश्विक चर वाले (या शायद आपके फ़ंक्शन से चल रहे मॉड्यूल से संदर्भित) निष्पादन के अंत तक हटाया नहीं जा सका। –

1

जब आयात करने डुप्लिकेट बचने के लिए, मैं निम्नलिखित रणनीति के साथ स्क्रिप्ट को संशोधित:

  • मौजूदा मॉड्यूल
  • आयात मॉड्यूल
  • हटाएं नाम बदली मॉड्यूल का नाम बदलें

मेरे पास आयात के दौरान अब डुप्लिकेट नहीं है।


Sub SaveCodeModules() 

'This code Exports all VBA modules 
Dim i As Integer, name As String 

With ThisWorkbook.VBProject 
For i = .VBComponents.Count To 1 Step -1 

    name = .VBComponents(i).CodeModule.name 

    If .VBComponents(i).Type = 1 Then 
     ' Standard Module 
     .VBComponents(i).Export Application.ThisWorkbook.Path & "\trunk\" & name & ".module" 
    ElseIf .VBComponents(i).Type = 2 Then 
     ' Class 
     .VBComponents(i).Export Application.ThisWorkbook.Path & "\trunk\" & name & ".classe" 
    ElseIf .VBComponents(i).Type = 3 Then 
     ' Form 
     .VBComponents(i).Export Application.ThisWorkbook.Path & "\trunk\" & name & ".form" 
    Else 
     ' DO NOTHING 
    End If 
Next i 
End With 

End Sub 

Sub ImportCodeModules() 

Dim i As Integer 
Dim delname As String 
Dim modulename As String 

With ThisWorkbook.VBProject 
For i = .VBComponents.Count To 1 Step -1 

    modulename = .VBComponents(i).CodeModule.name 

    If modulename <> "VersionControl" Then 

     delname = modulename & "_to_delete" 

     If .VBComponents(i).Type = 1 Then 
      ' Standard Module 
      .VBComponents(modulename).name = delname 
      .VBComponents.Import Application.ThisWorkbook.Path & "\trunk\" & modulename & ".module" 
      .VBComponents.Remove .VBComponents(delname) 

     ElseIf .VBComponents(i).Type = 2 Then 
      ' Class 
      .VBComponents(modulename).name = delname 
      .VBComponents.Import Application.ThisWorkbook.Path & "\trunk\" & modulename & ".classe" 
      .VBComponents.Remove .VBComponents(delname) 

     ElseIf .VBComponents(i).Type = 3 Then 
      ' Form 
      .VBComponents.Remove .VBComponents(modulename) 
      .VBComponents.Import Application.ThisWorkbook.Path & "\trunk\" & modulename & ".form" 
     Else 
      ' DO NOTHING 
     End If 

    End If 
Next i 

End With 

End Sub 

कोड एक नया मॉड्यूल "VersionControl"

0

नाम बदलने, आयात में चिपकाया और मेरे मामले में काम नहीं किया वैकल्पिक हल को नष्ट किया जाना है। ऐसा लगता है (लेकिन यह शुद्ध अटकलें है) कि एक्सेल संकलित ऑब्जेक्ट को अपनी .XLMS फ़ाइल में सहेज सकता है, और जब यह फ़ाइल दोबारा खोला जाता है तो यह ऑब्जेक्ट इस वर्कबुक_ऑपेन फ़ंक्शन से पहले स्मृति में पुनः लोड किया जाता है। और इससे विफल होने या देरी होने के लिए कुछ मॉड्यूल के नामकरण (या हटाने) की ओर जाता है (यहां तक ​​कि जब इसे DoEvents कॉल के साथ मजबूर करने का प्रयास किया जाता है)। मैंने पाया एकमात्र कामकाज .XLS बाइनरी प्रारूप का उपयोग करना है। कुछ अस्पष्ट कारणों के लिए (मुझे संदेह है कि संकलित वस्तुओं को फ़ाइल में बंडल नहीं किया जाता है), यह मेरे लिए काम करता है।

आपको यह जानना होगा कि जब आप अपना आयात कोड चलाते हैं तो आप किसी भी मॉड्यूल को इस्तेमाल या संदर्भित नहीं कर पाएंगे (संदर्भ 32813 त्रुटि/मॉड्यूल को हटाने में देरी होगी जब तक आप आयात करने की कोशिश नहीं करेंगे, तब तक आपके मॉड्यूल नामों के अंत में परेशान '1 जोड़ना)।लेकिन किसी भी अन्य मॉड्यूल के लिए, यह काम करना चाहिए।

यदि आपके सभी स्रोत कोड को प्रबंधित करने की आवश्यकता है, तो बेहतर कार्यवाही होगी कि आप अपनी कार्यपुस्तिका को कुछ स्क्रिप्ट या टूल का उपयोग करके स्क्रैच से "बनाएं", या एक बेहतर उपयुक्त प्रोग्रामिंग भाषा पर स्विच करें (यानी वह नहीं जो जीवित नहीं है एक ऑफिस सूट सॉफ़्टवेयर के अंदर;) मैंने कोशिश नहीं की है लेकिन आप यहां देख सकते हैं: Source control of Excel VBA code modules

1

मैं इस मुद्दे के साथ अब दिनों से संघर्ष कर रहा हूं। मैंने इस तरह के एक क्रूड संस्करण नियंत्रण प्रणाली का निर्माण किया, हालांकि सरणी का उपयोग नहीं किया। वर्जन कंट्रोल मॉड्यूल वर्कबुक_ ओपन पर आयात किया जाता है और फिर स्टार्टअप प्रक्रिया को संस्करण नियंत्रण मॉड्यूल में सूचीबद्ध सभी मॉड्यूल आयात करने के लिए कहा जाता है। एक्सेल को छोड़कर सबकुछ बढ़िया काम करता है सिवाय इसके कि डुप्लिकेट वर्जन कंट्रोल मॉड्यूल बनाना शुरू हो गया क्योंकि यह मौजूदा मॉड्यूल को हटाने से पहले नया मॉड्यूल आयात करेगा। मैंने पिछले मॉड्यूल में हटाए गए जोड़कर उस पर काम किया। तब समस्या यह है कि एक ही नाम के साथ अभी भी दो प्रक्रियाएं थीं। चिप पियरसन के पास प्रोग्रामेटिक रूप से प्रक्रिया को हटाने के लिए कुछ कोड है, इसलिए मैंने पुराने संस्करण नियंत्रण मॉड्यूल से स्टार्टअप कोड हटा दिया है। फिर भी, मैं एक ऐसे मुद्दे में भाग गया जहां स्टार्टअप प्रक्रिया के समय तक प्रक्रिया को हटाया नहीं गया था। अंत में मुझे एक और ढेर ओवरफ्लो थ्रेड पर एक समाधान मिला जो इतना आसान है कि यह मुझे दीवार के माध्यम से अपना सिर रखना चाहता है। मुझे बस इतना करना था कि

Application.OnTime Now + TimeValue("00:00:01"), "StartUp"  

सब कुछ पूरी तरह से काम करता है। हालांकि, मैं शायद वापस जाउंगा और मॉड्यूल के अनावश्यक नामकरण को हटा दूंगा और दूसरी प्रक्रिया को हटा दूंगा और देख सकता हूं कि यह अकेले मेरी मूल समस्या हल करता है या नहीं। यहाँ समाधान के साथ अन्य धागा ...

Source control of Excel VBA code modules