如何将C#DLL转换为VB6应用程序可以使用的COM互操作DLL?
如何将C#DLL转换为VB6应用程序可以使用的COM互操作DLL?
Answers:
这是我想在StackOverflow中找到的答案,但是找不到。事实证明,将简单的C#dll转换为COM dll相当容易。
使用C#类项目创建解决方案。该类应具有用于属性/方法的接口和用于事件的接口。如MSDN-示例COM类(C#编程指南)中所述,将GUID属性分配给类和接口。另请参见:MSDN-如何:引发由COM Sink处理的事件。
在项目属性>应用程序选项卡>程序集信息按钮中>选中“使程序集COM可见”。这使类COM中的所有公共方法可见。
在项目属性>生成选项卡中>将“平台目标”设置为x86。
这就是创建DLL所需要做的全部工作。要调用DLL,您需要对其进行注册。
您可以通过以下方式之一注册DLL:
向RegAsm手动注册DLL。这使您可以在所选目录中而不是在构建目录中注册DLL。这是我使用的方法。
使用管理员权限打开命令外壳,然后键入
RegAsm.exe -tlb -codebase mydll.dll
可以在“ C:\ Windows \ Microsoft.NET \ Framework \ v2.0.50727”中找到RegAsm.exe,而“ mydll.dll”是您的DLL的名称。tlb
表示“创建类型库”;
codebase
表示“假设目录位置未放入GAC中,则将其写入注册表”。
RegAsm将显示警告,该程序集应使用强名称。您可以忽略它。
在这一点上,您应该能够在VB6中添加对COM DLL的引用,并通过Intellisense对其进行查看,并像常规的COM DLL一样运行它。
如果您正在使用InstallShield与其他应用程序一起安装DLL,请执行以下操作。
在InstallShield中,向“组件”列表中添加一个新的组件。请记住将组件与功能关联。将组件属性“ .NET COM Interop”设置为“是”。
将.dll文件添加到组件的“文件”部分。不要检查“自注册”属性。右键单击.dll文件,然后选择“设置密钥文件”。
将.tlb文件添加到组件的“文件”部分。检查“自注册”属性。
.Net Framework的正确版本需要在目标PC上存在。
而已。
-tlb -codebase
。数小时将我的头弄坏了,直到我找到了。谢谢!
作为@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; }
}
希望这可以节省您一些时间
GetDellAsset
如果您试图通过VB6来访问课程,那么如何调用VB6 CreateObject
?
COM服务器互联网中的大多数示例仅包含一个CoClass,并且据称该CoClass必须具有公共构造函数。在这种情况下确实如此,但是普通服务器具有多个CoClass,其中只能创建一个,而不可创建的CoClass的实例是可创建的CoClass的属性。例如,考虑具有可创建CoClass的Word对象模型,该对象模型Application
具有的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; }
}
}
}
使用配置发布/任何CPU生成项目。输出是位于\ bin \ release文件夹中的托管DLL BankServerCSharp.dll。
现在,您必须注册您的托管COM DLL。不要尝试regsvr32,对于托管的COM DLL,有一个名为regasm的特殊程序。Regasm具有适用于32位和64位应用程序的版本。以管理员身份打开命令提示符,然后更改为C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319。此文件夹包含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 \ Framework 64 \ v4.0.30319并键入与以前相同的命令。
您可以通过运行具有管理权限的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;
}