vaciar portapapeles excel vba memory version-control flush

excel - portapapeles - clipboard clear vba



Cambios de limpieza realizados a VBProject.VBComponents en Excel usando VBA (5)

OP aquí ... Logré solucionar este extraño problema, pero no encontré una solución verdadera. Esto es lo que hice.

  1. Mi primer intento después de publicar la pregunta fue esto (spoiler: casi funcionó):

    Seguir eliminando por separado de la importación, pero en el mismo procedimiento. Esto significa que tenía 3 bucles: uno para almacenar una lista de los nombres de los módulos (como cadenas simples), otro para eliminar los módulos y otro para importar los módulos de los archivos (según los nombres que se almacenaron en la lista mencionada anteriormente). .

    El problema: algunos módulos todavía estaban en el proyecto cuando finalizó el ciclo de eliminación. ¿Por qué? No puedo explicar. Voy a marcar esto como un estúpido problema no. 1 . Luego intenté colocar la llamada Remove para cada módulo dentro de un bucle que intentaba eliminar ese único módulo hasta que no pudo encontrarlo en el proyecto. Esto se quedó atascado en un ciclo infinito para un determinado módulo; no puedo decir qué tiene de especial ese tema en particular.

    Finalmente me di cuenta de que los módulos solo se eliminaban realmente una vez que Excel encontraba tiempo para aclarar sus ideas. Esto no funcionó con Application.Wait (). El código VBA actualmente en ejecución realmente necesitaba terminar para que esto suceda. Extraño.

  2. Segundo intento de reparación (spoiler: una vez más, casi funcionó):

    Para dar a Excel el tiempo necesario para respirar después de las extracciones, coloqué el bucle de eliminación dentro de un controlador de clic de botón (sin el bucle de "llamar hasta que desapareció") y el bucle de importación en el controlador de clic de otro botón. Por supuesto, necesitaba la lista de nombres de módulos, así que la convertí en una matriz global de cadenas. Se creó en el controlador de clics, antes del ciclo de eliminación, y se suponía que el bucle de importación debía acceder a él. Debería haber funcionado, ¿verdad?

    El problema: la matriz de cadenas antes mencionada estaba vacía cuando se inició el ciclo de importación (dentro del otro controlador de clics). Definitivamente estaba allí cuando terminó el ciclo de eliminación, lo imprimí con Debug.Print. Supongo que fue desasignado por las eliminaciones (??). Este sería un estúpido problema, no. 2 . Sin la matriz de cadenas que contenía los nombres de los módulos, el ciclo de importación no hizo nada, por lo que falló este proceso.

  3. Solución final y funcional. Este funciona

    Tomé el número 2 de Work-around y, en lugar de almacenar los nombres de los módulos en una matriz de cadenas, los almacené en una fila de una hoja auxiliar (llamé a esta hoja "Devel").

Esto fue. Si alguien puede explicar el problema estúpido no. 1 y estúpido problema no. 2 , te lo ruego, hazlo. Probablemente no sean tan estúpidos. Todavía estoy empezando con VBA, pero tengo un conocimiento sólido de la programación en otros idiomas (sensatos y modernos).

Podría agregar el código para ilustrar el estúpido problema no. 2 , pero esta respuesta ya es larga. Si lo que hice no está claro, lo ubicaré aquí.

He estado experimentando algunos caprichos extraños en Excel mientras desinstalo módulos de forma programática y luego los vuelvo a importar de los archivos. Básicamente, tengo un módulo llamado VersionControl que se supone que debe exportar mis archivos a una carpeta predefinida y volver a importarlos a pedido. Este es el código para volver a importar (el problema con él se describe a continuación):

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

Después de ejecutar esto, me he dado cuenta de que algunos módulos ya no aparecen, mientras que otros tienen duplicados (por ejemplo, mymodule y mymodule1). Al recorrer el código, se hizo obvio que algunos módulos aún permanecen después de la llamada Remove , y se vuelven a importar mientras están en el proyecto. A veces, esto solo daba como resultado que el módulo tuviera el sufijo 1 , pero a veces tenía tanto el original como la copia.

¿Hay alguna manera de eliminar las llamadas a Remove e Import para que se apliquen solos? Estoy pensando en llamar a una función Save después de cada una, si hay una en el objeto Aplicación, aunque esto puede causar pérdidas si las cosas van mal durante la importación.

Ideas?

Editar: cambió la synchronization etiquetas al version-control .


Esta es una matriz en vivo, está agregando y eliminando elementos durante la iteración, cambiando así los números de índice. Intenta procesar la matriz al revés. Aquí está mi solución sin ningún tipo de manejo de errores:

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


Para evitar duplicados al importar, modifiqué el script con la siguiente estrategia:

  • Renombrar módulo existente
  • Módulo de importación
  • Eliminar módulo renombrado

No tengo más duplicados durante la importación.

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

Código para pegar en un nuevo módulo "VersionControl"


El cambio de nombre, la importación y la solución alternativa no funcionaron en mi caso. Parece (pero esto es pura especulación) que Excel podría guardar los objetos compilados en su archivo .XLMS, y cuando este archivo se vuelve a abrir, estos objetos se vuelven a cargar en la memoria antes de que se produzca la función ThisWorkbook_open. Y esto lleva a que el cambio de nombre (o eliminación) de ciertos módulos falle o se retrase (incluso cuando intenta forzarlo con la llamada DoEvents). La única solución alternativa que encontré es usar el formato binario .XLS. Por alguna razón oscura (sospecho que los objetos compilados no están incluidos en el archivo), funciona para mí.

Debe saber que no podrá volver a importar ningún módulo que esté / haya sido utilizado o referenciado en el momento en que se ejecute el código de importación (el cambio de nombre fallará con el error 32813 / la eliminación del módulo se retrasará hasta que lo haga) intente importar, agregando ''1''s molestos al final de los nombres de sus módulos). Pero para cualquier otro módulo, debería funcionar.

Si necesita administrar todo su código fuente, una mejor solución sería "crear" su libro de trabajo desde cero utilizando algún script o herramienta, o cambiar a un lenguaje de programación mejor adaptado (es decir, uno que no viva dentro de un paquete de Office software;) No lo he probado, pero podría mirar aquí: control de código fuente de los módulos de código VBA de Excel .


He estado luchando con este problema desde hace días. Construí un sistema de control de versión crudo similar a esto, aunque no usé matrices. El módulo de control de la versión se importa en Workbook_Open y luego se llama al procedimiento de inicio para importar todos los módulos enumerados en el módulo de control de la versión. Todo funciona de maravilla, excepto que Excel comenzó a crear módulos de control de versiones duplicados porque importaría el nuevo módulo antes de que terminara la eliminación del existente. Trabajé alrededor al agregar Delete al módulo anterior. El problema entonces era que todavía había dos procedimientos con el mismo nombre. Chip Pearson tiene algún código para eliminar un procedimiento mediante programación, así que eliminé el código de inicio del módulo de control de la versión anterior. Aún así, me encontré con un problema donde el procedimiento no se había eliminado para cuando se llamó al procedimiento de inicio. Finalmente encontré una solución en otro hilo de desbordamiento de pila que es tan simple que me da ganas de meter la cabeza en la pared. Todo lo que tenía que hacer era cambiar la forma en que llamo a mi procedimiento de inicio mediante el uso de

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

Todo funciona perfectamente ahora. Sin embargo, probablemente regrese y elimine el cambio de nombre ahora redundante del módulo y elimine el segundo procedimiento y vea si esto solo resuelve mi problema original. Aquí está el otro hilo con la solución ...

Control de fuente de los módulos de código de Excel VBA