问题
Declare
语句链接到一个dll,其路径可以在VBA宏中确定尝试解决方案
我尝试使用Declare
语句使用ThisDocument.VBProject.CodeModule.AddFromString(code)
语句动态添加代码,这在从正常目录加载模板时有效,但是当模板在Word \ STARTUP中时,它会出现以下错误:
运行时错误'50289':
以后无法执行操作 项目受到保护。
当模板在Word \ STARTUP中时,将注册表项“HKEY ___ LOCAL_MACHINE \ Software \ Microsoft \ Office \ 11.0 \ Word \ Security \ AccessVBOM”设置为1不会解决此问题
我真的很难找到解决方案。如果有人知道这样做的方法,那就太好了。
答案 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部分中的文件名。
克里斯