将一个简单的C#DLL转换为COM互操作组件
本文关键字:转换 C#DLL COM 组件 互操作 简单 一个 | 更新日期: 2023-09-27 18:05:56
如何将C#DLL制作成可供VB6应用程序使用的COM互操作DLL?
这是我想在StackOverflow中找到的答案,但无法找到。事实证明,将一个简单的C#dll转换为COM dll是相当容易的。
创建C#dll
使用C#类项目创建一个解决方案。类应该有一个用于属性/方法的接口和一个用于事件的接口。将GUID属性分配给类和接口,如MSDN-示例COM类(C#编程指南(中所述。另请参阅:MSDN-如何:引发COM接收器处理的事件。
在"项目属性">"应用程序"选项卡>"程序集信息"按钮>选中"使程序集COM可见"。这使得类COM中的所有公共方法都可见。
在项目属性>构建选项卡>将"平台目标"设置为x86。
这就是创建DLL所需要做的全部工作。要调用DLL,您需要注册它。
在开发机器上注册DLL
您可以通过以下方式之一注册DLL:
- 选中项目属性>构建选项卡>"注册COM互操作"。这将在您构建DLL时自动注册它
-
使用RegAsm手动注册DLL。这允许您在自己选择的目录中注册DLL,而不是在构建目录中。这就是我使用的方法。
- 不要检查项目属性>构建选项卡>"注册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将显示一条警告,说明程序集应该是强名称的。你可以忽略它。
此时,您应该能够在VB6中添加对COM DLL的引用,使用Intellisense查看它,并像运行常规COM DLL一样运行它。
使用InstallShield安装DLL
如果使用InstallShield将DLL与应用程序的其余部分一起安装,请执行以下操作。
在InstallShield中,将一个新组件添加到组件列表中。请记住将零部件与特征相关联。将组件属性".NET COM Interop"设置为"是"。
将.dll文件添加到组件的"文件"部分。不要检查"自注册"属性。右键单击.dll文件并选择"设置密钥文件"。
将.tlb文件添加到组件的"文件"部分。检查"自注册"属性。
的正确版本。Net Framework需要存在于目标PC上。
就是这样。
作为@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; }
}
希望这能为您节省一些时间
COM服务器互联网中的大多数示例只包含一个CoClass,并且声称该CoClass必须具有公共构造函数。在这种情况下是这样的,但普通服务器有多个CoClass,其中只能创建一个,而不可创建的CoClass的实例是可创建CoClass的属性。例如,考虑具有可创建的CoClass Application
的Word对象模型,该对象模型具有Documents
属性,该属性又由CoClass Document
的实例组成。以下服务器有两个CoClass,一个带有公共构造函数,另一个带有私有构造函数。
-
为C#类库(.Net Framework(而不是类库(.Net Standard(创建一个解决方案,并将其命名为BankServerCSharp。明智地选择这个名称,因为它将是CoClasses的ProgID和C++中的命名空间名称的主要部分。此名称也将列在C#和VBA的"参照"对话框中。
-
删除样板代码并添加两个文件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; } } } }
-
使用配置Release/Any CPU构建项目。输出是位于''bin''release文件夹中的托管DLL BankServerCSharp.DLL。
-
现在您必须注册托管COM DLL。不要尝试regsvr32,有一个名为regasm的特殊程序用于托管COM DLL。Regasm有一个32位和64位应用程序的版本。以管理员身份打开命令提示符,然后更改为C:''Windows''Microsoft。NET''Framework''v4.0.30119。此文件夹包含regasm.exe应用程序,用于注册托管COM DLL,就好像它是本机32位COM DLL一样。
-
键入
RegAsm.exe /tlb /codebase path_to_your_bin_release_folder'BankServerCSharp.dll
您必须以这种方式在任何计算机上注册DLL。不要忘记创建类型库的/tlb开关。编译器将在开关/代码库中注释一些可以忽略的警告。DLL在注册表的WoW64部分注册,可由本机(非托管(32位应用程序使用。 -
现在重复注册64位应用程序使用托管COM DLL。更改为C:''Windows''Microsoft。NET''Framework64''v4.0.30119并键入与以前相同的命令。
-
通过使用管理权限运行Visual Studio并添加以下生成后事件,您可以在自己的PC上加速注册:
%SystemRoot%'Microsoft.NET'Framework'v4.0.30319'RegAsm.exe /tlb /codebase "$(TargetPath)" %SystemRoot%'Microsoft.NET'Framework64'v4.0.30319'RegAsm.exe /tlb /codebase "$(TargetPath)"
现在,您可以像使用本机非托管COM DLL一样使用DLL。用VBA测试DLL:在"工具/引用"下勾选"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's balance: " & Account.Balance
Exit Sub
Oops:
MsgBox "Sorry, an unexpected error occurred!"
End Sub
要在C++中测试托管COM DLL,请创建一个新的控制台应用程序,插入以下代码并构建为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'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;
}