2009-01-16 21 views
6

El problemaestablecer mediante programación la ruta de búsqueda de DLL en VBA macro

  • Tengo una plantilla de la palabra que utiliza Declare declaración de VBA para enlazar a un archivo DLL, cuya trayectoria puede determinarse dentro de la macro VBA
  • I 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 esto no parece funcionar ya que no se actualiza hasta el reinicio de la aplicación)

intento de solución

He intentado añadir dinámicamente el código con los Declare declaraciones usando ThisDocument.VBProject.CodeModule.AddFromString(code) que funciona cuando se carga la plantilla de un directorio normal, pero cuando la plantilla está dentro de Word \ INICIO, se da siguiente error:

Run-time error '50289':

Can't perform operation since the project is protected.

Y el establecimiento de la clave de registro "HKEY ___ LOCAL_MACHINE \ Software \ Microsoft \ Office \ 11.0 \ Word \ Security \ AccessVBOM" a 1 no soluciona esto cuando la plantilla está en Word \ INICIO


Realmente estoy luchando por encontrar una solución. Si alguien sabe una manera de hacer esto, sería genial.

+0

relacionado para MSAccess http://stackoverflow.com/questions/28977285/how-do-i-find-the-current-path-directory-of-a-ms-access-database – DaveInCaz

Respuesta

5

Puede usar la API LoadLibrary.

Por ejemplo, en mis proyectos el código es el siguiente:

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 
2

Hay otra solución muy, muy feo, pero este blogger lo descubrió, y no puedo imaginar ninguna 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 sólo las cadenas que construye 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.

7

Francamente, no sé cuál es el problema con el uso de todas esas inyecciones de código VBA, generación de conjuntos para llamadas LoadLibrary(), etc. técnicas que he visto utilizar para esta simple tarea. En mi proyecto utilizo código simple para cargar DLL desde la misma ubicación que el libro, así:

Declare Function MyFunc Lib "MyDll.dll" (....) As ... 

Sub Test() 
    .... 
    ChDir ActiveWorkbook.Path 
    ... = MyFunc(....) 
End Sub 

Excel 2003, al menos, no tiene problema al cargar la DLL de la ruta actual, Conjunto ChDir a cualquier camino de su DLL tiene. 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 
+0

Ha , una solución tan brillantemente elegante para un problema que de otra manera sería complicado, ¡gracias! : D – CodeAndCats

+1

Eso no siempre funcionará. El directorio actual es un largo camino en la lista de rutas que se buscan, y en algunos escenarios, el directorio actual puede no ser uno en absoluto. Además, estás atornillando con el directorio de trabajo de todo el proceso. El enfoque explícito 'LoadLibrary' con una ruta absoluta es la mejor manera de hacerlo. –

+0

Suponiendo que necesita la ruta actual para un archivo de Access en lugar de Excel, su 'CurrentProject.Path' – DaveInCaz

0

Esto es lo que terminé haciendo, utilizando la metodología de Pranav Wagh vinculado anteriormente y el código desde el 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 el dll. La función especialmente diseñada en la 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 me di el libro (xlsm) en un complemento (xlam) me encontré con que Excel no dejaría que la macro crear nuevos módulos entonces mi LinkToDll no funcionaría 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

Cuestiones relacionadas