2009-01-16 258 views
6

问题编程设置DLL搜索路径

  • 我有一个使用VBA的Declare声明链接到一个DLL,其路径可以在VBA宏
  • 我内确定一个Word模板想要将其分解到用户%APPDATA%\ Microsoft \ Word \ STARTUP目录
  • 我不想永久更改用户的PATH环境变量(暂时可以,但这似乎不起作用,因为他们不会在应用程序重新启动之前不会刷新)

尝试性解决方案

我试图动态地添加使用ThisDocument.VBProject.CodeModule.AddFromString(code)从一个普通的目录装载模板时,其运作Declare语句的代码,但是当模板是Word中\ STARTUP内,它给以下错误:

Run-time error '50289':

Can't perform operation since the project is protected.

,并且设置注册表键 “HKEY LOCAL_MACHINE ___ \软件\微软\办公室\ 11.0 \字\安全\ AccessVBOM” 1不解决这个问题时,模板是在Word \ STARTUP


我真的很努力地找到解决方案。如果有人知道如何做到这一点,那就太好了。

+0

相关的MSACCESS http://stackoverflow.com/questions/28977285/how-do-i-find-the-current-path-directory-of-a-ms-access-database – DaveInCaz 2017-05-17 14:50:39

回答

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

还有另外一个非常非常丑陋的解决方案,但这个博客想通了,我想不出任何其他方式:

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 ...)。这是必要的,否则,项目不会编译,因为dummyfunction还不是函数。

你会发现在他的代码中,他使用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' 只是我构建了喜欢他的 'SS' 的字符串。该代码片段显示了如何重命名代码模块,以便稍后在需要时删除它。显然,这只是在创建后立即删除它,你可能不想这样做,但至少它会告诉你如何完成它。

说了这么多之后,我们现在主要只是编写.exe的文件并将其解压出来。如果您需要VBA等待外壳完成,那么也有针对该问题的解决方案。

7

坦率地说,我不知道使用所有这些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提供了一个空的虚拟函数。我不认为MS Word在这方面有什么不同。

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

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

哈,这是一个非常优雅的解决其他复杂问题的解决方案,谢谢! :D – CodeAndCats 2012-08-07 06:30:22

0

这里就是我最后做,使用Pranav Wagh的方法由C皮尔逊的网站(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不起作用。解决的办法是将声明函数放回到LinkToDll中,只需将dll文件名(“Geo_DLL.dll”)作为Lib以及runthisfunc子文件。我发现让用户只需通过打开文件窗口选择dll文件就足以将Excel指向dll,即使只使用Declare Function语句的Lib部分中的文件名。

克里斯