2012-01-13 123 views
11

我在Excel中遇到一些奇怪的怪癖,同時以編程方式刪除模塊,然後從文件重新導入它們。基本上,我有一個名爲VersionControl的模塊,它應該將我的文件導出到預定義的文件夾,並按需重新導入它們。這是(與它下面描述的問題)重新導入代碼:使用VBA在Excel中刷新VBProject.VBComponents

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 

運行在此之後,我注意到,一些模塊不會再出現,而一些有重複(如mymodule中和mymodule1) 。在逐步完成代碼的過程中,很明顯有些模塊仍然在Remove調用之後徘徊,並且仍然在項目中時重新導入它們。有時候,這隻會導致模塊後綴爲1,但有時我有原始文件和副本。

有沒有辦法將呼叫刷新到RemoveImport以便它們適用於自己?如果在Application對象中有一個函數,我打算在每個函數後調用Save函數,但如果導入過程中出現問題,這可能會導致損失。

想法?

編輯:更改標籤synchronizationversion-control

+0

+1聰明一點的方式做一些自制的版本控制。我應該自己做這樣的事情。 – 2012-01-13 19:32:47

+0

這是StackOverflow中的[這個問題](http://stackoverflow.com/questions/131605/best-way-to-do-version-control-for-ms-excel)的啓發 - 我的版本只是一個謙虛重拍。 – CamilB 2012-01-13 19:36:35

+1

我還沒有創建過這樣的東西,但我會嘗試的是:從另一個工作簿/插件調用;先備份工作簿,立即完成所有的刪除,保存,一次導入全部。您也可以使用Rob Bovey的Code Cleaner的COM版本。您可以設置對其的引用並訪問導入,導出和其他功能。我會很樂意看到你發現的。 – 2012-01-13 20:51:08

回答

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。首先,您揭示了錯誤的真正原因:編輯活動數組;這是我的一個非常愚蠢的錯誤,我不太確定那些陣列是如何工作的。原來他們實際上是'Collection'對象。其次,您的代碼正確處理_different類型的模塊_並將其保存爲正確的文件擴展名。我前一段時間改變了我的代碼,這確實很重要; +1顯示你的代碼,這樣做,所以人們會知道。 – CamilB 2012-11-14 18:40:02

1

OP在這裏...我設法解決這個奇怪的問題,但我還沒有找到一個真正的解決方案。這就是我所做的。

  1. 我張貼的問題後,第一次嘗試是這樣的(擾流板:它幾乎工作):

    保持從進口去除的,但是在相同的過程。這意味着我有3個循環 - 一個用於存儲模塊名稱列表(作爲普通字符串),另一個用於刪除模塊,另一個用於從文件中導入模塊(基於存儲在上述列表中的名稱) 。

    問題:當刪除循環結束時,一些模塊仍在項目中。爲什麼?我無法解釋。我會把這個標記爲愚蠢的問題沒有。 1。然後,我嘗試將每個模塊Remove調用放置在循環內,該循環不斷嘗試刪除該單個模塊,直到在項目中找不到它爲止。對於某個模塊來說,它陷入了一個無限循環 - 我無法分辨出特定模塊的特殊之處。

    我最終發現,在Excel找到一段時間來清除它的想法後,模塊才真正被刪除。這不適用於Application.Wait()。當前正在運行的VBA代碼實際上需要結束纔會發生。奇怪的。

  2. 二變通嘗試(擾流板:再次,它幾乎工作):

    爲了讓Excel中所需的時間清除後呼吸,我放在一個按鈕單擊處理程序中移除環(不「調用Remove直到它消失」循環),並在另一個按鈕的click處理程序中導入循環。當然,我需要模塊名稱列表,所以我將它作爲全局字符串數組。它是在刪除循環之前的點擊處理程序中創建的,並且應該由導入循環訪問。應該工作吧?

    問題:上述字符串數組在導入循環啓動時(在其他點擊處理程序中)爲空。當刪除循環結束時,它確實存在 - 我用Debug.Print打印它。我想它被清除(??)分配。這將是愚蠢的問題沒有。 2。如果沒有包含模塊名稱的字符串數組,導入循環無效,所以此解決方法失敗。

  3. 最終的功能解決方法。這一個工程。

    我把解決方法編號爲2,而不是將模塊名稱存儲在一個字符串數組中,而是將它們存儲在一張輔助工作表(我將這個工作表稱爲「Devel」)中。

就是這樣。如果任何人都可以解釋愚蠢的問題沒有。 1愚蠢的問題沒有。 2,我求求你,這樣做。他們可能並不那麼愚蠢 - 我仍然以VBA開頭,但我對其他(理智和現代)語言的編程有深入的瞭解。

我可以添加代碼來說明愚蠢的問題沒有。 2,但這個答案已經很長了。如果我所做的不清楚,我會把它放在這裏。

+1

純粹的猜測:我猜想Excel的VBA引擎不能處理(大概是已編譯的)VBA函數中間的removedmodules。 (它是做什麼的,重新編譯VBA並替換它來完成這個工作?太難了。)等待,直到你離開VBA函數對我來說是有意義的。 – 2012-01-14 10:46:27

+0

@ todda.speot.is:這是有道理的,但有些模塊在循環過程中被刪除了(我知道的一個模塊,至少在其他模塊中可能表現得很怪異,然後纔開始理解發生了什麼事上)。 VBA只停留在一個特定的模塊,並拒絕繼續進行。 – CamilB 2012-01-14 10:53:36

+1

更多推測:一些模塊有全局變量,其他模塊沒有。沒有的人可以很好地被刪除,因爲沒有必要重新編譯其他模塊。那些具有全局變量(或者可能是從你的函數運行的模塊引用的)在執行結束之前不能被刪除。 – 2012-01-15 03:35:36

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 

代碼在一個新的模塊 「版本控制」

0

重命名,導入粘貼和刪除的解決方法在我的情況沒有工作。看來(但這純粹是推測)Excel可能會將編譯對象保存在其.XLMS文件中,並且當重新打開此文件時,在ThisWorkbook_open函數發生之前,這些對象將重新加載到內存中。這導致某些模塊的重命名(或刪除)失敗或被延遲(即使嘗試強制使用DoEvents調用)。我找到的唯一解決方法是使用.XLS二進制格式。由於一些不明確的原因(我懷疑編譯的對象沒有捆綁在文件中),它對我很有用。

您必須知道,在導入代碼運行時,您將無法重新導入任何正在使用或引用的模塊(重命名將失敗,並顯示錯誤32813 /移除模塊將被延遲直到嘗試導入後,在模塊名稱的末尾添加令人討厭的'1')。但對於任何其他模塊,它應該工作。

如果您需要管理所有源代碼,更好的解決方案是使用某些腳本或工具從頭開始構建您的工作簿,或切換到更適合的編程語言(即不能生存的程序語言在Office套件軟件中;)我還沒有嘗試過,但你可以看看這裏:Source control of Excel VBA code modules

1

我一直在努力解決這個問題好幾天了。我構建了一個類似於此的粗版本控制系統,儘管不使用數組。版本控制模塊導入Workbook_Open,然後調用啓動程序導入版本控制模塊中列出的所有模塊。除了Excel開始創建重複版本控制模塊外,一切都很好,因爲它會在刪除現有模塊之前導入新模塊。我通過將Delete添加到前一個模塊來解決這個問題。問題在於,仍然存在兩個具有相同名稱的程序。 Chip Pearson有一些以編程方式刪除程序的代碼,因此我從舊版本控制模塊中刪除了啓動代碼。不過,我遇到了一個問題,即在啓動過程被調用之前該過程並未被刪除。我終於在另一個堆棧溢出線程上找到了一個解決方案,它非常簡單,它使我想讓我的頭穿過牆壁。我所要做的只是改變我使用啓動程序的方式

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

現在一切正常。儘管如此,我可能會回過頭去刪除模塊現在多餘的重命名並刪除第二個過程,看看這是否能解決我原來的問題。這裏是其他線程與解決方案...

Source control of Excel VBA code modules