Подтвердить что ты не робот

Поверните простую С# DLL в компонент COM-взаимодействия

Как создать DLL С# в DLL-библиотеку COM, которая может быть использована приложением VB6?

4b9b3361

Ответ 1

Это ответ, который я хотел найти в StackOverflow, но не смог. Оказывается, довольно легко превратить простую С# -dll в COM-dll.

Чтобы создать С# dll

Создайте решение с помощью проекта класса С#. Класс должен иметь интерфейс для свойств/методов и интерфейс для событий. Присвоить атрибуты GUID классу и интерфейсам, как описано в MSDN - Пример COM-класса (Руководство по программированию на С#). Также см.: MSDN - Как: События, обрабатываемые COM-раковиной.

В свойствах проектa > вкладка "Приложение" > "Информация о сборке" > "Сделать сборку COM-видимой". Это делает видимыми все общедоступные методы в классе COM.

В свойствах проектa > вкладка "Сборка" > "Установить цель платформы" на x86.

Это все, что вам нужно сделать для создания DLL. Чтобы вызвать DLL, вам необходимо зарегистрировать его.

Регистрация DLL на компьютере разработки

Вы можете зарегистрировать DLL одним из следующих способов:

  • Проверить свойства проектa > вкладка "Сборка" > "Регистрация для COM-взаимодействия". Это автоматически зарегистрирует DLL при его создании.
  • Вручную зарегистрируйте DLL с помощью RegAsm. Это позволяет вам зарегистрировать DLL в каталоге по вашему выбору, а не в каталоге сборки. Это метод, который я использовал.

    • Не проверяйте свойства проектa > вкладка "Строить" > "Регистрация для COM-взаимодействия"
    • Скопируйте DLL в каталог, в который вы хотите зарегистрировать его.
    • Откройте командную оболочку с правами администратора и введите

      RegAsm.exe -tlb -codebase mydll.dll
      

      RegAsm.exe можно найти в "C:\Windows\Microsoft.NET\Framework\v2.0.50727", а "mydll.dll" - это имя вашей DLL; tlb означает "создать библиотеку типов"; codebase означает "записать расположение каталога в реестр, если он не помещается в GAC".

      RegAsm отобразит предупреждение о том, что сборка должна быть сильной. Вы можете игнорировать это.

      На этом этапе вы сможете добавить ссылку на COM-DLL в VB6, увидеть ее с помощью Intellisense и запустить ее точно так же, как обычная COM-библиотека DLL.

Установка DLL с помощью InstallShield

Если вы используете InstallShield для установки DLL вместе с остальной частью вашего приложения, выполните следующие действия.

В InstallShield добавьте новый компонент в список компонентов. Не забудьте связать компонент с функцией. Установите свойство компонента ".NET COM Interop" на "Да".

Добавьте файл .dll в раздел "Файлы" Компонента. Не проверяйте свойство Self-Register. Щелкните правой кнопкой мыши файл .dll и выберите "Установить файл ключа".

Добавьте файл .tlb в раздел "Файлы" Компонента. Проверьте свойство Self-Register.

Правильная версия .NET Framework должна существовать на целевом ПК.

Что это.

Ответ 2

Как расширение @Kieren Johnstone, ответьте на практический пример кода о модификациях классов, которые вам нужно сделать:

От:

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; }

}

Для того, чтобы:

[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; }

}

Надеюсь, это сэкономит вам время

Ответ 3

Большинство примеров в Интернете серверов COM содержат только один CoClass, и утверждается, что этот CoClass должен иметь открытый конструктор. Это верно в этом случае, но обычные серверы имеют более одного CoClass, из которых может быть создан только один, тогда как экземпляры не-создаваемых CoClasses являются свойствами создаваемого CoClass. Например, рассмотрим объектную модель Word с создаваемым CoClass Application, обладающим свойством Documents, которое, в свою очередь, состоит из экземпляров CoClass Document. На следующем сервере есть два класса CoClass, один с открытым конструктором, а другой с закрытым конструктором.

  1. Создайте решение для библиотеки классов С# (.Net Framework), а не библиотеки классов (.Net Standard) и назовите ее, например, BankServerCSharp. Выбирайте это имя разумно, потому что оно будет основной частью ProgID ваших CoClasses и имени пространства имен в C++. Это имя также будет указано в диалоговом окне "Ссылки" в С# и VBA.

  2. Удалите шаблонный код и добавьте два файла Bank.cs и Account.cs. Вставьте следующий код:

    //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. Постройте проект с конфигурацией Release/Any CPU. Выводом является управляемая DLL BankServerCSharp.dll, расположенная в папке \bin\release.

  4. Теперь вы должны зарегистрировать управляемую COM DLL. Не пытайтесь использовать regsvr32, есть специальная программа regasm для управляемых библиотек COM. Regasm имеет версию для 32-битных и 64-битных приложений. Откройте командную строку с правами администратора и перейдите в C:\Windows\Microsoft.NET\Framework\v4.0.30319. Эта папка содержит приложение regasm.exe для регистрации управляемой COM DLL, как если бы это была собственная 32-битная COM DLL.

  5. Введите RegAsm.exe /tlb /codebase path_to_your_bin_release_folder\BankServerCSharp.dll. Таким образом, вы должны зарегистрировать свою DLL на любом компьютере. Не забудьте параметр /tlb, который создает библиотеку типов. Компилятор прокомментирует переключатель/кодовую базу с некоторыми предупреждениями, которые вы можете игнорировать. Библиотека DLL зарегистрирована в части реестра WoW64 и может использоваться собственными (неуправляемыми) 32-разрядными приложениями.

  6. Теперь повторите регистрацию для использования управляемой COM DLL 64-битными приложениями. Перейдите в C:\Windows\Microsoft.NET\Framework 64\ v4.0.30319 и введите ту же команду, что и раньше.

  7. Вы можете ускорить регистрацию на своем ПК, запустив Visual Studio с административными правами и добавив следующие события после сборки:

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

Теперь вы можете использовать вашу DLL как собственную неуправляемую COM DLL. Проверьте свою DLL с помощью VBA: в разделе Инструменты/Ссылки отметьте BankServerCSharp. Если это не показано, регистрация не удалась. Простой тестовый саб:

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 balance: " & Account.Balance

   Exit Sub

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

Чтобы проверить управляемую COM-библиотеку в C++, создайте новое консольное приложение, вставьте следующий код и создайте его как Release/x64 или Release/x86:

#include "stdafx.h"
#import "D:\Aktuell\CSharpProjects\BankServerCSharp\BankServerCSharp\bin\Release\BankServerCSharp.tlb"
//this is the path of my C# project 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;
}

Ответ 4

У Microsoft есть быстрый способ здесь. Однако вам нужно будет зарегистрировать dll. Чтобы сделать вашу С# dll более похожей на простую C dll (С# Unmanaged Exports), используйте этот метод, который подробно описывается здесь.

Ответ 5

COM Interop - это технология, включенная в .NET Framework Common Language Runtime (CLR), которая позволяет объектам объектной модели (COM) взаимодействовать с объектами .NET и наоборот. COM Interop стремится обеспечить доступ к существующим компонентам COM, не требуя изменения исходного компонента и наоборот. Больше на: http://www.writeulearn.com/com-interop-using-csharp/