VB.NET DLL über COM (Excel, Word usw.) verfügbar machen

    • VB.NET

    Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von BiedermannS.

      VB.NET DLL über COM (Excel, Word usw.) verfügbar machen

      Hallo liebe Community,

      wenn ihr, so wie ich, gezwungen seit in VBA zu arbeiten, aber (fast) alle Funktionen und Feinheiten des NET-Frameworks nutzen möchtet, dann ist COM genau das richtige für euch.

      Zuerst noch ein kleiner Hinweis: Eine DLL COM-verfügbar machen ist nicht immer einfach und erfordert etwas an umdenken, aber mit etwas Übung weiß man schnell was man machen kann und was nicht.
      Dieses Tutorial beschäftigt sich mit der Erstellung einer COM-DLL von Grund auf (ohne VS-Vorlage) und ist somit auch in den Express Versionen möglich.

      Aufbau:
      Im Großen und Ganze gibt es zwei Möglichkeiten eine COM-DLL zu erstellen. Mit automatisch oder mit manuell generierter Schnittstelle. Heute werde ich die Methode mit der automatisch generierten Schnittstelle erklären, weil ich diese etwas leichter finde.

      Zuerst erstellen wir ein normales DLL Projekt. Dieses nennen wir "MeineErsteCOM_DLL"


      Gleich danach öffnen wir die Projekteinstellungen. Unter "Anwendung -> Assemblyinformationen..." aktivieren wir das Häkchen bei "Assembly COM-sichtbar machen".


      Nun erstellen wir noch ein Schlüsselpaar, damit wir die DLL auch signieren können. Dazu öffnen wir die VisualStudio-Eingabeaufforderung und navigieren in das Projektverzeichnis. Mit dem sn-Befehl können Schlüssel erstellt werden. z.B.:

      Quellcode

      1. sn -k MeinSchluessel.snk


      Um die DLL zu signieren, gehen wir wieder in die Projekteigenschaften Signierung, aktivieren das Häkchen bei "Assembly signieren" und wählen das soeben erstellte Schlüsselpaar aus (MeinSchluessel.snk).

      Das war es schon fast mit der Vorbereitung. Nur noch ein letzter Schritt.

      Das Grundgerüst:
      Das Grundgerüst der COM-Klasse ist dafür verantwortlich, dass wir später unsere Klasse sowei deren Member in VBA zu Gesicht bekommen. Jede Klasse in der DLL die COM-Verfügbar gemacht werden soll, muss dementsprechend ein eigenes Grundgerüst enthalten.

      VB.NET-Quellcode

      1. <ComClass(MeineComKlasse.ClassId, MeineComKlasse.InterfaceId, MeineComKlasse.EventsId)> _
      2. Public Class MeineComKlasse
      3. #Region "COM-GUIDs"
      4. ' Diese GUIDs stellen die COM-Identität für diese Klasse
      5. ' und ihre COM-Schnittstellen bereit. Wenn Sie sie ändern, können vorhandene
      6. ' Clients nicht mehr auf die Klasse zugreifen.
      7. Public Const ClassId As String = "13880b56-4717-43f3-8ff4-8853d4916752"
      8. Public Const InterfaceId As String = "ba1c5a43-8a27-476d-862d-049da97ce808"
      9. Public Const EventsId As String = "2b4ad47f-4a11-4a57-9a33-1544c74eee36"
      10. #End Region
      11. ' Eine erstellbare COM-Klasse muss eine Public Sub New()
      12. ' ohne Parameter aufweisen. Andernfalls wird die Klasse
      13. ' nicht in der COM-Registrierung registriert und kann nicht
      14. ' über CreateObject erstellt werden.
      15. Public Sub New()
      16. MyBase.New()
      17. End Sub
      18. End Class


      Zuerst kommt das ComClass-Attribut welches angibt, dass diese Klasse eine COM-Klasse ist. Das Attribut nimmt drei Parameter entgegen. Jeder dieser drei Parameter muss eine Konstante GUID sein und wird direkt in der Klasse durch Konstanten angegeben.
      Dadurch wird sichergestellt dass keine COM-DLL doppelt vorkommt. Diese müssen ab dem ersten Erstellen immer gleich bleiben.

      Darunter steht wie bei einer normalen Klasse der Klassenname.

      In der Region "COM-GUIDs" werden die oben genannten Konstanten definiert. Hier muss bei jeder neue Klasse eine neue Guid erstellt werden.

      Danach folgt noch der Parameterlose Konstruktor, der mit MyBase.New() die Klasse initialisiert. COM-Klassen können nur über ein parameterloses "Sub New()" aufgerufen werden.

      Hinweis: Konstruktoren mit Parametern können trotzdem hilfreich sein, falls die Klasse auch in .NET verwendet wird!

      Neue Funktionen

      Damit die Klasse nun auch wirklich COM-Sichtbar werden kann, muss auch noch mindestens eine öffentliche Methode oder Eigenschaft definiert werden.

      Hierzu fügen wir folgenden Code ein:

      VB.NET-Quellcode

      1. Public Function Addiere(ByVal a As Double, ByVal b As Double) As Double
      2. Return a + b
      3. End Function


      Zum Schluss muss die DLL nur noch erstellt und registriert werden. Das erstellen erfolgt wie bei jeder anderen DLL auch, mit einem Klick auf Erstellen

      Das Registrieren geht am schnellsten über die VisualStudio-Eingabeaufforderung. Wir navigieren in den Ordner in dem die DLL liegt und registrieren diese mit dem Befehl RegAsm:

      Quellcode

      1. regasm MeineErsteCOM_DLL.dll /tlb:MeineErsteCOM_DLL.tlb /codebase


      Bei Erfolg sollte die Ausgabe von RegAsm ungefähr so aussehen:

      Quellcode

      1. Types registered successfully
      2. Assembly exported to 'C:\Documents and Settings\user\My Documents\Visual Studio 2010\Projects\MeineErsteCOM_DLL\MeineErsteCOM_DLL\bin\Debug\MeineErsteCOM_DLL.tlb', and the type library was registered successfully


      Wenn man nun VBA öffnet (z.B.: in Excel) und einen neuen Verweis hinzufügt, sollte unsere soeben erstellte DLL in der Liste vorhanden sein. Diese aktivieren wir erst mal.


      Nun können wir in VBA auf die Klasse zugreifen sowie deren Methoden nutzen:

      VB.NET-Quellcode

      1. Sub Test()
      2. 'Initialisieren der COM-Klasse
      3. Dim MeineCOM As MeineErsteCOM_DLL.MeineComKlasse
      4. Set MeineCOM = New MeineErsteCOM_DLL.MeineComKlasse
      5. Dim Ergebnis As Double
      6. 'Aufruf der Funktion Addiere
      7. Ergebnis = MeineCOM.Addiere(15, 20)
      8. 'Ausgabe
      9. MsgBox (Ergebnis)
      10. End Sub


      Zum Schluss noch ein Paar wichtige Hinweise:
      • VBA kennt keine generischen Typen
      • Die Übergabe von Arrays eines eigenen Typen von VB.NET an VBA, funktioniert nicht immer reibungslos. Hier sollte man die Elemente einzeln über den Index holen.
      • Die Übergabe eines eigenen Typ von VBA an VB.NET funktioniert nur über ByRef. In diversen Fällen funktioniert dies auch nicht. In diesem Fall kann man die Übergabewerte vom Typ Object machen und diese in VB.NET richtig Casten.
      • VBA unterstützt keine Überladung von Methoden
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BiedermannS“ ()

      COM mit manuell generierter Schnittstelle

      Beim letzten Mal habe ich erklärt, wie man eine COM-Dll mit automatisch generierter Schnittstelle erzeugt, diesmal ist die manuelle Methode dran.

      Im Großen und Ganzen ändert sich nicht viel zur manuellen Methode, ausser das diesmal eine Schnittstelle erzeugt werden muss. Alles was vor "Das Grundgerüst:" (siehe vorheriger Post) kommt, kann 1:1 übernommen werden. Danach geht es hier weiter.

      Das Grundgerüst:
      Nachdem man ein neues Projekt erstellt und dies wie im vorherigen Post eingerichtet hat (COM-Sichtbar, Signierung), fügt man eine weitere Datei hinzu, eine Schnittstelle, diese wird benannt wie die COM-Klasse, nur mit einem großen i davor. In unserem Beispiel also: IMeineComKlasse

      In die Schnittstelle kommt folgender Code (Erklärungen als Kommentar)

      VB.NET-Quellcode

      1. Option Strict On
      2. 'Beinhaltet die Attribute die benötigt werden, um die Klasse COM-Verfügbar zu machen
      3. Imports System.Runtime.InteropServices
      4. 'GuidAttribute gibt die UID der COM-Schnittstelle an. Diese wird auch zum registrieren verwendet. Im Vergleich zur automatischen Version, wird hier nur eine GUID benötigt.
      5. 'InterfaceTypeAttribute definiert ob die COM-Klasse durch Early- oder Late-Binding verfügbar ist.
      6. ' ComInterfaceType.InterfaceIsDual gibt an, dass sowohl Early- als auch Late-Binding möglich sind
      7. <GuidAttribute("F082D476-7F65-48EE-AD99-CEC6D32B3BE2"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)>
      8. Public Interface IMeineComKlasse
      9. 'Hier wird bereits definiert, welche Subs, Functions, Properties oder Events, später in der COM-Klasse verfügbar sein werden
      10. Function Addiere(ByVal a As Double, ByVal b As Double) As Double
      11. Property Name As String
      12. Sub ZeigeHinweis(ByVal text As String)
      13. End Interface


      In der Klassen-Datei, muss man nun nur noch angegeben, welches Interface für die COM-Klasse verwendet wird. und die entsprechende Funktionalität implementieren:

      VB.NET-Quellcode

      1. Option Strict On
      2. Imports System.Runtime.InteropServices
      3. 'ComDefaultInterface und ComSourceInterfaces müssen auf die Schnittstelle zeigen, die die COM-Klasse definiert.
      4. ' Damit weiß der Compiler, welche Methoden er für COM verfügbar machen muss.
      5. 'ClassInterface(ClassInterfaceType.None) gibt an, welcher Schnittstellentyp für die COM-Klasse erzeugt wird.
      6. ' In 99.9% der Fälle, sollte dies auf None bleiben.
      7. <ComDefaultInterface(GetType(IMeineComKlasse)), ClassInterface(ClassInterfaceType.None)>
      8. <ComSourceInterfaces(GetType(IMeineComKlasse))>
      9. Public Class MeineComKlasse
      10. 'Hier wird die Schnittstelle implementiert. Somit ist sichergestellt, dass alle angegebenen Methoden auch vorhanden sind.
      11. 'Des Weiteren werden nur die Member COM-Verfügbar gemacht, welche explizit in dem Interface angegeben wurden
      12. Implements IMeineComKlasse
      13. 'Hier werden die von der Schnittstelle definierten Methoden nur noch mit Funktionalität befüllt
      14. Public Function Addiere(a As Double, b As Double) As Double Implements IMeineComKlasse.Addiere
      15. Return a + b
      16. End Function
      17. Public Sub ZeigeHinweis(text As String) Implements IMeineComKlasse.ZeigeHinweis
      18. End Sub
      19. 'COM-Properties benötigen einen expliziten Getter bzw. Setter entsprechend ihrer Ausführung (ReadOnly: nur Getter, WriteOnly: nur Setter, sonst beide)
      20. 'Der Getter bzw. Setter benötigt auch noch eine (private) Variable, auf die er zugreifen kann
      21. Private _Name As String = String.Empty
      22. Public Property Name As String Implements IMeineComKlasse.Name
      23. Get
      24. 'Hier wird definiert, was beim Lesen der Eigenschaft passieren soll
      25. Return _Name
      26. End Get
      27. Set(value As String)
      28. 'Hier wird definiert, was beim Schreiben der Eigenschaft passieren soll
      29. _Name = value
      30. End Set
      31. End Property
      32. End Class


      Vorteile dieser Methode:
      • Besserer Überblick, über die Member, die für COM Verfügbar gemacht werden.
      • Austauschbarkeit der Klassen (Polymorphie)
      • Größere Flexibilität

      Nachteile dieser Methode:
      • Bei neuen Funktionen muss sowohl das Interface, als auch die Klasse erweitert werden
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D