我在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调用的异常处理存在问题,所以调试器给我的唯一信息是"异常发生&#34 ;)
由于我能够以相同的方式从同一个BCB4应用程序调用其他COM对象,所以我不认为问题出在我的C ++代码上。我认为这是C#创建的COM DLL或其注册的问题。
为了探索这一点,我使用Microsoft OLE / COM对象查看器来浏览我的系统中的OLE对象。我能够按预期找到我的对象" TRSDotNetCOM.TRSCOM_Class&#34 ;.
我是使用OLE / COM对象查看器的新手,所以我希望我正在查看以下正确的内容:
当我扩展课程时,我看到以下内容:
我右键单击_Object并选择"查看",然后"查看类型信息"。然后,右侧的窗格显示:
[ 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和Paul的更新代码。 (但它仍然不起作用)
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;
}
}
}
答案 0 :(得分:2)
你只缺少几个。
将您的接口声明为ComVisible
:
[ComVisible(true)]
public interface TRSCOM_Interface
如果您的程序集默认情况下已经可以看到COM(您可以在项目的属性中或通常在AssemblyInfo.cs中查看),那么您不需要这样做,但它没有任何危害,如果您还原此配置,它会保持regasm.exe
和tlbexp.exe
的界面可用。
将事件接口声明为ComImport
:
[ComImport]
public interface TRSCOM_Events
我的猜测是,这个接口是在C#项目之外定义的,可能是由BCB4应用程序或其中一个模块定义的。
如果我猜错了,你的C#项目定义了这个界面,那么[ComVisible(true)]
。
如果此界面有事件方法,那么您需要implement then as events in the class。
最后,为避免为您的班级导出其他界面,您可能需要添加ClassInterface
属性:
[ClassInterface(ClassInterfaceType.None)]
public class TRSCOM_Class : TRSCOM_Interface
这样,您就知道TRSCOM_Interface
是您的类默认界面,因为它是您实现的第一个界面,regasm.exe /tlb
无法生成类接口。< / p>
根据实现的接口的顺序不能让人放心,所以你也可以使用ComDefaultInterface
属性:
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(TRSCOM_Interface))]
public class TRSCOM_Class : TRSCOM_Interface
现在,您可以在已实现的接口列表中获得任何顺序,而无需担心更改默认类接口。
答案 1 :(得分:0)
这是我第一次看到一个声明为ComVisible的方法。我会放弃它,而是声明TRSCOM_Interface接口ComVisible。