Verwandeln Sie eine einfache C # -DLL in eine COM-Interop-Komponente

70

Wie mache ich aus einer C # -DLL eine COM-Interop-DLL, die von einer VB6-Anwendung verwendet werden kann?

Elena Lawrence
quelle

Antworten:

93

Dies ist die Antwort, die ich in StackOverflow finden wollte, aber nicht konnte. Es stellt sich als ziemlich einfach heraus, eine einfache C # -Dll in eine COM-DLL umzuwandeln.

So erstellen Sie die C # -Dll

Erstellen Sie eine Lösung mit einem C # -Klassenprojekt. Die Klasse sollte eine Schnittstelle für die Eigenschaften / Methoden und eine Schnittstelle für die Ereignisse haben. Weisen Sie der Klasse und den Schnittstellen GUID-Attribute zu, wie in MSDN - Beispiel für eine COM-Klasse (C # -Programmierhandbuch) beschrieben . Siehe auch: MSDN - Gewusst wie: Auslösen von Ereignissen, die von einer COM-Senke verarbeitet werden .

Aktivieren Sie unter Projekteigenschaften> Registerkarte Anwendung> Schaltfläche Baugruppeninformationen die Option "Baugruppe COM-sichtbar machen". Dadurch werden alle öffentlichen Methoden in der Klasse COM sichtbar.

Klicken Sie unter Projekteigenschaften> Registerkarte Erstellen auf "Plattformziel" auf x86.

Das ist alles, was Sie tun müssen, um die DLL zu erstellen. Um die DLL aufzurufen, müssen Sie sie registrieren.

Registrieren der DLL auf Ihrem Entwicklungscomputer

Sie können die DLL auf eine der folgenden Arten registrieren:

  • Überprüfen Sie Projekteigenschaften> Registerkarte Erstellen> "Für COM Interop registrieren". Dadurch wird die DLL beim Erstellen automatisch registriert.
  • Registrieren Sie die DLL manuell bei RegAsm. Auf diese Weise können Sie die DLL im Verzeichnis Ihrer Wahl und nicht im Erstellungsverzeichnis registrieren. Dies ist die Methode, die ich verwendet habe.

    • Unterlassen SieAktivieren Projekteigenschaften> Registerkarte Erstellen> "Für COM Interop registrieren".
    • Kopieren Sie die DLL in das Verzeichnis, in dem Sie sie registrieren möchten
    • Öffnen Sie eine Befehlsshell mit Administratorrechten und geben Sie ein

      RegAsm.exe -tlb -codebase mydll.dll
      

      RegAsm.exe befindet sich unter "C: \ Windows \ Microsoft.NET \ Framework \ v2.0.50727", während "mydll.dll" der Name Ihrer DLL ist. tlbbedeutet "Typbibliothek erstellen"; codebasebedeutet "Schreiben Sie den Verzeichnisspeicherort in die Registrierung, vorausgesetzt, er wird nicht im GAC abgelegt".

      RegAsm zeigt eine Warnung an, dass die Assembly einen starken Namen haben sollte. Sie können es ignorieren.

      Zu diesem Zeitpunkt sollten Sie in der Lage sein, einen Verweis auf die COM-DLL in VB6 hinzuzufügen, ihn mit Intellisense anzuzeigen und wie eine normale COM-DLL auszuführen.

Installieren der DLL mit InstallShield

Wenn Sie InstallShield verwenden, um die DLL zusammen mit dem Rest Ihrer Anwendung zu installieren, gehen Sie wie folgt vor.

Fügen Sie in InstallShield der Komponentenliste eine neue Komponente hinzu. Denken Sie daran, die Komponente einem Feature zuzuordnen. Setzen Sie die Komponenteneigenschaft ".NET COM Interop" auf "Ja".

Fügen Sie die DLL-Datei zum Abschnitt "Dateien" der Komponente hinzu. Überprüfen Sie nicht die Eigenschaft "Selbstregistrierung". Klicken Sie mit der rechten Maustaste auf die DLL-Datei und wählen Sie "Set Key File".

Fügen Sie die TLB-Datei zum Abschnitt "Dateien" der Komponente hinzu. Überprüfen Sie die Eigenschaft "Selbstregistrierung".

Die richtige Version des .Net Framework muss auf dem Ziel-PC vorhanden sein.

Das ist es.

Kieren Johnstone
quelle
Ich verwende InstallSheild 11.5 und sehe die Option ".NET COM Interop" nicht. Die Hilfedatei, die mit 11.5 geliefert wird, besagt, dass sie vorhanden sein sollte, sodass ich nicht weiß, ob ich nur in die falsche Ansicht schaue oder was. Könnten Sie Ihre Antwort aktualisieren, um anzugeben, wo diese Eigenschaft festgelegt ist?
Beth Whitezel
Bei Verwendung von InstallShield 2011 gibt es in der Organisations- / Komponentenstruktur einen Abschnitt für .NET-Einstellungen, der die .NET COM Interop-Einstellung enthält.
Elena Lawrence
Ihre Antwort ist wirklich hilfreich.
Nagendra Upwanshi
Wenn ich versuche, auf die DLL in der VB6-IDE zu verweisen, wird folgende Fehlermeldung angezeigt: "Der angegebenen Datei kann kein Verweis hinzugefügt werden". Soll ich auf die DLL oder die TLB-Datei verweisen?
Ewerton
Tolle Antwort, ich habe den Teil verpasst -tlb -codebase. Ich habe mir ein paar Stunden lang den Kopf darüber kaputt gemacht, bis ich das gefunden habe. Vielen Dank!
Rahul Kishore
9

Als Erweiterung zu @Kieren Johnstones Antwort ein praktisches Codebeispiel müssen:

Von:

public class ApiCaller 
{

    public DellAsset GetDellAsset(string serviceTag, string apiKey)
    {
     ....
    }
}

public class DellAsset
{
    public string CountryLookupCode { get; set; }
    public string CustomerNumber { get; set; }
    public bool IsDuplicate { get; set; }
    public string ItemClassCode { get; set; }
    public string LocalChannel { get; set; }
    public string MachineDescription { get; set; }
    public string OrderNumber { get; set; }
    public string ParentServiceTag { get; set; }
    public string ServiceTag { get; set; }
    public string ShipDate { get; set; }

}

Zu:

[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
[ComVisible(true)]
public interface IComClassApiCaller
{
    IComClassDellAsset GetDellAsset(string serviceTag, string apiKey);

}

[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IComClassApiCallerEvents
{
}

[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"),
    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(IComClassApiCallerEvents))]
[ComVisible(true)]
[ProgId("ProgId.ApiCaller")]
public class ApiCaller : IComClassApiCaller {

    public IComClassDellAsset GetDellAsset(string serviceTag, string apiKey)
    {
        .....
    }
}


[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83E")]
[ComVisible(true)]
public interface IComClassDellAsset
{
     string CountryLookupCode { get; set; }
     string CustomerNumber { get; set; }
     bool IsDuplicate { get; set; }
     string ItemClassCode { get; set; }
     string LocalChannel { get; set; }
     string MachineDescription { get; set; }
     string OrderNumber { get; set; }
     string ParentServiceTag { get; set; }
     string ServiceTag { get; set; }
     string ShipDate { get; set; }

}

[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA70"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IComClassDellAssetEvents
{
}

[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F937"),
    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(IComClassDellAssetEvents))]
[ComVisible(true)]
[ProgId("ProgId.DellAsset")]
public class DellAsset : IComClassDellAsset
{
    public string CountryLookupCode { get; set; }
    public string CustomerNumber { get; set; }
    public bool IsDuplicate { get; set; }
    public string ItemClassCode { get; set; }
    public string LocalChannel { get; set; }
    public string MachineDescription { get; set; }
    public string OrderNumber { get; set; }
    public string ParentServiceTag { get; set; }
    public string ServiceTag { get; set; }
    public string ShipDate { get; set; }

}

Hoffe das spart dir etwas Zeit

Matas Vaitkevicius
quelle
Danke für das Beispiel! Wie könnten Sie GetDellAssetVB6 aufrufen, wenn Sie versuchen, über auf die Klasse zuzugreifen CreateObject?
Panzercrisis
@Panzercrisis Hallo, werfen Sie einen Blick auf diese stackoverflow.com/questions/345178/…
Matas Vaitkevicius
@Matas Vaitkevicius können Sie eine Ressource oder ein Beispiel für die Verwendung von Ereignissen bereitstellen. weil ich nicht herausfinden kann, wie ich mit COM-Ereignissen umgehen soll. schrieb eine Frage hier stackoverflow.com/questions/61060831/…
xurca
5

Die meisten Beispiele im Internet von COM-Servern enthalten nur eine CoClass, und es wird behauptet, dass diese CoClass einen öffentlichen Konstruktor haben muss. Dies ist in diesem Fall der Fall, aber normale Server haben mehr als eine CoClass, von denen nur eine erstellt werden kann, während die Instanzen der nicht erstellbaren CoClasses Eigenschaften der erstellbaren CoClass sind. Betrachten Sie beispielsweise das Word-Objektmodell mit der erstellbaren CoClass Application, deren DocumentsEigenschaft wiederum aus Instanzen der CoClass besteht Document. Der folgende Server verfügt über zwei CoClasses, eine mit einem öffentlichen Konstruktor und eine mit einem privaten Konstruktor.

  1. Erstellen Sie eine Lösung für eine C # -Klassenbibliothek (.Net Framework), nicht für eine Klassenbibliothek (.Net Standard), und nennen Sie sie beispielsweise BankServerCSharp. Wählen Sie diesen Namen mit Bedacht aus, da er der Hauptteil der ProgIDs Ihrer CoClasses und des Namespace-Namens in C ++ ist. Dieser Name wird auch im Dialogfeld Referenzen von C # und VBA aufgeführt.

  2. Löschen Sie den Boilerplate-Code und fügen Sie zwei Dateien Bank.cs und Account.cs hinzu. Geben Sie den folgenden Code ein:

    //Account.cs
    using System.Runtime.InteropServices;
    
    namespace BankServerCSharp
    {
      [ComVisible(true)]  // This is mandatory.
      [InterfaceType(ComInterfaceType.InterfaceIsDual)]
      public interface IAccount
      {
        double Balance { get; } // A property
        void Deposit(double b); // A method
      }
    
      [ComVisible(true)]  // This is mandatory.
      [ClassInterface(ClassInterfaceType.None)]
      public class Account:IAccount
      {
        private  double mBalance = 0;
        private Account() { }     // private constructor, coclass noncreatable
    
        public static Account MakeAccount() { return new Account(); }
        //MakeAccount is not exposed to COM, but can be used by other classes
    
        public double Balance  { get {  return mBalance; } }
        public void Deposit(double b) { mBalance += b; }
      }
    }
    
    //Bank.cs
    using System.Runtime.InteropServices;
    
    namespace BankServerCSharp
    {
      [ComVisible(true)]  // This is mandatory.
      [InterfaceType(ComInterfaceType.InterfaceIsDual)]
      public interface IBank
      {
        string BankName  {  get;  set;  }      // A property
        IAccount FirstAccount { get; }         // Another one of type IDispatch
      }
    
      [ComVisible(true)]  // This is mandatory.
      [ClassInterface(ClassInterfaceType.None)]
      public class Bank:IBank
      {
        private string Name = "";
        private readonly Account First;
    
        public Bank() { First = Account.MakeAccount(); }
    
        public string BankName  {
          get {   return Name; }
          set {   Name= value; }
        }
    
        public IAccount FirstAccount  {
          get { return First; }
        }
      }
    }
    
  3. Erstellen Sie das Projekt mit der Konfiguration Release / Any CPU. Die Ausgabe ist die verwaltete DLL BankServerCSharp.dll im Ordner \ bin \ release.

  4. Jetzt müssen Sie Ihre verwaltete COM-DLL registrieren. Versuchen Sie nicht regsvr32, es gibt ein spezielles Programm namens regasm für verwaltete COM-DLLs. Regasm hat eine Version für 32-Bit- und für 64-Bit-Apps. Öffnen Sie eine Eingabeaufforderung als Administrator und wechseln Sie zu C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319. Dieser Ordner enthält die regasm.exe-App zum Registrieren der verwalteten COM-DLL, als wäre es eine native 32-Bit-COM-DLL.

  5. Typ RegAsm.exe /tlb /codebase path_to_your_bin_release_folder\BankServerCSharp.dll. Sie müssen Ihre DLL auf diese Weise auf jedem Computer registrieren . Vergessen Sie nicht den Schalter / tlb, mit dem die Typbibliothek erstellt wird. Der Compiler kommentiert den Schalter / die Codebasis mit einigen Warnungen, die Sie ignorieren können. Die DLL ist im WoW64-Teil der Registrierung registriert und kann von nativen (nicht verwalteten) 32-Bit-Apps verwendet werden.

  6. Wiederholen Sie nun die Registrierung für die Verwendung der verwalteten COM-DLL durch 64-Bit-Apps. Wechseln Sie zu C: \ Windows \ Microsoft.NET \ Framework 64 \ v4.0.30319 und geben Sie denselben Befehl wie zuvor ein.

  7. Sie können die Registrierung auf Ihrem eigenen PC beschleunigen, indem Sie Visual Studio mit Administratorrechten ausführen und die folgenden Ereignisse nach dem Erstellen hinzufügen:

    %SystemRoot%\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /tlb /codebase "$(TargetPath)"
    %SystemRoot%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /tlb /codebase "$(TargetPath)"
    

Sie können Ihre DLL jetzt wie eine native, nicht verwaltete COM-DLL verwenden. Testen Sie Ihre DLL mit VBA: Aktivieren Sie unter Tools / Referenzen das Kontrollkästchen BankServerCSharp. Wenn es nicht angezeigt wird, ist die Registrierung fehlgeschlagen. Ein einfaches Test-Sub:

Sub TestSOExampleNew()
 On Error GoTo Oops

   Dim BiBiBaBa As New BankServerCSharp.Bank 'New!
   BiBiBaBa.BankName = "Big Bird Bad Bank"
   Dim Account As BankServerCSharp.Account   'No New!
   Set Account = BiBiBaBa.FirstAccount
   Account.Deposit 2000
   MsgBox BiBiBaBa.BankName & ". First client's balance: " & Account.Balance

   Exit Sub

 Oops:
   MsgBox "Sorry, an unexpected error occurred!"
End Sub

Um Ihre verwaltete COM-DLL in C ++ zu testen, erstellen Sie eine neue Konsolenanwendung, fügen Sie den folgenden Code ein und erstellen Sie ihn als Release / x64 oder Release / x86:

#include "stdafx.h"
#import "D:\Aktuell\CSharpProjects\BankServerCSharp\BankServerCSharp\bin\Release\BankServerCSharp.tlb"
//this is the path of my C# project's bin\Release folder

inline void TESTHR(HRESULT x) { if FAILED(x) _com_issue_error(x); };

int main()
{
  try
  {
    TESTHR(CoInitialize(0));
    BankServerCSharp::IBankPtr BankPtr = nullptr;
    TESTHR(BankPtr.CreateInstance("BankServerCSharp.Bank"));
    BankPtr->BankName = L"Ernie First Global Bank";
    BankServerCSharp::IAccountPtr AccountPtr = BankPtr->FirstAccount;
    TESTHR(AccountPtr->Deposit(200.09));
    wprintf(L"Name: %s, Balance: %.2f\n", (LPCWSTR)BankPtr->BankName, AccountPtr->Balance);
  }
  catch (const _com_error& e)
  {
    CStringW out;
    out.Format(L"Exception occurred. HR = %lx, error = %s", e.Error(), e.ErrorMessage());
    MessageBoxW(NULL, out, L"Error", MB_OK);
  }

  CoUninitialize();// Uninitialize COM
  return 0;
}
Dietrich Baumgarten
quelle
0

Microsoft hat einen schnellen , wie man hierher . Sie müssen jedoch die DLL registrieren. Verwenden Sie diese Methode, die hier ausführlich beschrieben wird, um Ihre C # -Dll einer einfachen C-DLL (C # Unmanaged Exports) ähnlicher zu machen .

pcunite
quelle