我用 C# 编写了一个简单的 COM 对象,只有一个方法,称为 GetMac。我无法让它工作。我正在尝试从旧版 Borland C++ Builder 4 (BCB4) 应用程序访问它,我知道该应用程序很旧,并且不再使用太多,但我可以从中访问其他 COM 对象。
Borland 开发机器运行的是 Windows XP,因此我使 C# COM 对象面向 .NET 4.0 框架。我将 DLL 和 PDB 文件从 C# Visual Studio 计算机复制到 XP 计算机。我通过以下命令注册了它:
"%WINDIR%\Microsoft.NET\Framework\v4.0.30319\regasm.exe" TRSDotNetCOM.dll /tlb /nologo /codebase
我可以通过以下代码行很好地实例化 COM 对象(类):
Variant TDN = CreateOleObject("TRSDotNetCOM.TRSCOM_Class");
如果我更改名称字符串,它就不起作用,所以我知道我的这部分是正确的。
但是,当我尝试按如下方式调用该方法时:
MacV = TDN.OleFunction(funcNameV,counterV,macKeyV);
...我收到运行时异常(不幸的是,BCB4 对 OLE 调用的异常处理存在问题,因此调试器给我的唯一信息是“发生异常”)。
由于我能够以相同的方式从同一 BCB4 应用程序调用其他 COM 对象,因此我认为问题不在于我的 C++ 代码。我认为这是 C# 创建的 COM DLL 或其注册的问题。
为了探索这一点,我使用 Microsoft OLE/COM 对象查看器来浏览我的系统以查找 OLE 对象。正如预期的那样,我能够找到“TRSDotNetCOM.TRSCOM_Class”对象。
我在使用 OLE/COM 对象查看器方面是全新的,所以我希望我正在查看以下正确的内容:
当我扩展类(class)时,我看到以下内容:
我右键单击 _Object 并选择“查看”,然后选择“查看类型信息”。然后,右侧 Pane 显示:
[ uuid(65074F7F-63C0-304E-AF0A-D51741CB4A8D), hidden, dual, nonextensible,
custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "System.Object")
] dispinterface _Object {
properties:
methods:
[id(00000000), propget,
custom({54FC8F55-38DE-4703-9C4E-250351302B1C}, "1")]
BSTR ToString();
[id(0x60020001)]
VARIANT_BOOL Equals([in] VARIANT obj);
[id(0x60020002)]
long GetHashCode();
[id(0x60020003)]
_Type* GetType(); };
当我展开左侧的树时,我看到的是:
我没有看到我的方法“GetMac”在其中列出。因此,我认为该方法对 COM 不可见,或者它没有通过 regasm 注册。
这是 COM 对象的源代码:
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
namespace TRSDotNetCOM
{
[Guid("80ef9acd-3a75-4fcd-b841-11199d827e8f")]
public interface TRSCOM_Interface
{
[DispId(1)]
string GetMac(string counter, string macKey);
}
// Events interface Database_COMObjectEvents
[Guid("67bd8422-9641-4675-acda-3dfc3c911a07"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface TRSCOM_Events
{
}
[Guid("854dee72-83a7-4902-ab50-5c7a73a7e17d"),
ClassInterface(ClassInterfaceType.None),
ComVisible(true),
ComSourceInterfaces(typeof(TRSCOM_Events))]
public class TRSCOM_Class : TRSCOM_Interface
{
public TRSCOM_Class()
{
}
[ComVisible(true)]
public string GetMac(string counter, string macKey)
{
// convert counter to bytes
var counterBytes = Encoding.UTF8.GetBytes(counter);
// import AES 128 MAC_KEY
byte[] macKeyBytes = Convert.FromBase64String(macKey);
var hmac = new HMACSHA256(macKeyBytes);
var macBytes = hmac.ComputeHash(counterBytes);
var retval = Convert.ToBase64String(macBytes);
return retval;
}
}
}
我确实确认并进入项目属性并选中“注册 COM 互操作”复选框。我还使用“sn”实用程序生成了一个安全名称文件,并将该文件加载到设置的“签名”部分中。
所以...
1) 我是否在 OLE/COM 对象查看器中的正确位置查找我的方法?
2)如果是这样,为什么我的方法不可见或未注册?
3)还有什么想法可能是错误的吗?
更新:这是根据 Joe W 和 Paulo 的建议更新的代码。 (但是仍然不起作用)
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
namespace TRSDotNetCOM
{
[Guid("80ef9acd-3a75-4fcd-b841-11199d827e8f"),
ComVisible(true)]
public interface TRSCOM_Interface
{
[DispId(1)]
string GetMac(string counter, string macKey);
}
// Events interface Database_COMObjectEvents
[Guid("67bd8422-9641-4675-acda-3dfc3c911a07"),
ComImport,
ComVisible(true),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface TRSCOM_Events
{
}
[Guid("854dee72-83a7-4902-ab50-5c7a73a7e17d"),
ClassInterface(ClassInterfaceType.None),
ComDefaultInterface(typeof(TRSCOM_Interface)),
ComVisible(true),
ComSourceInterfaces(typeof(TRSCOM_Events))]
public class TRSCOM_Class : TRSCOM_Interface
{
public TRSCOM_Class()
{
}
public string GetMac(string counter, string macKey)
{
// convert counter to bytes
var counterBytes = Encoding.UTF8.GetBytes(counter);
// import AES 128 MAC_KEY
byte[] macKeyBytes = Convert.FromBase64String(macKey);
var hmac = new HMACSHA256(macKeyBytes);
var macBytes = hmac.ComputeHash(counterBytes);
var retval = Convert.ToBase64String(macBytes);
return retval;
}
}
}
最佳答案
你只遗漏了一些内容。
将您的接口(interface)声明为 ComVisible
:
[ComVisible(true)]
public interface TRSCOM_Interface
如果您的程序集默认情况下已经是 COM 可见的(您可以在项目的属性中或通常在 AssemblyInfo.cs 中检查这一点),则不需要执行此操作,但这不会造成任何损害,并且会保留接口(interface)可用于 regasm.exe
和 tlbexp.exe
,以防您恢复此配置。
将事件接口(interface)声明为 ComImport
:
[ComImport]
public interface TRSCOM_Events
我的猜测是,该接口(interface)是在 C# 项目之外定义的,可能是由 BCB4 应用程序或其模块之一定义的。
如果我的猜测是错误的,并且您的 C# 项目是定义此接口(interface)的项目,则使用 [ComVisible(true)]
。
如果这个接口(interface)有事件方法,那么 implement then as events in the class .
最后,为了避免为您的类导出另一个接口(interface),您可能需要添加 ClassInterface
属性:
[ClassInterface(ClassInterfaceType.None)]
public class TRSCOM_Class : TRSCOM_Interface
这样,您就可以告诉 TRSCOM_Interface
是您的类默认接口(interface),因为它是您实现的第一个接口(interface),而 regasm.exe/tlb
不会生成一个类接口(interface)。
依赖于实现接口(interface)的顺序并不令人放心,因此您还可以使用 ComDefaultInterface
属性:
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(TRSCOM_Interface))]
public class TRSCOM_Class : TRSCOM_Interface
现在,您可以在已实现的接口(interface)列表中按任意顺序排列,而不必担心更改默认类接口(interface)。
关于c# - 用 C# 编写的 COM 对象 - 获取类,但不获取方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29134942/