Als alter VBA-Hase werden Sie sich wahrscheinlich schon hin und wieder gewünscht haben, auch eigenständig laufende Anwendungen mit VBA entwickeln zu können. Immerhin - mit der VBA-Entwicklungsumgebung in Microsoft Office 2000-Anwendungen können Sie COM-AddIns als ausführbare DLLs kompilieren. Doch das Erstellen eigenständig ausführbarer Anwendungen bleibt Ihnen offensichtlich verwehrt. Sie können sich natürlich die "richtige" Visual Basic-Entwicklungsumgebung oder gar das komplette Visual Studio von Microsoft zulegen. Aber unbedingt notwendig ist das nicht - es gibt einen Ausweg. Diesen Ausweg stellen wir Ihnen hier zur Verfügung: Eine von uns gebrauchsfertig (natürlich in VB) erstellte Starter-Anwendung und Beispiel-Code bzw. -Module zum Einbinden in mit der VBA-Entwicklungsumgebung erstellte DLLs. Es genügen tatsächlich wenige Zeilen Code, die Ihre VBA-DLL darauf vorbereiten, dass sie von dieser Starter-Anwendung (RunVBADLL) gestartet und am Leben erhalten werden kann.
Sie werden sich vielleicht schon darüber gewundert haben, dass Klassen-Module in VBA entweder nur privat oder aber öffentlich, aber dann nicht instanzierbar sein können. Eine instanzierbare ActiveX-DLL braucht jedoch mindestens eine instanzierbare Klasse. Tatsächlich steht ein solcher Modul-Typ zur Verfügung. Wenn Sie ein neues AddIn-Projekt anlegen, wird automatisch ein AddInDesigner-Modul geladen. Lassen Sie den ganzen AddIn-Schnickschnack weg, haben Sie damit eine öffentliche, instanzierbare Klasse. Sie können sie nach Belieben mit öffentlichen Methoden und Eigenschaften versehen und von anderen Anwendungen aus instanzieren. Das Designer-Formular können Sie einfach unausgefüllt schließen und auch die speziellen Ereignisse der AddinInstance-Schnittstelle können Sie getrost ignorieren. Sie können dem Modul auch jeden beliebigen Namen geben.
Das Grundprinzip für eine startbare VBA-DLL sieht folgendermaßen aus: Sie rufen die erwähnte Starter-Anwendung auf und übergeben dieser als Kommandozeilen-Parameter die ProgId Ihrer VBA-DLL. Die Starter-Anwendung legt nun eine Instanz Ihrer öffentlichen Klasse an, und diese lädt ein UserForm, das als Start-Form der eigenständigen Anwendung dient - fertig!
Nein, nicht ganz. Es gibt ein kleines Problem. Im Gegensatz zu "echten" Visual Basic-Forms leben VBA-UserForms nicht sehr lange, jedenfalls nicht aus eigenem Antrieb. Das von Ihrer DLL-Klasse angezeigte UserForm würde nur kurz aufblitzen und gleich wieder verschwinden, sobald die Starter-Anwendung endet. Wir brauchen also einen Mechanismus, der die Starter-Anwendung so lange leben lässt, bis das oder die UserForm(s) Ihrer DLL alle wieder geschlossen sind oder Ihre DLL anderweitig ihr Ok dazu gibt.
Der einfachste Weg ist, in der Starter-Anwendung ein VB-Form zu laden, das das Beenden der Starter-Anwendung so lange verhindert, bis das Form geschlossen wird. Es braucht sogar selbst gar nicht sichtbar zu werden. Aber es bleibt die Frage, wie dieses Form erfährt, wann es geschlossen werden soll, weil Ihre DLL-Anwendung beendet werden will. Die Lösung ist ein Hilfsobjekt, das in der Starter-Anwendung implementiert ist und von dieser instanziert wird. Dieses Hilfsobjekt wird Ihrer öffentlichen Verbindungsklasse in einer Initialisierungs-Methode übergeben. Ihre Klasse wiederum reicht nun dieses Objekt an das UserForm weiter, das dieses Objekt in einer Variablen speichert. Wird das UserForm geschlossen, wird bei diesem das Terminate-Ereignis ausgelöst. In diesem Ereignis wird nun die Methode DoFinish des Hilfsobjekts aufgerufen. Und dieses schließlich sendet ein Ereignis an das Form in der Starter-Anwendung, woraufhin sich jenes Form entlädt und damit die Starter-Anwendung beendet.
Die Initialisierungs-Methode der öffentlichen Klasse in Ihrer VBA-DLL muss "Init" heißen - die Verfügbarkeit dieser Methode wird von der Starter-Anwendung erwartet. Die Methode muss über einen als "Object" deklarierten Parameter verfügen, in dem das Hilfsobjekt "Helper" übergeben wird. Hier brauchen Sie nichts weiter zu tun, als eine Instanz Ihres Start-UserForms anzulegen und wiederum die Init-Methode aufzurufen, über die das UserForm verfügen muss. Hier können Sie den Namen der Methode beliebig wählen - das fällt ausschließlich in den Verantwortungsbereich Ihrer DLL - und liegt damit in Ihrer Verantwortung.
Die Init-Methode im zur öffentlichen Klasse umgewidmeten AddIn-Designer-Modul könnte wie folgt aussehen:
Public Sub Init(Helper As Object)
With New frmBeispiel
.Init Helper
End With
End Sub
Im Code-Modul des UserForms deklarieren Sie im Allgemein-Teil eine private Objekt-Variable, beispielsweise mHelper genannt. Dieser Variablen weisen Sie in der Init-Prozedur des UserForms (Sie müssen diese Methode selbst anlegen - sie gehört nicht zum Standard-Funktionsumfang eines UserForms) das übergebene Hilfsobjekt ("Helper") zu und lassen mit Me.Show das UserForm sichtbar werden.
Private mHelper As Object
Public Sub Init(Helper As Object)
Set mHelper = Helper
Me.Show
End Sub
Im Terminate-Ereignis des UserForms rufen Sie die DoFinish-Methode des Helper-Objekts auf und geben die Objekt-Variable frei:
Private Sub UserForm_Terminate()
On Error Resume Next
mHelper.DoFinish
Set mHelper = Nothing
End Sub
Das ist schlichtweg alles, was Sie in Ihrer VBA-DLL zu unternehmen brauchen, um sie als eigenständige Anwendung laufen lassen zu können, nachdem Sie sie kompiliert haben. Sie rufen die Starter-Anwendung auf und übergeben ihr als Kommandozeilen-Parameter die ProgId Ihrer VBA-DLL. Die ProgId ist die Bezeichnung, unter der die DLL in der Windows-Registrierung vermerkt ist und besteht aus dem Projekt-Namen der DLL und, getrennt durch einen Punkt, dem Namen des umfunktionierten AddIn-Designer-Moduls.
Die Starter-Anwendung steht in zwei Versionen zur Verfügung. RunVBADLL6.exe benötigt die Laufzeitdateien (Runtime) von VB 6 und RunVBADLL5.exe benötigt die Laufzeitdateien von VB 5. Sie können beide Versionen mit oder ohne Laufzeitdateien (falls Sie diese bereits haben sollten) zu diesem Artikel herunterladen.
Der Aufruf der Starter-Anwendung mit der Beispiel-DLL zu diesem Artikel sähe so aus:
RunVBADLL6.exe VBADLL.Run
bzw.
RunVBADLL5.exe VBADLL.Run
Falls Sie nun noch die Funktionsweise der Starter-Anwendung interessiert, lesen Sie bitte weiter. Falls nicht, können sie die Lektüre dieses Artikels beenden und die Starter-Anwendung und Muster-Module für Ihre VBA-DLL herunterladen.
Die Starter-Anwendung ist als ActiveX-EXE angelegt, damit sie ein öffentliches Hilfsobjekt anbieten kann. Sie umfasst drei Module, ein Standard-Modul mit der Main-Prozedur, die Klasse für das Hilfsobjekt "Helper" und das oben erwähnte Form.
Die Main-Prozedur in dem Standard-Modul sorgt für die formale Prüfung der in der Kommandozeile (Command$) übergebenen ProgId der zu startenden VBA-DLL. Wird keine ProgId übergeben, wird eine InputBox angeboten, in der der Anwender selbst eine ProgId eingeben kann. Gibt er keine ProgId ein, oder bricht er die InputBox ab, wird die Starter-Anwendung abgebrochen. Gibt er irgendeinen String ein, der nicht mindestens den formal notwendigen Punkt enthält, wird ihm der Fehler in einer MessageBox angezeigt. Er kann nun erneut eine ProgId eingeben ("Wiederholen") oder die Starter-Anwendung abbrechen.
Nur wenn eine nach diesem Verfahren formal gültige ProgId vorliegt, wird die Start-Methode des Forms mit der ProgId als Parameter aufgerufen.
Public Sub Main()
Dim nCmd As String
If App.StartMode = vbSModeAutomation Then
MsgBox _
"RunVBADLL kann nicht als Server gestartet werden.", _
vbCritical, "RunVBADLL"
Else
If Len(Command$) Then
nCmd = Command$
Else
Do
nCmd = _
Trim$(InputBox("ProgId der zu startenden VBA-DLL:", _
"RunVBADLL"))
If Len(nCmd) Then
If InStr(nCmd, ".") = 0 Then
If MsgBox(Chr$(34) & nCmd & Chr$(34) & _
" ist keine gültige ProgId.", _
vbRetryCancel Or vbCritical, "RundVBADLL") _
= vbCancel Then
nCmd = ""
Exit Do
End If
Else
Exit Do
End If
Else
Exit Do
End If
Loop
End If
If Len(nCmd) Then
With New frmStart
.Start nCmd
End With
End If
End If
End Sub
In der Start-Methode des Forms ("frmStarter") wird versucht, über CreateObject eine Instanz Ihrer VBA-DLL anzulegen. Gelingt das nicht, wird eine entsprechende Fehlermeldung ausgegeben und das Form erst gar nicht geladen - die Starter-Anwendung wird wieder beendet.
Gelingt die Instanzierung, wird eine Instanz des Helper-Objekts angelegt. Die Objekt-Variable für das Helper-Objekt ist als Ereignis-Empfänger ausgelegt (mit WithEvents-Anweisung). Diese Instanz des Helper-Objekts wird dem Aufruf der Init-Methodes des Instanz-Objekts Ihrer VBA-DLL übergeben. Sollte nun dieser Methoden-Aufruf schief gehen, erfolgt wieder eine entsprechende Fehlermeldung - die Starter-Anwendung wird wieder beendet, ohne dass das Form tatsächlich geladen wird.
Private WithEvents eHelper As Helper
Public Sub Start(Cmd As String)
Dim nObj As Object
On Error Resume Next
Set nObj = CreateObject(Cmd)
If Err.Number Then
MsgBox "VBA-DLL " & Cmd & " konnte nicht gestartet werden." _
& vbCrLf & vbCrLf & "Fehler " & Err.Number & vbCrLf & vbCrLf _
& Err.Description, vbCritical, "RunVBADLL"
Else
Set eHelper = New Helper
nObj.Init eHelper
If Err.Number Then
MsgBox "VBA-DLL " & Cmd _
& " konnte nicht initialisiert werden." & vbCrLf & vbCrLf _
& "Fehler " & Err.Number & vbCrLf & vbCrLf _
& Err.Description, vbCritical, "RunVBADLL"
Else
Load Me
End If
End If
End Sub
Nun wartet das Form auf das Eintreffen des Finish-Ereignisses, das von dem Helper-Objekt ausgelöst wird, wenn dessen DoFinish-Methode von Ihrer VBA-DLL aus aufgerufen wurde, um das Beenden Ihrer DLL-Anwendung zu signalisieren. Trifft das Ereignis ein, wird das Form entladen und die Starter-Anwendung beendet.
Private Sub eHelper_Finish()
Unload Me
End Sub
Die Klasse des Helper-Objekts enthält nichts weiter als die Deklaration des Finish-Ereignisses und die Methode DoFinish, in der dieses Ereignis ausgelöst wird.
Public Event Finish()
Public Sub DoFinish()
RaiseEvent Finish
End Sub
|