Промывка изменений, внесенных в VBProject.Компоненты VB в Excel с использованием VBA
я испытывала какие-то странные закидоны в 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
вызов, и они могут быть повторно перенесены в то время как все еще в проекте. Иногда, это привело только к имеющим модуль suffixed с 1
, но иногда у меня был и оригинал и копия.
есть ли способ сбросить вызовы Remove
и Import
чтобы они сами? Я думаю позвонить Save
функция после каждого, если есть один в объекте приложения, хотя это может привести к потерям, если что-то пойдет не так во время импорта.
идеи?
Edit: изменен тег synchronization
to version-control
.
5 ответов
это живой массив, вы добавляете и удаляете элементы во время итерации, тем самым изменяя номера индексов. Попробуйте обработать массив назад. Вот мое решение без какой-либо обработки ошибок:
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 здесь... Мне удалось обойти эту странную проблему, но я не нашел истинного решения. Вот что я сделал.
-
моя первая попытка после публикации вопроса была такой (спойлер: it почти работала):
продолжайте удалять отдельно от импорта, но в той же процедуре. Это означает, что у меня было 3 цикла - один для хранения списка имен модулей (как простых строк), другой для удаления модулей, а другой для импорта модулей из файлы (на основе имен, которые были сохранены в вышеупомянутом списке).
проблема: некоторые модули все еще находились в проекте, когда цикл удаления закончился. Почему? Я не могу объяснить. Я отмечу это как глупая проблема нет. 1. Затем я попытался разместить
Remove
вызов для каждого модуля внутри цикла это продолжало пытаться удалить этот единственный модуль, пока он не смог найти его в проекте. Это застряло в бесконечном цикле для определенного модуля - я не могу сказать что в нем такого особенного?в конце концов я понял, что модули были действительно удалены только после того, как Excel найдет время, чтобы очистить свои мысли. Это не сработало с приложением.Ждать.)( В настоящее время запущенный код VBA фактически должен был закончиться, чтобы это произошло. Странный.
-
вторая попытка обхода (спойлер: опять же, это почти работала):
чтобы дать Excel необходимое время для дыхания после удаления, I разместил цикл удаления внутри обработчика нажатия кнопки (без цикла "вызов удалить, пока он не исчезнет") и цикл импорта в обработчике нажатия другой кнопки. Конечно, мне нужен был список имен модулей, поэтому я сделал его глобальным массивом строк. Он был создан в обработчике click перед циклом удаления, и к нему должен был получить доступ цикл импорта. Должно было сработать, верно?
проблема: вышеупомянутый массив строк был пуст, когда цикл импорта запущен (внутри другого обработчика щелчка). Он определенно был там, когда цикл удаления закончился - я напечатал его с Debug.Печать. Я предполагаю, что он был де-выделен удалениями (??). Это было бы глупая проблема нет. 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 к предыдущему модулю. Проблема тогда заключалась в том, что все еще было две процедуры с тем же именем. У чипа Пирсона есть код для программного удаления процедуры, поэтому я удалил код запуска из старого модуля управления версиями. Тем не менее, я столкнулся с проблемой, когда процедура не была удалена к моменту вызова процедуры запуска. Я, наконец, нашел решение в другом потоке переполнения стека, который настолько прост, что мне хочется положить голову через стену. Все, что мне нужно было сделать, это изменить способ вызова процедуры запуска с помощью
Application.OnTime Now + TimeValue("00:00:01"), "StartUp"
теперь все работает отлично. Хотя, я, вероятно, вернусь и удалю теперь избыточное переименование модуля и удаление второй процедуры и посмотрю, решит ли это только мою первоначальную проблему. Вот другая нить с решением...
обходной путь переименования, импорта и удаления не работал в моем случае. Кажется (но это чистая спекуляция), что Excel может сохранить скомпилированные объекты в своем .XLMS-файл, и при повторном открытии этого файла эти объекты перезагружаются в памяти до возникновения функции ThisWorkbook_open. И это приводит к переименованию (или удалению) определенных модулей к сбою или задержке (даже при попытке заставить его с помощью вызова DoEvents). Единственное решение, которое я нашел, - использовать .Двоичный формат XLS. Для по какой-то неясной причине (я подозреваю, что скомпилированные объекты не включены в файл), это работает для меня.
вы должны знать, что вы не сможете повторно импортировать какой-либо модуль, используемый/используемый или на который ссылаются во время выполнения кода импорта (переименование завершится ошибкой 32813/удаление модуля будет отложено до тех пор, пока вы не попытаетесь импортировать, добавив раздражающие " 1 " в конце имен модулей). Но для любого другого модуля, он должен работать.
Если весь ваш источник код должен управляться, лучшим решением было бы "построить" вашу книгу с нуля с помощью какого-либо скрипта или инструмента или переключиться на более подходящий язык программирования (т. е. тот, который не живет внутри программного обеспечения Office suite ;) я не пробовал, но вы можете посмотреть здесь: управление версиями модулей кода Excel VBA.