COM-Klasse lässt keine Properties zu?

  • VB.NET

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von Higlav.

    COM-Klasse lässt keine Properties zu?

    Hallo liebe Community,

    Da ich schon einige Monate mit VB.NET programmiere, will ich nicht mehr auf all die Möglichkeiten des .NET-Frameworks verzichten, wenn ich in Excel-VBA code. Aus diesem Grund nahm ich mir vor, eine COM-Klasse zu schreiben, die einige wichtige und nützliche Klassen aus dem Framework beinhaltet. Bis jetzt klappt es.

    Jedoch, als ich sie testen wollte, und es sich bei der getesteten Klasse um eine zu instanzierende Klasse (z.B. IO.FileInfo) mit Properties handelte, bekam ich folgenden Fehler:
    Objekterstellung durch ActiveX-Komponente nicht möglich


    Ich hab' wie im Tipp beschrieben im Konstruktor keine Paramter verlangt. Die holte ich dann über eine Klasseneigene Prozedur. Und genau an diesem Punkt meckert Excel rum :(
    Ich wollte fragen, ob es eine Möglichkeit gibt, in COM-Klassen mit Properties zu arbeiten, da ich die statischen Methoden der IO.File-Klasse micht wirklich mag und die Instanzierung doch sehr angenehm zuhandhaben ist.

    Ich hab' das Ganze mit dieser Klasse ausprobiert

    VB.NET-Quellcode

    1. <ComClass(Versuch.ClassId, Versuch.InterfaceId, Versuch.EventsId)> Public Class Versuch
    2. Public Const ClassId As String = "819f1953-1dee-4c4e-b1d6-e4580a11cf7d"
    3. Public Const InterfaceId As String = "90982013-e8a8-4d5b-8fb8-4165b2530c56"
    4. Public Const EventsId As String = "d36565fc-d501-4ac8-896b-c1c0da60f9bf"
    5. Private _Str As String
    6. Public Property Str As String
    7. Get
    8. Return _Str
    9. End Get
    10. Set(value As String)
    11. _Str = value
    12. End Set
    13. End Property
    14. Public Sub New()
    15. MyBase.New()
    16. End Sub
    17. Public Sub Eintragen(Str As String)
    18. _Str = Str
    19. End Sub
    20. End Class

    Und mit diesem Code in Excel getestet

    VB.NET-Quellcode

    1. Option Explicit
    2. Sub los()
    3. Dim Vers As New NETSystem.Versuch
    4. Vers.Eintragen "Hallo" '<-- Der Fehler kommt hier
    5. MsgBox Vers.Str
    6. End Sub


    Grüsse

    Higlav

    Edit:
    Ich hab' gestern noch ein wenig herumprobiert.
    Anscheinend funktioniert auch die Instanzierung von Objekten in der Klasse nicht.

    Ich wollte mit meiner COM-Klasse eigentlich die Funktionsweise einiger meiner VBA-Skripte beträchtlich erhöhen, aber mit so starken Einschränkungen hab' ich nicht gerechnet... :(

    Edit by hal2000:
    - Posts zusammengefügt.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „hal2000“ () aus folgendem Grund: Titel war falsch gewählt

    Wichtig bei COM, die Assembly muss signiert sein (sn.exe) und mit regasm registriert werden.

    Versuchs mal so:

    VB.NET-Quellcode

    1. Dim Vers As NETSystem.Versuch
    2. Set Vers = New NETSystem.Versuch
    3. Call Vers.Eintragen("Hallo")
    4. MsgBox Vers.Str
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D
    Hallo BiedermannS,

    Ich hatte schon einen Signierungsschlüssel im Projekt erstellt("Eigenschaften" -> "Signierung") und ein Häckchen bei "für COM-Interop registrieren" wie auch in den Assemblyinformationen eines bei "Assembly COM-sichtbar machen" gesetzt. Ich denke, somit muss ich die Klasse nicht noch mit RegAsm.exe oder sn.exe weiterbehandeln. (?)
    Es scheint, als würde nur schon das Erstellen des Objektes misslingen (bis auf die Initialisierung der Variable), denn nun stoppt der Debugger schon beim Setzen der Variable auf die neue Instanz der Klasse. (bei Set Vers = New NETSystem.Versuch)
    Mir scheint, als ob es nicht ("nur") am internen Erstellen von Objekten, sondern allgemein etwas in meiner Klasse nicht so hinhaut, wie es sollte... :S

    Grüsse

    Higlav
    Hier ist ein sehr lesenswerter Artikel über COM-Interop: codeproject.com/Articles/990/U…-Interoperability-With-NE

    ...ist aber nichts für schwache Nerven. Man sollte sich vorab schon ein wenig mit der Materie auskennen.

    Ich habe ein wenig Erfahrung damit, also habe ich mal ein Beispiel geschrieben (ist kommentiert, inklusive Vorgehensweise):
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. 'Projekt: Klassenbibliothek, .NET 4, Name = VbaDLL
    3. 'Assembly COM-sichtbar machen: Nein
    4. 'Assembly für Interop registrieren: Nein
    5. 'Assembly signieren (neuen Key in den Projekteigenschaften erstellen, kein Passwort)
    6. 'als x86 erstellen (!)
    7. 'Visual Studio-Eingabeaufforderung als Administrator:
    8. 'Nach bin/x86/Release navigieren
    9. 'tlbexp VbaDLL.dll
    10. 'regasm /tlb:VbaDLL.tlb VbaDLL.dll
    11. 'gacutil /i VbaDLL.dll
    12. 'rückgängig (--> wieder sauberes System) mit:
    13. 'gacutil /u VbaDLL <-- OHNE .dll
    14. 'regasm /u /tlb:VbaDLL.tlb VbaDLL.dll
    15. 'Excel:
    16. 'Verweis auf VbaDLL setzen
    17. 'Das Interface ist standardmäßig dual, also IUnknown und IDispatch.
    18. 'Das ist die IID in der Registry (Extras --> GUID generieren)
    19. <Guid("F791D6E9-CBB2-441b-A57C-ABB549991F22")> _
    20. <ComVisible(True)> _
    21. Public Interface IFrameworkWrapper
    22. 'mehrere Beispiele für Funktionen, Properties, etc.
    23. Property ValueX As Int32 'X-Wert
    24. Sub SetY(y As Int32) 'Y-Wert
    25. Function Add() As Int32 'Addition, Ergebnis als Rückgabewert
    26. Sub Subtract() 'Subtraktion, Ergbnis in ReadOnly SubResult
    27. ReadOnly Property SubResult As Int32
    28. 'Division + Modulo, Argumente als Parameter, Ergebnis als Parameter (x DIV y) und Rückgabewert (x MOD y)
    29. Function DivMod(x As Int32, y As Int32, ByRef quotient As Int32) As Int32
    30. 'beliebiger String
    31. Property MyString As String
    32. Sub Eintragen(s As String)
    33. End Interface
    34. 'Das ist die CLSID in der Registry (Extras --> GUID generieren)
    35. <Guid("C5637559-C50A-44dd-8DD3-0E91E6F030ED")> _
    36. <ClassInterface(ClassInterfaceType.None)> _
    37. <ComVisible(True)> _
    38. Public Class FrameworkWrapper
    39. Implements IFrameworkWrapper
    40. Private m_x As Int32
    41. Private m_y As Int32
    42. Private m_res As Int32
    43. Private m_str As String
    44. Public Function Add() As Integer Implements IFrameworkWrapper.Add
    45. Return m_x + m_y
    46. End Function
    47. Public Function DivMod(x As Integer, y As Integer, ByRef quotient As Integer) As Integer Implements IFrameworkWrapper.DivMod
    48. quotient = x \ y
    49. Return x Mod y
    50. End Function
    51. Public Sub SetY(y As Integer) Implements IFrameworkWrapper.SetY
    52. m_y = y
    53. End Sub
    54. Public ReadOnly Property SubResult As Integer Implements IFrameworkWrapper.SubResult
    55. Get
    56. Return m_res
    57. End Get
    58. End Property
    59. Public Sub Subtract() Implements IFrameworkWrapper.Subtract
    60. m_res = m_x - m_y
    61. End Sub
    62. Public Property ValueX As Integer Implements IFrameworkWrapper.ValueX
    63. Get
    64. Return m_x
    65. End Get
    66. Set(value As Integer)
    67. m_x = value
    68. End Set
    69. End Property
    70. Public Sub Eintragen(s As String) Implements IFrameworkWrapper.Eintragen
    71. m_str = s
    72. End Sub
    73. Public Property MyString As String Implements IFrameworkWrapper.MyString
    74. Get
    75. Return m_str
    76. End Get
    77. Set(value As String)
    78. m_str = value
    79. End Set
    80. End Property
    81. End Class

    Testcode (Office Excel 2003, Button "TuWas"):
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Sub TuWas_BeiKlick()
    2. Dim dotnet As VbaDLL.FrameworkWrapper
    3. Set dotnet = New VbaDLL.FrameworkWrapper
    4. dotnet.ValueX = 11
    5. dotnet.SetY 3
    6. MsgBox "x is " & dotnet.ValueX
    7. MsgBox "x + y = " & dotnet.Add
    8. Call dotnet.Subtract
    9. MsgBox "x - y = " & dotnet.SubResult
    10. Dim rDiv As Long
    11. Dim rMod As Long
    12. rMod = dotnet.DivMod(11, 3, rDiv)
    13. MsgBox "11 DIV 3 = " & rDiv & ", 11 MOD 3 = " & rMod
    14. dotnet.Eintragen "Hallo Welt"
    15. MsgBox "DotNET String: " & dotnet.MyString
    16. dotnet.MyString = "anderer String"
    17. MsgBox "DotNET String: " & dotnet.MyString
    18. End Sub

    Dieser Code funktioniert bei mir wie erwartet. Ganz wichtig ist das explizite Erstellen als x86, denn Office ist immer noch ein 32-Bit-Programm und kann daher keine 64-Bit-DLLs laden. Letztere werden aber generiert, wenn AnyCPU eingestellt ist. Das wird auch der Grund für den Fehler sein, den du oben kassiert hast.

    Debuggen kann man dieses Gebilde auch - man setzt in VB.NET einfach ein Debugger.Break() in die Funktion, führt sie aus und wartet auf den so simulierten Absturz. Der JIT-Debugger erledigt den Rest und hält in der .NET-Funktion an.

    Du kannst bei regasm /codebase setzen - dann musst du beim Neuerstellen nicht immer das gacutil bemühen. Erstelle am besten eine Batch-Datei für das Neuregistrieren der DLL.
    Gruß
    hal2000
    Hallo hal2000,

    Danke für die umfangreiche Antwort.:thumbsup: Ich werd' mir das alles mal in Ruhe ansehen.

    Grüsse

    Higlav