2009-01-16 9 views
6

Das ProblemProgrammatically gesetzt DLL-Suchpfad in VBA-Makro

  • ich ein Wort Vorlage haben, die Declare Erklärung der VBA verwendet, um eine DLL zu verknüpfen, deren Pfad innerhalb des VBA-Makro
  • I bestimmt werden Ich möchte dies nicht an die Benutzer% APPDATA% \ Microsoft \ Word \ STARTUP Verzeichnis
  • delegieren. Ich möchte nicht dauerhaft die PATH-Umgebungsvariable des Benutzers ändern (vorübergehend wäre OK, aber das scheint nicht zu funktionieren, wie sie es tun wird nicht aktualisiert, bis die Anwendung neu gestartet wird.

Versuchte Lösung

Ich habe versucht, dynamisch den Code mit den Declare Aussagen Hinzufügen ThisDocument.VBProject.CodeModule.AddFromString(code) verwendet, die funktionieren, wenn die Vorlage von einem normalen Verzeichnis laden, aber wenn die Vorlage in Word \ STARTUP ist, gibt es die folgende Fehlermeldung:

Run-time error '50289':

Can't perform operation since the project is protected.

und Setzen Sie den Registrierungsschlüssel "HKEY ___ LOCAL_MACHINE \ Software \ Microsoft \ Office \ 11.0 \ Word \ Security \ AccessVBOM" bis 1 bedeutet dies nicht beheben, wenn die Vorlage in Word \ STARTUP ist


Ich habe wirklich Mühe, eine Lösung zu finden. Wenn jemand einen Weg kennt, dies zu tun, wäre das großartig.

+0

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

Antwort

5

Sie können LoadLibrary API verwenden.

Zum Beispiel in meinen Projekten sieht der Code wie folgt aus:

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

Es gibt einen anderen wirklich wirklich hässliche Lösung, aber diese Blogger es herausgefunden, und ich kann jede andere Art und Weise nicht herausfinden:

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

Grundsätzlich schreiben Sie eine Prozedur, die zur Laufzeit ein Codemodul in VBA erstellt. Dieses Modul muss einen Verweis auf die DLL erstellen und es muss eine Dummy-Funktion (oder Prozedur) als Teil dieses Moduls erstellen, das die DLL aufruft. Dann verwenden Sie aus Ihrem Code Application.Run (dummyfunction(), arg1, arg2 ...). Dies ist notwendig, da das Projekt sonst nicht kompiliert wird, da die Dummyfunktion noch keine Funktion ist.

Sie werden in seinem Code feststellen, dass er InputBox() verwendet, um den Speicherort der DLL zu ermitteln, aber offensichtlich können Sie den Speicherort aus einem Bereich in der Kalkulationstabelle abrufen. Das folgende Code-Snippet kann nützlich sein.

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' und 'funcString' waren nur Saiten, die ich wie sein 'ss' konstruiert. Das Snippet zeigt, wie Sie das Codemodul umbenennen können, damit Sie es bei Bedarf später löschen können. Offensichtlich löscht es das einfach direkt nachdem es erstellt wurde, und das würdest du wahrscheinlich nicht tun wollen, aber es zeigt dir zumindest, wie es gemacht würde.

Nachdem wir all das gesagt haben, schreiben wir meistens einfach .exe und berappen. Wenn Sie mit VBA warten müssen, bis die Shell fertig ist, gibt es auch Lösungen für dieses Problem.

7

Ehrlich gesagt, ich weiß nicht, was ist das Problem mit der Verwendung all dieser VBA-Code-Injektion, Assembly-Generierung für LoadLibrary() -Aufrufe, etc. Techniken, die ich gesehen habe für diese einfache Aufgabe verwendet. In meinem Projekt verwende ich einfache Code-DLL aus dem gleichen Ort wie die Arbeitsmappe zu laden, wie folgt aus:

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

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

Excel 2003 zumindest, hat kein Problem, die DLL aus dem aktuellen Pfad, Set ChDir zu welchem ​​Pfad Laden Sie Ihre DLL hat. Möglicherweise müssen Sie auch Ihr aktuelles Laufwerk ändern, das vom aktuellen Pfad getrennt ist. Sie müssen es nur einmal tun, vor dem ersten Funktionsaufruf, nachdem die DLL angehängt ist, egal wo sich Ihr aktueller Pfad befindet, also können Sie es einmal in workbook_open tun und sich später nicht um den Pfad kümmern. Ich stelle eine leere Dummy-Funktion in der DLL nur für diesen Zweck zur Verfügung. Ich denke nicht, dass MS Word darin anders ist.

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

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

Ha , so eine brillant elegante Lösung für ein ansonsten kompliziertes Problem, danke! : D – CodeAndCats

+1

Das wird nicht immer funktionieren. Das aktuelle Verzeichnis ist weit in der Liste der durchsuchten Pfade entfernt, und in einigen Szenarien ist das aktuelle Verzeichnis möglicherweise überhaupt nicht der Suchpfad. Außerdem schrauben Sie sich mit dem prozessweiten Arbeitsverzeichnis. Die explizite "LoadLibrary" -Ansatz mit einem absoluten Pfad ist der beste Weg, dies zu tun. –

+0

Angenommen, Sie benötigen den aktuellen Pfad für eine Access-Datei anstelle von Excel, dessen 'CurrentProject.Path' – DaveInCaz

0

Hier ist, was ich tun endete, Pranav Wagh Methodik oben und Code von C Pearson Seite (http://www.cpearson.com/excel/vbe.aspx) verknüpft werden. Dieser Code fordert den Benutzer auf, den Pfad zur DLL mit einem Open File-Fenster auszuwählen, erstellt ein neues Modul mit einer Declare-Funktion mit dem eingegebenen Pfad und eine Funktion zum Ausführen eines Handshakes mit der DLL. Der Zweck gebaute Funktion in der DLL gibt ein 1 bei Erfolg:

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 

Allerdings, wenn ich die Arbeitsmappe (xlsm) in ein AddIn (xlam) gedreht fand ich, dass Excel nicht das Makro erstellen neue Module lassen würde also würde mein LinkToDll nicht funktionieren. Die Lösung bestand darin, die Declare-Funktion mit dem Namen der DLL-Datei ("Geo_DLL.dll") als Lib zusammen mit dem Unterverzeichnis runthisfunc wieder in LinkToDll einzufügen. Ich fand, dass der Benutzer einfach die DLL-Datei über das Datei öffnen-Fenster auswählen musste, um Excel auf die DLL zu verweisen, sogar mit nur dem Dateinamen im Lib-Teil der Declare Function-Anweisung.

Chris

Verwandte Themen