以编程方式在VBA宏中设置DLL搜索路径

时间:2009-01-16 11:47:45

标签: vba dll ms-word word-vba

问题

  • 我有一个单词模板,它使用VBA的Declare语句链接到一个dll,其路径可以在VBA宏中确定
  • 我想将其重新部署到用户%APPDATA%\ Microsoft \ Word \ STARTUP目录
  • 我不想永久更改用户的PATH环境变量(暂时没有问题,但这似乎不起作用,因为它们在应用程序重新启动之前不会刷新)

尝试解决方案

我尝试使用Declare语句使用ThisDocument.VBProject.CodeModule.AddFromString(code)语句动态添加代码,这在从正常目录加载模板时有效,但是当模板在Word \ STARTUP中时,它会出现以下错误:

  

运行时错误'50289':

     

以后无法执行操作   项目受到保护。

当模板在Word \ STARTUP中时,将注册表项“HKEY ___ LOCAL_MACHINE \ Software \ Microsoft \ Office \ 11.0 \ Word \ Security \ AccessVBOM”设置为1不会解决此问题


我真的很难找到解决方案。如果有人知道这样做的方法,那就太好了。

4 个答案:

答案 0 :(得分:8)

坦率地说,我不知道使用所有这些VBA代码注入,LoadLibrary()调用的程序集生成等技术,我已经看到这些技术用于这个简单的任务。在我的项目中,我使用简单的代码从与工作簿相同的位置加载dll,如下所示:

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

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

Excel 2003至少,从当前路径加载dll没有问题,将ChDir设置为DLL所具有的任何路径。您可能还需要更改与当前路径分开的当前驱动器。在第一次函数调用之前,无论当前路径在哪里,DLL都必须保持连接,所以你必须只执行一次,因此你可以在workbook_open中执行一次,而不用再考虑以后的路径。我在DLL中为这个pupose提供了一个空虚拟函数。我不认为MS Word在这方面有任何不同。

Private Declare Sub Dummy Lib "MyDLL.dll" ()

Private Sub Workbook_Open()
    ChDrive Left$(Me.Path, 1)
    ChDir Me.Path
    Dummy
End Sub

答案 1 :(得分:5)

您可以使用LoadLibrary api。

例如在我的项目中,代码如下所示:

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 :(得分:2)

还有另一个非常难看的解决方案,但是这个博主发现了它,我无法想出任何其他方式:

http://blogs.msdn.com/pranavwagh/archive/2006/08/30/How-To-Load-Win32-dlls-Dynamically-In-VBA.aspx

基本上,您编写了一个在运行时在VBA中创建代码模块的过程。该模块必须创建对dll的引用,并且必须创建一个虚函数(或过程)作为调用dll的模块的一部分。然后,从您的代码中,您使用Application.Run(dummyfunction(),arg1,arg2 ...)。这是必要的,否则,项目将无法编译,因为虚函数还不是函数。

你会在他的代码中注意到,他使用InputBox()来获取.dll的位置,但显然你可以从电子表格中的范围中获取位置。以下代码段可能很有用。

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'和'funcString'只是我构建的字符串,就像他的's'一样。该代码段显示了如何重命名代码模块,以便以后可以根据需要将其删除。显然,这只是在它创建后立即删除它,你可能不想这样做,但至少它会告诉你它将如何完成。

说了这么多,我们现在大部分只是编写.exe现在和shell。如果你需要VBA等待shell完成,那么也有解决方案。

答案 3 :(得分:0)

以下是我最终做的事情,使用上面链接的Pranav Wagh方法和C Pearson网站(http://www.cpearson.com/excel/vbe.aspx)的代码。此代码提示用户使用“打开文件”窗口选择dll的路径,使用带有输入路径的声明函数构建新模块,并使用函数执行与dll的握手。如果成功,则dll中的专用函数返回1:

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

但是,一旦我将工作簿(xlsm)转换为插件(xlam),我发现Excel不会让宏创建新模块,因此我的LinkToDll将无法正常工作。解决方法是将Declare Function重新放入LinkToDll,只使用dll文件名(" Geo_DLL.dll")作为Lib以及runthisfunc sub。我发现让用户只需通过Open File窗口选择dll文件就足以将Excel指向dll,即使只有Declare Function语句的Lib部分中的文件名。

克里斯