2009-04-01 16 views
19

ExcelスプレッドシートのVBAモジュール(現在はExcel 2003 SP3を使用しています)をソースコードで管理したいので、さまざまなスプレッドシートで使用されているコードを共有して管理できます。したがって、私はスプレッドシートが開かれたときにファイルからそれらを再ロードしたいと思います。Excel VBAコードモジュールのソース管理

私はロバの仕事の大部分を行うために使用するLoader.basというモジュールを持っています(必要な他のモジュールのロードとアンロード) - それをロードすることができますスプレッドシートを開くとすぐにファイルを開きます。

次のコードを(ThisWorkbookクラスの)Workbook_Openイベントに添付しました。

Private Sub Workbook_Open() 
    Call RemoveLoader 
    Call LoadLoader 
End Sub 
(またはThisWorkbookクラス内)RemoveLoaderは、次のコードが含まれ

Private Sub RemoveLoader() 
    Dim y As Integer 
    Dim OldModules, NumModules As Integer 
    Dim CompName As String 

    With ThisWorkbook.VBProject 
     NumModules = ThisWorkbook.VBProject.VBComponents.Count 
     y = 1 
     While y <= NumModules 
      If .VBComponents.Item(y).Type = 1 Then 
       CompName = .VBComponents.Item(y).Name 
       If VBA.Strings.InStr(CompName, "Loader") > 0 Then 
        OldModules = ThisWorkbook.VBProject.VBComponents.Count 
        .VBComponents.Remove .VBComponents(CompName) 
        NumModules = ThisWorkbook.VBProject.VBComponents.Count 
        If OldModules - NumModules = 1 Then 
         y = 1 
        Else 
         MsgBox ("Failed to remove " & CompName & " module from VBA project") 
        End If 
       End If 
      End If 
      y = y + 1 
     Wend 
    End With 
End Sub 

おそらく少しovercomplicatedと多少の粗である - しかし、私はすべてをしようとしている私はそれを得るために見つけることができます外部モジュールをロードする!

多くの場合、スプレッドシートを開くと、RemoveLoader関数はVBAプロジェクトに既に含まれている「Loader1」モジュールが削除されていることを検出し、ファイルから新しいLoaderモジュールを読み込むこともできません。

私がしようとしていることがあれば、どんなアイデアも可能ですか? Excelはロードや削除のいずれかのときに、これらのモジュール名に1を追加するのが非常に好きです(どちらがわかりませんか)。

+0

私はいくつか考えて、これを思いついたことを追加するのを忘れてしまった:http://grumpyop.wordpress.com/2009/04/20/version-control-for-excel-workbooks-part-2/ 注コメントに挙げられているVBAMavenというものもあります。このコメントは、役に立つ外部サービスのように見えます。 –

答えて

4

VBAMavenページを見てください。私は同じコンセプトを使用した自国のソリューションを持っています。私は、一連のソースコード、antビルド、および「インポート」VBスクリプトを備えた共通のライブラリを持っています。 Antはビルドを制御します。ビルドは、空白のファイルを取り込み、必要なコードをそこにプッシュします。 @Mikeは絶対に正しいです - 重複したモジュール定義には自動的にモジュール名に数字が追加されます。また、クラスモジュール(SheetおよびThisWorkbookのような)クラスでは特別な扱いが必要です。これらのモジュールを作成することはできません。入力ファイルを読み込み、バッファを適切なモジュールに書き込む必要があります。これは私が現在これを行うために使用しているVBスクリプトです。 @区切りのテキスト(@build file @)を含むセクションはプレースホルダです。antのビルドはこれらのタグを意味のある内容に置き換えます。それは完璧ではないが、私のために働く。

'' 
' Imports VB Basic module and class files from the src folder 
' into the excel file stored in the bin folder. 
' 

Option Explicit 

Dim pFileSystem, pFolder, pPath 
Dim pShell 
Dim pApp, book 

Dim pFileName 

pFileName = "@build [email protected]" 

Set pFileSystem = CreateObject("Scripting.FileSystemObject") 

Set pShell = CreateObject("WScript.Shell") 
pPath = pShell.CurrentDirectory 

If IsExcelFile (pFileName) Then 
    Set pApp = WScript.CreateObject ("Excel.Application") 
    pApp.Visible = False 
    Set book = pApp.Workbooks.Open(pPath & "\build\" & pFileName) 
Else 
    Set pApp = WScript.CreateObject ("Word.Application") 
    pApp.Visible = False 
    Set book = pApp.Documents.Open(pPath & "\build\" & pFileName) 
End If 


'Include root source folder code if no args set 
If Wscript.Arguments.Count = 0 Then 
    Set pFolder = pFileSystem.GetFolder(pPath & "\src") 
    ImportFiles pFolder, book 
    ' 
    ' Get selected modules from the Common Library, if any 
    @common [email protected]@common [email protected] 
Else 
    'Add code from subdirectories of src . . . 
    If Wscript.Arguments(0) <> "" Then 
     Set pFolder = pFileSystem.GetFolder(pPath & "\src\" & Wscript.Arguments(0)) 
     ImportFiles pFolder, book 
    End If 
End If 





Set pFolder = Nothing 
Set pFileSystem = Nothing 
Set pShell = Nothing 


If IsExcelFile (pFileName) Then 
    pApp.ActiveWorkbook.Save 
Else 
    pApp.ActiveDocument.Save 
End If 

pApp.Quit 
Set book = Nothing 
Set pApp = Nothing 


'' Loops through all the .bas or .cls files in srcFolder 
' and calls InsertVBComponent to insert it into the workbook wb. 
' 
Sub ImportFiles(ByVal srcFolder, ByVal obj) 
    Dim fileCollection, pFile 
    Set fileCollection = srcFolder.Files 
    For Each pFile in fileCollection 
     If Right(pFile, 3) = "bas _ 
      Or Right(pFile, 3) = "cls _ 
      Or Right(pFile, 3) = "frm Then 
      InsertVBComponent obj, pFile 
     End If 
    Next 
    Set fileCollection = Nothing 
End Sub 


'' Inserts the contents of CompFileName as a new component in 
' a Workbook or Document object. 
' 
' If a class file begins with "Sheet", then the code is 
' copied into the appropriate code module 1 painful line at a time. 
' 
' CompFileName must be a valid VBA component (class or module) 
Sub InsertVBComponent(ByVal obj, ByVal CompFileName) 
    Dim t, mName 
    t = Split(CompFileName, "\") 
    mName = Split(t(UBound(t)), ".") 
    If IsSheetCodeModule(mName(0), CompFileName) = True Then 
     ImportCodeModule obj.VBProject.VBComponents(mName(0)).CodeModule, _ 
         CompFileName 
    Else 
     If Not obj Is Nothing Then 
      obj.VBProject.VBComponents.Import CompFileName 
     Else 
      WScript.Echo "Failed to import " & CompFileName 
     End If 
    End If 
End Sub 

'' 
' Imports the code in the file fName into the workbook object 
' referenced by mName. 
' @param target destination CodeModule object in the excel file 
' @param fName file system file containing code to be imported 
Sub ImportCodeModule (ByVal target, ByVal fName) 
    Dim shtModule, code, buf  
    Dim fso 
    Set fso = CreateObject("Scripting.FileSystemObject") 
    Const ForReading = 1, ForWriting = 2, ForAppending = 3 
    Const TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0 

    Set buf = fso.OpenTextFile(fName, ForReading, False, TristateUseDefault) 
    buf.SkipLine 
    code = buf.ReadAll 

    target.InsertLines 1, code 
    Set fso = Nothing 
End Sub 


'' 
' Returns true if the code module in the file fName 
' appears to be a code module for a worksheet. 
Function IsSheetCodeModule (ByVal mName, ByVal fName) 
    IsSheetCodeModule = False 
    If mName = "ThisWorkbook" Then 
     IsSheetCodeModule = False 
    ElseIf Left(mName, 5) = "Sheet" And _ 
     IsNumeric(Mid (mName, 6, 1)) And _ 
     Right(fName, 3) = "cls Then 
     IsSheetCodeModule = True 
    End If 
End Function 

'' 
' Returns true if fName has a xls file extension 
Function IsExcelFile (ByVal fName) 
    If Right(fName, 3) = "xls" Then 
     IsExcelFile = True 
    Else 
     IsExcelFile = False 
    End If 
End Function 
+0

ありがとうございます。私はあなたのスクリプトとvbaMavenを見てきました。そして、両方のメソッドがまったく新しいスプレッドシートを作成し、それらにマクロをコピーするように見えます。あれは正しいですか? スプレッドシートを開いたときにマクロをスプレッドシートに読み込む方法が見つかることを期待していましたが、動作させることができませんでした。おそらく私はExcelでできないことをしようとしています。私は今、COM経由でExcelにアクセスする外部スクリプトでマクロを置き換えることを検討し始めています。少なくとも、バージョン管理下にスクリプトを保存することができます。 –

+0

はい、基本的な考え方は、「空白の」スプレッドシートがコピーされ、コードがコピーされることです。私が開始する前に私がvbaMavenについて知っていたならば、私はそれに行ったかもしれません...私がこのルートに行くことを選んだのは、すべてのコード行をソース管理下に置くことができるからです。ワークブックに他のコードをロードするコードがあると、コードロードモジュールは他のすべてと同じレベルの制御下にはなく、Excelファイル内で折り返されます。 – DaveParillo

+0

また、Auto_OpenイベントまたはWorkbook_Openイベントでイベントコードを変更する際には、非常に注意が必要です。 Auto_Open関数内からThisWorkbookオブジェクトを編集しようとすると、Excelをクラッシュするための高速なパスになります。 – DaveParillo

2

通常、「Loader1」は、Excelにモジュールのインポートを要求され、同じ名前のモジュールが既に存在する場合に発生します。だから、 "Loader"を読み込んだら、もう一度ロードすれば、 "Loader1"が得られます。これはExcelが実際に同じものか、まったく同じ機能を持つ新しい機能のチャンクが同じモジュール名を持っているとExcelが知らない(または多分気にしない)ため、とにかくそれをインポートするからです。

私は完璧な解決策は考えられませんが、アドインにロード/アンロードロジックを置くことを考えています。つまり、Workbook_Openの問題は少し脆弱で、すべてのブックでコードを変更する必要がある場合(決して決して言わないでください)、大きな苦痛になります。 XLAロジックは複雑で(必要なイベントをトラップするのは難しい)かもしれませんが、少なくとも1つの場所にしか存在しません。

+0

答えはマイクに感謝します。あなたは正しいかもしれません、AddInがそれを行う唯一の方法かもしれません。 SubVersionにマクロを保存したいので、私はAddInソリューションを避けようとしていました。バイナリXLAファイルではなく、マージして簡単に比較できるようにテキストファイルとして保存したいと思っています。 –

+0

小さなアドインを使ってモジュールを読み込むことはできますか? –

+0

私はこの状況でどのようにアドインを使用するのが助けになるのか分かりません。非バイナリVBAモジュールをExcelに読み込むには、Workbookを開いたときに何らかのコードを実行する必要があります.AdderInにLoaderコードを保存すると問題の本質が変わるとは思いません。 –

3

私はこれを数か月間続けています。私はそれを理解したと思う。

VBプロジェクトが呼び出しスタック内に何かを含むモジュールを削除しようとすると、呼び出しスタックが置き換えられるモジュールをポップするまで、削除が遅延します。

モジュールがコールスタックに入らないようにするには、アプリケーションでコードを起動します。OnTime

Private Sub Workbook_Open() 

    'WAS: module_library (1) 

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

End Sub 

私はのようにあなたのコードを自己修復している場合、あなたはまた、その同じ戦略で「呼び出し」コードを上書きしてコードを起動する必要があります。

私はまだ広範なテストを実行していなかった、私は、全体のお祝いモードでですが、これは、スタンドアロンの.xlsファイル内の簡単な99.9%の自己回復コードに私は非常に近づいた他のトリックなし

+1

ありがとうございます。これはほぼ4年後の私の正確な問題でした。 – enderland

12

ありhttps://github.com/hilkoc/vbaDeveloper

これは素晴らしいことですが、にエクスポートすると、ブックを保存するとすぐにコードがエクスポートされます。また、ブックを開くと、コードがインポートされます。

ビルドスクリプトやmavenコマンドを実行する必要はなく、ワークブックを変更する必要はありません。それはすべての人のために働く。

また、ModNameなどのモジュールをModName1として複製モジュールにインポートする際のインポートの問題も解決しました。インポートは、複数回実行しても必要なときに機能します。

ボーナスとして、簡単なコードフォーマッタが付属しています。これにより、VBAコードをVBAエディタで書いているときにフォーマットすることができます。

+1

私はちょうど全体の構築プロセスを経て、2つの問題がありました。あなたが把握しているかどうか聞いてみたいと思います: 1. refreshMenuを実行しない限り、アドインリボンはvbaDeveloperメニューを表示していません)メソッドを使用します 2.コードのエクスポートは自動的に行われず、手動でアクションをトリガーする必要があります –

+3

1メニューはworkbook_openイベントによって作成されます。ビルド作業をするには、アドインを保存して閉じます。次に、手動で無効にしない限り、workbook_openイベントが再び開かれます。Application.EnableEvents = False 2自動インポート/エクスポートは、デフォルトでは無効になっています。https://github.com/hilkoc/vbaDeveloper/issues/8 – CodeKid

関連する問題