我一直在Excel中遇到一些奇怪的怪癖,同时以编程方式删除模块,然后从文件中重新导入它们。基本上,我有一个名为VersionControl的模块,它应该将我的文件导出到预定义的文件夹,并根据需要重新导入它们。这是重新导入的代码(下面介绍了它的问题):
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
,但有时我同时拥有原件和副本。
有没有办法刷新来电 Remove
和 Import
所以他们适用自己?我想打个电话 Save
每个之后的函数,如果Application对象中有一个,虽然如果在导入过程中出现问题,这可能会导致丢失。
想法?
编辑:更改标签 synchronization
至 version-control
。
这是一个实时数组,您在迭代期间添加和删除项目,从而更改索引号。尝试向后处理数组。这是我的解决方案没有任何错误处理:
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
OP在这里...我设法解决这个奇怪的问题,但我还没有找到真正的解决方案。这就是我做的。
在发布问题后我的第一次尝试就是这个(剧透:它 几乎 工作):
继续从导入中删除,但是在相同的过程中。这意味着我有3个循环 - 一个用于存储模块名称列表(作为普通字符串),另一个用于删除模块,另一个用于从文件导入模块(基于存储在上述列表中的名称) 。
问题:当删除循环结束时,某些模块仍在项目中。为什么?我无法解释。我会把它标记为 愚蠢的问题没有。 1。然后我尝试放置 Remove
呼吁每个模块 在循环内 一直试图删除该单个模块,直到它无法在项目中找到它。对于某个模块,它陷入了无限循环 - 我不知道那个特定模块有什么特别之处。
我最终发现,在Excel找到一些时间来清除它的想法之后,这些模块才被真正删除。这不适用于Application.Wait()。当前运行的VBA代码实际上需要结束才能实现。奇怪的。
第二次解决尝试(剧透:再次,它 几乎 工作):
为了给Excel删除后需要的呼吸时间,我将删除循环放在一个按钮单击处理程序中(没有“调用删除直到它消失”循环),并且导入循环在另一个按钮的单击处理程序中。当然,我需要模块名称列表,所以我把它作为一个全局字符串数组。它是在删除循环之前在单击处理程序中创建的,它应该由导入循环访问。应该有用,对吗?
问题:当导入循环开始时(在其他单击处理程序内),上述字符串数组为空。当删除循环结束时肯定存在 - 我用Debug.Print打印它。我猜它是由删除(??)取消分配的。这将是 愚蠢的问题没有。 2。如果没有包含模块名称的字符串数组,则导入循环不会执行任何操作,因此此解决方法失败。
最终的功能性解决方案。这个有效。
我使用了2号解决方案,而不是将模块名称存储在字符串数组中,而是将它们存储在辅助工作表的一行中(我称之为“Devel”)。
就是这样。如果有人可以解释 愚蠢的问题没有。 1 和 愚蠢的问题没有。 2我求求你这样做。他们可能不是那么愚蠢 - 我仍然处于VBA的开端,但我对其他(理智和现代)语言的编程有扎实的了解。
我可以添加代码来说明 愚蠢的问题没有。 2,但这个答案已经很久了。如果我做的不清楚,我会把它放在这里。
为避免导入时出现重复,我使用以下策略修改了脚本:
我在导入过程中没有任何重复。
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”中的代码
我几天来一直在努力解决这个问题。我构建了一个与此类似的原始版本控制系统,但不使用数组。在Workbook_Open上导入版本控制模块,然后调用启动过程以导入版本控制模块中列出的所有模块。一切都很好,除了Excel开始创建重复版本控制模块,因为它会在删除现有模块之前导入新模块。我通过将Delete添加到上一个模块来解决这个问题。那么问题是仍然有两个具有相同名称的程序。 Chip Pearson有一些用于以编程方式删除过程的代码,因此我从旧版本控制模块中删除了启动代码。尽管如此,我遇到的问题是,在调用启动过程时,该过程尚未删除。我终于找到了另一个堆栈溢出线程的解决方案,这很简单,它让我想把头穿过墙。我所要做的就是通过使用改变我调用启动过程的方式
Application.OnTime Now + TimeValue("00:00:01"), "StartUp"
现在一切都很完美。虽然,我可能会回去删除现在冗余的模块重命名并删除第二个程序,看看这是否能解决我原来的问题。这是解决方案的另一个主题......
Excel VBA代码模块的源代码控制
重命名,导入和删除变通方法在我的情况下不起作用。似乎(但这是纯猜测)Excel可能会将编译对象保存在其.XLMS文件中,并且当重新打开此文件时,这些对象将在ThisWorkbook_open函数发生之前重新加载到内存中。这导致某些模块的重命名(或删除)失败或被延迟(即使在尝试使用DoEvents调用强制它时)。我找到的唯一解决方法是使用.XLS二进制格式。由于一些不明原因(我怀疑编译的对象没有捆绑在文件中),它对我有用。
您必须知道在导入代码运行时您将无法重新导入正在/已经使用或引用的任何模块(重命名将失败,错误32813 /模块的移除将延迟到您将尝试导入,在模块名称的末尾添加恼人的'1'。但对于任何其他模块,它应该工作。
如果需要管理所有源代码,更好的解决方案是使用某些脚本或工具从头开始“构建”您的工作簿,或者切换到更适合的编程语言(即不在Office套件中生成的语言)软件;)我没试过,但你可以看看这里: Excel VBA代码模块的源代码控制。