variable - Establecer de manera programada la ruta de búsqueda de DLL en la macro de VBA
vba excel (4)
Hay otra solución realmente muy fea, pero este blogger lo descubrió, y no puedo entender de otra manera:
http://blogs.msdn.com/pranavwagh/archive/2006/08/30/How-To-Load-Win32-dlls-Dynamically-In-VBA.aspx
Básicamente, usted escribe un procedimiento que crea un módulo de código en VBA durante el tiempo de ejecución. Este módulo debe crear una referencia al dll y debe crear una función ficticia (o procedimiento) como parte de este módulo que llama al dll. Luego, desde su código, usa Application.Run (dummyfunction (), arg1, arg2 ...). Esto es necesario porque, de lo contrario, el proyecto no compilará porque la función ficticia aún no es una función.
Notarás en su código que usa InputBox () para obtener la ubicación del .dll, pero obviamente podrías obtener la ubicación de un rango en la hoja de cálculo. El siguiente fragmento de código puede ser útil.
Dim cm As CodeModule
Dim vbc As VBComponent
Set cm = Application.VBE.ActiveVBProject.VBComponents.Add(vbext_ct_StdModule).CodeModule
cm.AddFromString (decString & funcString)
cm.Name = "MyNewModule"
Set vbc = cm.Parent
Application.VBE.ActiveVBProject.VBComponents.Remove vbc
''decString'' y ''funcString'' eran solo cadenas que construí como su ''ss''. El fragmento muestra cómo puede cambiar el nombre del módulo de código para que pueda eliminarlo más adelante si es necesario. Obviamente, esto simplemente lo elimina justo después de que se creó, y es probable que no quieras hacerlo, pero al menos te muestra cómo se haría.
Habiendo dicho todo eso, en su mayoría solo escribimos .exe ahora y pagamos. Si necesita que VBA espere a que finalice el shell, también hay soluciones para ese problema.
El problema
- Tengo una plantilla de palabra que usa la declaración
Declare
de VBA para vincular a un archivo DLL, cuya ruta se puede determinar dentro de la macro de VBA - Quiero desplegar esto en el directorio% APPDATA% / Microsoft / Word / STARTUP de los usuarios
- NO QUIERO cambiar permanentemente la variable de entorno PATH del usuario (temporalmente estaría bien, pero parece que no funciona, ya que no se actualizan hasta que se reinicie la aplicación)
Intento de solución
Intenté agregar dinámicamente el código con las instrucciones Declare
utilizando ThisDocument.VBProject.CodeModule.AddFromString(code)
que funciona al cargar la plantilla desde un directorio normal, pero cuando la plantilla está dentro de Word / STARTUP, da el siguiente error:
Error en tiempo de ejecución ''50289'':
No se puede realizar operación ya que el proyecto está protegido.
Y establecer la clave de registro "HKEY ___ LOCAL_MACHINE / Software / Microsoft / Office / 11.0 / Word / Security / AccessVBOM" en 1 no soluciona esto cuando la plantilla está en Word / STARTUP
Realmente estoy luchando por encontrar una solución. Si alguien sabe una manera de hacer esto, sería genial.
Puede usar LoadLibrary api.
Por ejemplo, en mis proyectos, el código se ve así:
If LibraryLoaded() Then
Call MyFunc ...
End If
Public Function LibraryLoaded() As Boolean
Static IsLoaded As Boolean
Static TriedToLoadAlready As Boolean
If TriedToLoadAlready Then
LibraryLoaded = IsLoaded
Exit Function
End If
Dim path As String
path = VBAProject.ThisWorkbook.path
path = Left(path, InStrRev(path, "/") - 1)
IsLoaded = LoadLibrary(path & "/bin/" & cLibraryName)
TriedToLoadAlready = True
LibraryLoaded = IsLoaded
End Function
Esto es lo que terminé haciendo, usando la metodología de Pranav Wagh vinculada anteriormente y el código del sitio de C Pearson ( http://www.cpearson.com/excel/vbe.aspx ). Este código solicita al usuario que seleccione la ruta al archivo dll utilizando una ventana Abrir archivo, crea un nuevo módulo con una función de declaración con la ruta ingresada y una función para ejecutar un protocolo de enlace con la dll. La función creada a propósito en el dll devuelve un 1 si tiene éxito:
Public rtn As Integer
Sub LinkToDll()
Dim path As String, default As String
MsgBox "Select Geo_DLL.dll file from next window"
With Application.FileDialog(msoFileDialogOpen)
.AllowMultiSelect = False
.Title = "Select Geo_DLL.dll file"
If .Show = True Then
path = .SelectedItems(1)
End If
End With
''Add a module
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents.Add(vbext_ct_StdModule)
VBComp.Name = "LinkModule"
''Add procedure to module
Dim CodeMod As VBIDE.CodeModule
Dim LineNum As Long
Set VBComp = VBProj.VBComponents("LinkModule")
Set CodeMod = VBComp.CodeModule
With CodeMod
LineNum = .CountOfLines + 1
.InsertLines LineNum, "Declare Function RegDll Lib " & Chr(34) & path & Chr(34) & " (ByRef rtn As Integer)"
LineNum = LineNum + 1
.InsertLines LineNum, "Sub runthisfunc(rtn)"
LineNum = LineNum + 1
.InsertLines LineNum, "On Error Resume Next"
LineNum = LineNum + 1
.InsertLines LineNum, "rtn = 0"
LineNum = LineNum + 1
.InsertLines LineNum, "RegDll rtn"
LineNum = LineNum + 1
.InsertLines LineNum, "If rtn = 1 Then MsgBox (" & Chr(34) & "DLL linked" & Chr(34) & ")"
LineNum = LineNum + 1
.InsertLines LineNum, "If rtn = 0 Then MsgBox (" & Chr(34) & "DLL not found" & Chr(34) & ")"
LineNum = LineNum + 1
.InsertLines LineNum, "End Sub"
End With
''This is what CodeMod.InsertLines is writing:
''--------------------------------------------
''Declare Function RegDll Lib "C:/path/Geo_DLL.dll" (ByRef rtn As Integer)
''Sub runthisfunc(rtn)
''On Error Resume Next
''rtn = 0
''RegDll rtn
''If rtn = 1 Then MsgBox ("DLL Linked")
''If rtn = 0 Then MsgBox (DLL not found")
''End Sub
Application.Run "runthisfunc", rtn
''Delete Module
VBProj.VBComponents.Remove VBComp
End Sub
Sin embargo, una vez que convertí el libro de trabajo (xlsm) en un complemento (xlam) encontré que Excel no permitiría que la macro creara nuevos módulos para que mi LinkToDll no funcionara. La solución era volver a poner la función de declaración en LinkToDll con el nombre del archivo dll ("Geo_DLL.dll") como Lib junto con el sub runthisfunc. Encontré que hacer que el usuario simplemente seleccionara el archivo dll a través de la ventana Abrir archivo fue suficiente para apuntar a Excel al dll incluso con el nombre del archivo en la parte Lib de la declaración Declare Function.
Chris
Francamente, no sé cuál es el problema con el uso de todas esas inyecciones de código VBA, generación de ensamblaje para LoadLibrary () llamadas, etc. técnicas que he visto utilizar para esta simple tarea. En mi proyecto uso un código simple para cargar dll desde la misma ubicación que el libro de trabajo, así:
Declare Function MyFunc Lib "MyDll.dll" (....) As ...
Sub Test()
....
ChDir ActiveWorkbook.Path
... = MyFunc(....)
End Sub
Excel 2003 al menos, no tiene problemas para cargar el dll desde la ruta actual, establezca ChDir en cualquier ruta que tenga su DLL. Es posible que también deba cambiar su unidad actual, que está separada de la ruta actual. Tienes que hacerlo solo una vez, antes de la primera llamada de función, después de que la DLL permanezca conectada sin importar dónde esté tu ruta actual, por lo que puedes hacerlo una vez en workbook_open y no molestarte sobre la ruta más adelante. Proporciono una función ficticia vacía en la DLL solo para este pupose. No creo que MS Word sea diferente en esto.
Private Declare Sub Dummy Lib "MyDLL.dll" ()
Private Sub Workbook_Open()
ChDrive Left$(Me.Path, 1)
ChDir Me.Path
Dummy
End Sub