Discussion:
DLL laden
(zu alt für eine Antwort)
Mike Wesling
2009-02-06 20:34:02 UTC
Permalink
Hallo,

ich müsste aus einem VBA-Makro heraus eine .NET-DLL zur Laufzeit
einbinden/registrieren/laden und auf eine Methode zugreifen. Ich habe
schon mal mit der Win32-API Funktion LoadLibrary die DLL geladen und
daher einen gültigen Handle.

Wie geht es aber dann weiter? Hat jemand ein Beispiel hierfür?
Hartwig Constien
2009-02-09 08:51:00 UTC
Permalink
Post by Mike Wesling
ich müsste aus einem VBA-Makro heraus eine .NET-DLL zur Laufzeit
einbinden/registrieren/laden und auf eine Methode zugreifen. Ich habe
schon mal mit der Win32-API Funktion LoadLibrary die DLL geladen und
daher einen gültigen Handle.
Wie geht es aber dann weiter? Hat jemand ein Beispiel hierfür?
Hallo Mike,

für VB6-ActiveX-DLLs verwende ich die nachstehenden Proceduren. Ob das mit
.Net-DLLs auch so geht, musst Du ausprobieren.

Wenn Deine DLL dauerhaft registriert ist, kannst Du sie im VBA-Editor über
Extras\Verweise fest einbinden. Dann funktioniert auch Intellisence.

Bei dem Weg über LoadLibrary findet keine dauerhaft Registrierung statt. Du
musst dann mit Object arbeiten, hast also kein Intellisence. Da musst Du dann
schon selber wissen, wie Du welche Proceduren anzusprechen hast.

In meinen eigenen VB6-DLLs habe ich immer eine Funktion "IsLoaded"
integriert die True liefert, wenn die DLL angesprochen werden kann, um
bereits registrierte DLLs nicht erneut zu laden.

Hang loose, Hartwig


Option Explicit

Private Declare Function LoadLibrary Lib "kernel32" Alias _
"LoadLibraryA" (ByVal lpLibFileName As String) As Long

Private Declare Function GetProcAddress Lib "kernel32" _
(ByVal hModule As Long, ByVal lpProcName As String) As Long

Private Declare Function WaitForSingleObject Lib "kernel32" _
(ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long)
As Long

Private Declare Function CreateThread Lib "kernel32" _
(lpThreadAttributes As Any, ByVal dwStackSize As Long, _
ByVal lpStartAddress As Long, ByVal lParameter As Long, _
ByVal dwCreationFlags As Long, lpThreadID As Long) As _
Long

Private Declare Function GetExitCodeThread Lib "kernel32" _
(ByVal hThread As Long, lpExitCode As Long) As Long

Private Declare Sub ExitThread Lib "kernel32" (ByVal dwExitCode As Long)

Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As
Long) As Long

Const STATUS_WAIT_0 = &H0

'Es muss einmalig RegServeDLL gerufen sein, damit getDLL ein Klassenobject
zurückliefert
Public Function getDLL(sClassName As String) As Object
On Error Resume Next
Set getDLL = CreateObject(sClassName)
End Function

'Vor Aufruf muss geklärt sein, dass die DLL-Datei existiert
'Liefert True, wenn ein Fehler auftrat!
Public Function RegServeDLL(ByVal Path As String, mode As Boolean) As Boolean
Dim insthLib As Long, lpLibAdr As Long, hThd As Long, lpExCode As Long
Dim procName As String, Result As Long, okFlag As Boolean
'DLL in den Speicher laden
insthLib = LoadLibrary(Path)
'Aktion wählen
If insthLib Then
If mode Then
procName = "DllRegisterServer"
Else
procName = "DllUnregisterServer"
End If
'Adresse der DLL im Speicher
lpLibAdr = GetProcAddress(insthLib, procName)
If lpLibAdr <> 0 Then
'Aktion starten
hThd = CreateThread(ByVal 0, 0, ByVal lpLibAdr, ByVal 0&, 0&, 0&)
If hThd Then
'Maximal 5 sec warten
Result = WaitForSingleObject(hThd, 5000)
If Result = STATUS_WAIT_0 Then
'Vorgang erfolgreich in 5 Sec beendet
Call CloseHandle(hThd)
okFlag = True
Else
'5 sec überschritten -> Thread schließen
Call GetExitCodeThread(hThd, lpExCode)
Call ExitThread(lpExCode)
Call CloseHandle(hThd)
End If
End If
End If
'Speicher wieder freigeben
Call FreeLibrary(insthLib)
End If
If Not okFlag Then
RegServeDLL = True
Else
RegServeDLL = False
End If
End Function
Hartwig Constien
2009-02-11 09:18:01 UTC
Permalink
... mir fällt da noch ein, dass mit dem Gespann Windows Vista und Word 2007
kein temporärer DLL-Verweis mit LoadLibrary gebildet werden kann.
Irgendwelche neumodischen Sicherheitsmechanismen verhindern dies und lassen
sich trotz voller Adminrechte nicht umgehen. Da funktioniert nur die normale
Vorab-Registrierung der DLL.

Vielleicht weiß jemand von unseren MVPlern, wie das besser geht?

Hang loose, Hartwig
Thomas Gahler
2009-02-14 21:07:40 UTC
Permalink
Hallo Hartwig
für VB6-ActiveX-DLLs verwende ich die nachstehenden Proceduren. [....]
Dein Code intererssiert mich.
Versteh ich richtig, dass du mit diesen Zeilen via VBA eine ActiveX-Dll
registrierts (analog RegSvr32.exe /s ".dll-Datei") oder macht das Ding noch
ein wenig mehr?

Ich möchte wirklich nur die .dll registrieren. Kannst du mir hier kurz
weiterhelfen (auch wenn wir in einer WordVBA-NG sind). Danke.
--
Thomas Gahler
MVP für Word
Co-Autor von »Microsoft Word-Programmierung.
Das Handbuch« (MS Press)


- Windows Vista (SP1), Office 2007 (SP?)
Hartwig Constien
2009-02-16 07:56:01 UTC
Permalink
Hallo Thomas,
Post by Thomas Gahler
Dein Code intererssiert mich.
Ich muss zugeben, dass ich das mal irgendwo abgekupfert habe, aber nicht
mehr weiß, wer das ursprünglich entwickelt hat. Und der Entwickler hat ganz
offensichtlich mehr Ahnung von API-Programmierung als ich.
Post by Thomas Gahler
Versteh ich richtig, dass du mit diesen Zeilen via VBA eine ActiveX-Dll
registrierts (analog RegSvr32.exe /s ".dll-Datei") oder macht das Ding noch
ein wenig mehr?
So wie ich es verstehe, wird nichts registriert. LoadLibrary liefert
lediglich ein Modul-Handle der ActiveX-DLL zurück, unter dem die
DLL-Funktionen dann angesprochen werden können.

Für meine Zwecke ist das sehr praktisch, da ich im Code eine ganz bestimmte
DLL ansprechen kann, ohne dass diese registriert sein muss. Da spielt es dann
auch keine Rolle, ob noch andere Versionen der DLL in anderen Pfaden
existieren.

Für Vista/Office 2007 habe ich noch keinen vergleichbaren Weg gefunden.

Hang loose, Hartwig
Hartwig Constien
2009-02-16 14:11:23 UTC
Permalink
Hallo Thomas,
Post by Thomas Gahler
Ich möchte wirklich nur die .dll registrieren. Kannst du mir hier kurz
weiterhelfen (auch wenn wir in einer WordVBA-NG sind). Danke.
Dafür nehme ich in VB6 eine Process-Klasse (unbekannter Autor), der ich Pfad
und Name der RegSvr32 übergebe und als Parameter "/s Pfad\DLLName".
Die Klasse hat eine Function IsTerminated. Damit wartet der Aufrufer-Code
solange, bis die DLL-Registrierung abgeschlossen ist.

Hang loose, Hartwig

Option Explicit

'------------------------------------------------------------------------------
' KLASSE: clsProcess
' ------
'
' Ein Object der Klasse clsProcess wird verwendet, um eine Applikation zu
starten,
' und den Status derselbigen ermitteln zu können
' Die Benutzung der Klasse ist sowohl mit 16Bit als auch mit 32 Bit mit
identischer
' Syntax möglich.
'
'
' Interface
' ---------
'
' Property Programmname Name des zu startenden Programms
' Property Arguments Zusätzliche Argumente
' Method Exec Ausführen
' Property IsTerminated Ist der Process beendet.
'----------------------------------------------------------------

'Im aufrufenden Modul
' Dim Process As New clsProcess
' Process.ProgramName = "attrib"
' Process.Arguments = "-r " & "C:\Pfad\Unterpfad\*.* /s"
' Process.Exec
' Do While Not Process.IsTerminated
' DoEvents
' Loop
' DoEvents

'----- API Declarationen ---------------------------------------

#If Win32 Then

Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type

Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As Long, ByVal dwMilliseconds As Long) As Long

Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal _
hObject As Long) As Long

Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&

#Else

Private Declare Function GetModuleUsage Lib "Kernel" (ByVal hModule
As Integer) As Integer


#End If

'------------------------------------------------------------------------------------
'-------------------------------------------------------------------------------------
' Internal Member
'------------------------------------------------------------------------------------
'-------------------------------------------------------------------------------------

#If Win32 Then
Private m_ProcessInfo As PROCESS_INFORMATION
Private m_StartupInfo As STARTUPINFO
#Else
Private ApplicationHandle As Integer
#End If

Private m_ProgramName As String
Private m_Arguments As String


'------------------------------------------------------------------------------------
'-------------------------------------------------------------------------------------
' INTERFAC
'------------------------------------------------------------------------------------
'-------------------------------------------------------------------------------------

'-------------------------------------------------------------------------------------
' ProgramName
' -----------
'
' Name des zu startenden Programmes (ohne Argumente)
'
------------------------------------------------------------------------------------
Public Property Let ProgramName(szName As String)
m_ProgramName = szName
'MsgBox m_ProgramName
End Property


'
------------------------------------------------------------------------------------
' Argmuments
' ----------
'
' zusätzliche Argumente für das zu startende Programm
'
'
------------------------------------------------------------------------------------
Public Property Let Arguments(szArg As String)
m_Arguments = szArg
'MsgBox m_Arguments
End Property


'
------------------------------------------------------------------------------------
' Exec()
' ----
'
' Starten des Programmes und sichern der relevanten Informationen in den
internen
' Member-Variablen
'
Public Sub Exec()

#If Win32 Then

Dim ret As Long

' Initialisierung der STARTUPINFO Structure.
' (im ersten Element erwartet die API-Funktion die Grösse der
Struktur)
m_StartupInfo.cb = Len(m_StartupInfo)

' Start der Applikation über die API-Funktion CreateProcess anstelle
' der Verwendung der VB-internen Shell-Funktion
'
ret = CreateProcessA(0&, m_ProgramName & " " & m_Arguments, 0&, 0&,
1&, _
NORMAL_PRIORITY_CLASS, 0&, 0&, m_StartupInfo, m_ProcessInfo)

#Else

' In der 16-Bit Variante wird das Programm über die interne
Shell-Funktion
' gestartet
'
ApplicationHandle = Shell(m_ProgramName & " " & m_Arguments, 1)

#End If

End Sub


'
------------------------------------------------------------------------------------
' IsTerminated
' ------------
'
' Liefert zurück, ob die gestartete Applikation beendet wurde
'
'
------------------------------------------------------------------------------------
Public Property Get IsTerminated() As Boolean

#If Win32 Then
IsTerminated = (WaitForSingleObject(m_ProcessInfo.hProcess, 10) = 0)
#Else
IsTerminated = (GetModuleUsage(ApplicationHandle) = 0)
#End If

End Property
m***@googlemail.com
2009-03-11 18:37:43 UTC
Permalink
Hallo,

ich möchte per VBA die dsofile.dll einbinden, da ich sie auf den
zugenagelten Rechnern nicht ins Systemverzeichnis bekomme und
ordnungsgemäß registrieren kann. Hat jemand einen lauffähigen Code,
wie ich die dll einbinden kann?
Ich habe mir hier schon Codebeispiele kopiert, bekomme das aber bisher
nicht zum laufen.

Für eine Antwort wäre ich dankbar.

Gruß Matthias
Hartwig Constien
2009-03-12 14:48:11 UTC
Permalink
Hallo Matthias,
Post by m***@googlemail.com
ich möchte per VBA die dsofile.dll einbinden, da ich sie auf den
zugenagelten Rechnern nicht ins Systemverzeichnis bekomme und
ordnungsgemäß registrieren kann. Hat jemand einen lauffähigen Code,
wie ich die dll einbinden kann?
Ich habe mir hier schon Codebeispiele kopiert, bekomme das aber bisher
nicht zum laufen.
Sag doch mal, mit welchem Betriebssystem Du arbeitest und welche Word-Version.
Nur Du, oder für mehrere Benutzer mit unterschiedlichen Konfigurationen?

Ablegen im Systemverzeichnis ist bei dieser DLL nicht unbedingt
erforderlich. Kann auch ein lokaler UserPfad sein. Doubletten der DLL sollte
man aber vermeiden, sonst kommt man in die Hölle. Wenn Du/Ihr schreibenden
Zugriff auf den Registry-Hive HKLM hast (ggf. as Admin), kannst Du das Teil
auch registrieren und alles ist in Butter.

Für den Fall, dass das nicht geht, kann man die hier angesprochenen Routinen
benutzen (außer Vista/Word2007). Dann muss man aber mit Object arbeiten und
nicht mit der DLL-Referenz und hat auch kein Intellisence. Dein Ursprungscode
müsste entsprechend angepasst werden.

Warum öffnest Du nicht das entsprechende Word-Dokument und holst Dir daraus
die DokEigenschaften?

Hang loose, Hartwig
m***@googlemail.com
2009-03-13 15:03:58 UTC
Permalink
Hallo Hartwig,
Post by Hartwig Constien
Sag doch mal, mit welchem Betriebssystem Du arbeitest und welche Word-Version.
Nur Du, oder für mehrere Benutzer mit unterschiedlichen Konfigurationen?
Windows XP mit Word 2003 ; ca. 600 identische clients, 1000 Benutzer
mit eigenen Anmeldungen
Post by Hartwig Constien
Ablegen im Systemverzeichnis ist bei dieser DLL nicht unbedingt
erforderlich. Kann auch ein lokaler UserPfad sein. Doubletten der DLL sollte
man aber vermeiden, sonst kommt man in die Hölle. Wenn Du/Ihr schreibenden
Zugriff auf den Registry-Hive HKLM hast (ggf. as Admin), kannst Du das Teil
auch registrieren und alles ist in Butter.
Zugriff auf Registry ist gesperrt
Post by Hartwig Constien
Für den Fall, dass das nicht geht, kann man die hier angesprochenen Routinen
benutzen (außer Vista/Word2007). Dann muss man aber mit Object arbeiten und
nicht mit der DLL-Referenz und hat auch kein Intellisence. Dein Ursprungscode
müsste entsprechend angepasst werden.
Daran bin ich bis jetzt gescheitert.
Post by Hartwig Constien
Warum öffnest Du nicht das entsprechende Word-Dokument und holst Dir daraus
die DokEigenschaften?
Ich verwende die Eigenschaften zur Steuerung von Auswahlfeldern meiner
Userform. Der Benutzer klickt in einer Listbox eine von hunderten von
Vorlagen an. Über eine Zahl in "Keywords" wird dann z.B. gesteuert,
welche Auswahlfelder aktiv geschaltet werden und der Eintrag unter
"Kommentar" wird in einem Hinweisfenster angezeigt. Dort sind Hinweise
zur Vorlage, z.B. für welchen Zweck sie dient usw.
Da der User sich einfach da durchklickt und verschiedene Vorlagen
anwählt, würde das Öffnen im Hintergrund die Performance vermutlich
ganz schön beeinflussen und erhebliche Netzlasten bringen.
Deshalb wäre es schön, wenn ich es nach wie vor über die dsofile.dll
auslesen könnte.

Kannst du mir vielleicht einen VBA Code, mit dem ich die dll einbinde,
zur Verfügung stellen?

Gruß Matthias
Post by Hartwig Constien
Hang loose, Hartwig
Hartwig Constien
2009-03-13 16:01:01 UTC
Permalink
Hallo Matthias,

ich hab mal vor einiger Zeit für ein Projekt eine VB6-Anwendung begonnen,
die mit dsofile Teile von Datei-Kommentaren aus Dokumenten gelistet hat. Die
dsofile musste dabei nicht registriert sein. Da das Projekt gecancelt wurde,
ist die Anwendung aber nicht fertig gestellt geworden.

Ich denke, das wird hier jetzt etwas speziell. Ich werde per Mail Kontakt
aufnehmen.

Hang loose, Hartwig

Loading...