visual programación programacion para lenguaje español comandos codigos avanzado excel vba excel-vba

programación - Control de fuente de los módulos de código de Excel VBA



vba excel 2016 pdf español (5)

Me gustaría poder controlar los módulos VBA de mi hoja de cálculo de Excel (actualmente usando Excel 2003 SP3) para poder compartir y administrar el código utilizado por un grupo de hojas de cálculo diferentes, y por lo tanto me gustaría volver a cargarlos desde archivos cuando se abre la hoja de cálculo.

Tengo un módulo llamado Loader.bas, que utilizo para hacer la mayor parte del trabajo de donkey (cargar y descargar cualquier otro módulo que sea necesario), y me gustaría poder cargarlo desde un archivo tan pronto como la hoja de cálculo se abre.

He adjuntado el siguiente código al evento Workbook_Open (en la clase ThisWorkbook).

Private Sub Workbook_Open() Call RemoveLoader Call LoadLoader End Sub

Donde RemoveLoader (también dentro de la clase ThisWorkbook) contiene el siguiente código:

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

Lo cual es probablemente un poco demasiado complicado y un poco tosco, ¡pero estoy intentando todo lo que puedo encontrar para cargar el módulo externo!

A menudo, cuando abro la hoja de cálculo, la función RemoveLoader encuentra que ya hay un módulo "Loader1" incluido en el proyecto VBA que no se puede eliminar, y también falla al cargar el nuevo módulo Loader desde el archivo.

¿Alguna idea si lo que estoy tratando de hacer es posible? Excel parece muy aficionado a agregar un 1 a estos nombres de módulos, ya sea al cargar o eliminar (no estoy seguro de cuál).


Hay una excelente solución al problema de control de versiones de vba aquí: https://github.com/hilkoc/vbaDeveloper

Lo bueno de esto es que exporta su código automáticamente , tan pronto como guarda su libro de trabajo. Además, cuando abres un libro de trabajo, importa el código.

No necesita ejecutar ningún script de compilación ni comandos de Maven, y no necesita realizar ningún cambio en sus libros de trabajo. Funciona para todos.

También solucionó el problema de importación donde los módulos como ModName se importan como ModName1 en un módulo duplicado. La importación funciona como debería, incluso cuando se hace varias veces.

Como beneficio adicional, viene con un formateador de código simple, que le permite formatear su código vba a medida que lo escribe dentro del Editor VBA.


Llevo meses trabajando exactamente en esto. Creo que lo descubrí.

Si el Proyecto VB está intentando eliminar un módulo que contiene algo en la pila de llamadas, demora la eliminación hasta que la pila de llamadas haga saltar el módulo que se está reemplazando.

Para evitar que un módulo esté en la pila de llamadas, inicie su código con Application.OnTime

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

Si se está recuperando automáticamente su código como lo soy yo, también tendrá que iniciar el código que sobrescribe el código de "llamada" con la misma estrategia.

Todavía no realicé pruebas exhaustivas, estoy en modo de celebración total, pero esto me acerca mucho al código de auto-recuperación del 99.9% en un archivo .xls independiente sin ningún otro truco.


Mira la página de VBAMaven. Tengo una solución de cosecha propia que utiliza los mismos conceptos. Tengo una biblioteca común con un montón de código fuente, una compilación de ant y un script VB de ''importación''. Ant controla la construcción, que toma un archivo de Excel en blanco e inserta el código necesario en él. @Mike es absolutamente correcto: cualquier definición de módulo duplicado tendrá automáticamente un número agregado al nombre del módulo. Además, las clases de módulos de clase (como en Hoja y ThisWorkbook) requieren un tratamiento especial. No puede crear esos módulos, debe leer el archivo de entrada y escribir el búfer en el módulo apropiado. Este es el script VB que actualmente uso para hacer esto. La sección que contiene @ texto delimitado (es decir, @build file @) son marcadores de posición: la compilación ant reemplaza estas etiquetas con contenido significativo. No es perfecto, pero funciona para mí.

'''' '' 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


No puedo dejar comentario para comentar.

Hay una excelente solución al problema de control de versiones de vba aquí: https://github.com/hilkoc/vbaDeveloper

Acerca de guardar VBAProjects personalizados utilizando este XLAM. Prueba esto en Build.bas:

''=============== Public Sub testImport() Dim proj_name As String Dim vbaProject As Object ''proj_name = "VBAProject" ''Set vbaProject = Application.VBE.VBProjects(proj_name) Set vbaProject = Application.VBE.ActiveVBProject proj_name = vbaProject.name Build.importVbaCode vbaProject End Sub ''=============== Public Sub testExport() Dim proj_name As String Dim vbaProject As Object ''proj_name = "VBAProject" ''Set vbaProject = Application.VBE.VBProjects(proj_name) Set vbaProject = Application.VBE.ActiveVBProject proj_name = vbaProject.name Build.exportVbaCode vbaProject End Sub ''===============

Esto exportará / importará el proyecto activo de VBA.


Por lo general, la cosa "Loader1" sucede cuando se le pide a Excel que importe un módulo y ya existe un módulo con el mismo nombre. Por lo tanto, si importa "Loader", luego vuelva a cargarlo y obtendrá "Loader1". Esto se debe a que Excel no sabe (o quizás simplemente no le importa) si realmente es lo mismo o una nueva parte de la funcionalidad que simplemente tiene el mismo nombre de módulo, por lo que lo importa de todos modos.

No puedo pensar en una solución perfecta, pero creo que me inclino a intentar poner la lógica de carga / descarga en un complemento. Esa cosa de Workbook_Open parece un poco vulnerable y tenerla en todos los workbooks va a ser una Gran dolor si el código alguna vez necesita cambiar (nunca digas nunca). La lógica XLA podría ser más compleja (más difícil de atrapar los eventos necesarios, por una cosa) pero al menos solo existirá en un lugar.