2012-01-13 101 views
11

プログラムでモジュールを削除してからファイルから再インポートする際に、Excelでいくつかの変わった問題が発生しました。基本的には、ファイルをあらかじめ定義されたフォルダにエクスポートし、必要に応じて再インポートすることになっているVersionControlというモジュールがあります。一部が重複(例えばのmymoduleとmymodule1)を持っている一方で、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 

これを実行した後、私はいくつかのモジュールはもう表示されないことに気付きました:これは(それは以下の通りであるとの問題を)再インポートするためのコードで。コードを進めていくうちに、Removeコールの後にいくつかのモジュールがまだ残っていることが明らかになり、それらはプロジェクト中に再インポートされることになります。場合によっては、これには末尾に1という接尾辞が付いていただけですが、元のコピーとコピーの両方があることもありました。

RemoveImportへのコールをフラッシュする方法はありますか?私はSave関数をそれぞれの後に呼び出すことを考えています、アプリケーションオブジェクトに1つがあれば、インポート中に何かが間違ってしまうと損失を引き起こす可能性があります。

アイデア?

編集:変更タグsynchronizationversion-control

+0

+1賢い少し方法です。私はこのような何かを自分で行うべきです。 –

+0

これは、[この質問](http://stackoverflow.com/questions/131605/best-way-to-do-version-control-for-ms-excel)ここでStackOverflowに触発されました - 私のバージョンは控えめですリメイク。 – CamilB

+1

私はこれのようなものを作成していませんが、私が試してみるものは次のようなものです:別のブック/アドインから呼び出します。最初にワークブックをバックアップし、一度にすべての削除を実行し、一度にすべてをインポートして保存します。また、Rob Boveyのコードクリーナーの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を与えます。まず、エラーの真の原因を明らかにしました。ライブアレイを編集します。それは私の非常に愚かな間違いだった、私は非常にそれらの配列がどのように働いたか分からなかった。彼らは実際には 'Collection'オブジェクトであることが分かります。第2に、あなたのコードは_さまざまな種類のmodules_を適切に処理し、適切なファイル拡張子で保存します。私はあまりにもずっと前にそれをするために私のコードを変更しました、それは本当に重要です。 +1することでコードを表示するので、人々は知っているでしょう。 – CamilB

1

ここに...私はこの奇妙な問題を回避することができましたが、私は真の解決策を見いだせませんでした。ここに私がしたことがあります。 (:それははほとんど働いスポイラー):質問を投稿した後

  1. 私の最初の試みは、このでした

    キープが輸入別に削除するが、同様の手順で。つまり、モジュール名のリスト(プレーンストリング)を格納するループ、モジュールを削除するモジュール、ファイルからモジュールをインポートするループ(前述のリストに格納されている名前に基づく)の3つのループがありました。 。

    問題:削除ループが終了したときに一部のモジュールがプロジェクトに残っていました。どうして?私は説明できません。私はこれをという愚かな問題点としてマークします。 1。私はその後、ループ内にすべてのモジュールRemoveコールを配置しようとしましたプロジェクトでそれが見つからないまで、その単一モジュールを削除しようとし続けました。これは特定のモジュールのために無限ループに陥ってしまった - 私はその特定のものについて何が特別なのか分からない。

    私は結局、モジュールが本当に削除されたのは、Excelが思考をクリアするためにある程度時間を割いた後であることだけです。これはApplication.Wait()では機能しませんでした。現在実行中のVBAコードは、実際にこれが発生するために終了する必要がありました。奇妙な。第二の回避策

  2. の試み(スポイラー:もう一度、それほとんどが働いていた):

    エクセルに削除した後、息をするために必要な時間を与えるために、私はなし(ボタンのクリックハンドラ内で削除ループを配置「削除するまで呼び出す」ループ)、および別のボタンのクリックハンドラにインポートするループが含まれます。もちろん、私はモジュール名のリストが必要なので、グローバルな文字列の配列にしました。これは、削除ループの前にクリックハンドラで作成され、インポートループによってアクセスされるはずです。うまくいきましたか?

    問題:上記の文字列配列は、インポートループが開始されたときに(他のクリックハンドラ内で)空でした。削除ループが終了したときは確かにそこにありました - 私はDebug.Printでそれを印刷しました。私はそれが削除によって割り当て解除されたと思います(??)。これはです。 2。モジュール名を含む文字列配列がなければ、インポートループは何もしなかったので、この回避策は失敗しました。

  3. 最終的な機能的な回避策。これは動作します。

    私は回避策番号2をとり、モジュール名を文字列配列に格納する代わりに、補助シートの行に格納しました(私はこのシートを "Devel"と呼んでいました)。

これはそれでした。誰でも説明できれば愚かな問題はありません。 1愚かな問題はありません。 2、私はあなたに頼んでいます。彼らはおそらく愚かではありません - 私はまだVBAの冒頭にいますが、私は他の(まじめで現代的な)言語でのプログラミングの知識があります。

説明するコードを追加できます。愚かな問題はありません。 2ですが、この回答は既に長いです。もし私がしたことがはっきりしていなかったら、私はここにそれを置くでしょう。

+1

純粋な推測:ExcelのVBAエンジンは、(おそらくコンパイルされた)VBA関数の途中でモジュールの削除を処理できないと思います。 (何をすれば、VBAを再コンパイルし、それをジョブに取り替えるか?余りにも難しいですか?)あなたがVBAの機能を使い切るまで待つことは意味があります。 –

+0

@ todda.speot.is:これは理にかなっていますが、ループ中にいくつかのモジュールが削除されました(私が知っているもの、少なくとも、他のものと変わったかもしれません。に)。 VBAは特定のモジュールでしか動かず、後で進めることを拒否しました。 – 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 

新しいモジュールに貼り付けるコード "バージョン管理"

0

名前変更、インポートおよび回避策を削除するには、私の場合には動作しませんでした。 Excelがコンパイルされたオブジェクトを.XLMSファイルに保存する可能性がありますが、このファイルを再オープンすると、これらのオブジェクトはThisWorkbook_open関数が実行される前にメモリに再ロードされます。これにより、特定のモジュールの名前変更(または削除)が失敗するか、遅延することになります(DoEvents呼び出しで強制的に実行しようとしても)。私が見つけた唯一の回避策は、.XLSバイナリ形式を使用することです。あいまいな理由(私はコンパイルされたオブジェクトがファイルにバンドルされていないと思う)のために、それは私のために働く。

インポートコードの実行時に使用中または参照中のモジュールを再インポートできないことを知っておく必要があります(名前変更はエラー32813で失敗する/モジュールの削除が遅れるあなたがインポートしようとするまで、あなたのモジュール名の最後に厄介な '1'を追加するまで)。しかし、他のどのモジュールであれ、動作するはずです。

すべてのソースコードを管理する必要がある場合は、スクリプトやツールを使用してワークブックを一から作成するか、より適切なプログラミング言語(つまり、 Officeスイートソフトウェアの中で);私はそれを試していないが、ここで見ることができる:Source control of Excel VBA code modules

1

私は今この問題を数日間苦労しています。私は、配列を使用していないが、これに似た粗バージョンコントロールシステムを構築しました。バージョン管理モジュールはWorkbook_Openにインポートされ、起動制御プロシージャが呼び出されて、バージョン管理モジュールにリストされているすべてのモジュールがインポートされます。既存のモジュールの削除が完了する前に、新しいモジュールをインポートするため、Excelは重複したバージョンコントロールモジュールの作成を開始したことを除いて、すべて機能します。以前のモジュールにDeleteを追加することでその問題を解決しました。問題は、同じ名前のプロシージャがまだ2つあることです。 Chip Pearsonにはプロシージャをプログラムで削除するコードがいくつかあるので、古いバージョンのコントロールモジュールからスタートアップコードを削除しました。それでも、起動手続きが呼ばれるまでに手続きが削除されていないという問題が発生しました。私は最終的に別のスタックオーバーフロースレッド上の解決策を見つけました。それはとても簡単なので、私は頭を壁に置きたいのです。

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

すべてが完全に機能しています。しかし、私はおそらく、モジュールの現在の冗長な名前変更を削除し、2番目の手順を削除し、これだけで私の元の問題を解決するかどうかを確認します。ここでは解決策と他のスレッド...いくつかの自家製のバージョン管理を行うための

Source control of Excel VBA code modules

関連する問題