Excel VBA代码模块的源代码控制

我希望能够控制我的Excel电子表格的VBA模块(目前使用Excel 2003 SP3),以便我可以共享和pipe理一堆不同的电子表格所使用的代码 – 因此,我想重新加载它们从电子表格打开时的文件。

我有一个名为Loader.bas的模块,用于完成大部分驴工作(加载和卸载任何其他需要的模块) – 我希望能够尽快从文件加载它随着电子表格打开。

我已将以下代码附加到Workbook_Open事件(在ThisWorkbook类中)。

Private Sub Workbook_Open() Call RemoveLoader Call LoadLoader End Sub 

RemoveLoader(也在ThisWorkbook类中)包含以下代码:

 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 

这可能有点过于复杂和粗略 – 但我正在尝试一切,我可以find它来加载外部模块!

通常,当我打开电子表格时,RemoveLoader函数发现VBA项目中已经包含一个“Loader1”模块,它无法删除,也无法从文件中加载新的Loader模块。

任何想法,如果我想要做的是可能的? Excel似乎非常喜欢追加1到这些模块名称 – 无论是加载或删除(我不知道是哪一个)。

看看VBAMaven页面。 我有一个使用相同概念的本土解决scheme。 我有一个共同的库,有一堆源代码,一个ant构build和一个“导入”VB脚本。 Ant控制构build,这需要一个空白的Excel文件,并将所需的代码放入其中。 @Mike绝对正确 – 任何重复的模块定义都会自动将一个数字附加到模块名称上。 另外,类模块(如Sheet和ThisWorkbook类)需要特殊的处理。 您不能创build这些模块,您必须读取input文件并将缓冲区写入相应的模块。 这是我目前用来做这个的VB脚本。 包含@分隔文本(即@build file @)的部分是占位符 – ant构build用有意义的内容replace这些标记。 这不是完美的,但为我工作。

 '' ' 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 file@" 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 path@@common file@ 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 

有一个很好的解决scheme,在这里vba版本控制问题: https : //github.com/hilkoc/vbaDeveloper

关于这一点的好处在于,只要保存工作簿,它就会自动导出代码。 另外,当您打开工作簿时,它会导入代码。

您不需要运行任何构build脚本或maven命令,也不需要对工作簿进行任何更改。 它适用于所有人。

它还解决了将模块(如ModName)作为ModName1导入到重复模块中的导入问题。 导入工作,因为它应该,即使做多次。

作为奖励,它附带了一个简单的代码格式化程序,它允许您在VBA编辑器中编写VBA代码的格式。

几个月来我一直在研究这个。 我想我知道了。

如果VB工程正在尝试删除包含调用堆栈中的某个模块,则会延迟删除,直到调用堆栈popup被replace的模块。

要避免模块在调用堆栈中,请使用Application.OnTime启动代码

 Private Sub Workbook_Open() 'WAS: module_library (1) Application.OnTime (Now + TimeValue("00:00:01")), "load_library_kicker_firstiter" End Sub 

如果您像我一样自我修复您的代码,那么您还必须启动代码,以相同的策略覆盖“调用”代码。

我还没有进行广泛的testing,我处于完全庆祝模式,但是这使我非常接近独立的.xls文件中直接的99.9%自我修复代码,没有任何其他技巧

当Excel被要求导入一个模块并且一个模块已经存在同名时,通常会发生“Loader1”事情。 所以,如果你导入“Loader”,然后再次加载,你会得到“Loader1”。 这是因为Excel不知道(或者只是不在意),如果它真的是一样的东西,或者刚刚发生的一个新的function块有相同的模块名称,所以它无论如何导入它。

我想不出一个完美的解决scheme,但我想我会倾向于尝试将加载/卸载逻辑放入加载项中 – Workbook_Open看起来有点脆弱,并且在所有的工作簿中都将成为巨大的痛苦,如果代码需要改变(永远不要说永远不会)。 XLA逻辑可能更为复杂(比较复杂),但至less它只能存在于一个地方。