COM 互操作,客户端未在进程外 COM 中找到接口
我使用 Microsoft 的 CSExeCOMServer 作为设置进程外 COM 的基础服务器,但无法正常工作。服务器是64位的,客户端是32位的。
这是示例接口
[Guid(XXCryptService.InterfaceId), ComVisible(true)/*, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)*/]
public interface IXXCryptService
{
[DispId(1)] string Encrypt(string password, string key);
[DispId(2)] string Decrypt(string password, string key);
}
和类
[ClassInterface(ClassInterfaceType.None)]
[Guid(XXCryptService.ClassId), ComVisible(true)]
public class XXCryptService : ReferenceCountedObject, IXXCryptService
{
internal const string ClassId =
"C5F6938B-5593-4872-B8C7-B47EE33EABCD";
internal const string InterfaceId =
"6990FF5F-22E2-4032-8B98-36115DBCEFFF";
[EditorBrowsable(EditorBrowsableState.Never)]
[ComRegisterFunction()]
public static void Register(Type t)
{
try
{
COMHelper.RegasmRegisterLocalServer(t);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
[ComUnregisterFunction()]
public static void Unregister(Type t)
{
try
{
COMHelper.RegasmUnregisterLocalServer(t);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
}
public string Encrypt(string password, string key)
{
return "Encrypted";
}
public string Decrypt(string password, string key)
{
return "Decrypted";
}
}
程序运行,但是当客户端连接时,在服务器触发 ObjectClassFactory 上的 CreateInstance 并使用 Marshal.GetComInterfaceForObject(new XXCryptService(), typeof(IXXCryptService) 返回 ppvObject 上的对象后,它在客户端上崩溃)) 并返回 0。
在 .NET 上运行客户端会触发“无法将类型为‘COMTest.XXCryptService’的 COM 对象转换为接口类型“COMTest.IXXCryptService”。此操作失败,因为对 IID 为“{6990FF5F-22E2-4032-8B98-36115DBCEFFF}”的接口的 QueryInterface 调用因以下错误而失败:未找到元素。 HRESULT:0x8002802B(TYPE_E_ELEMENTNOTFOUND))。”。
[Guid("6990FF5F-22E2-4032-8B98-36115DBCEFFF")]
//[InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IXXCryptService
{
[DispId(1)] string Encrypt(string password, string key);
[DispId(2)] string Decrypt(string password, string key);
}
[ComImport, Guid("C5F6938B-5593-4872-B8C7-B47EE33EABCD")]
class XXCryptService
{
}
class Program
{
static void Main(string[] args)
{
XXCryptService cs = new XXCryptService();
IXXCryptService ics = (IXXCryptService) cs;
Console.WriteLine(ics.Encrypt("Test","Test"));
Console.ReadKey();
}
}
在 Delphi 上运行客户端会触发 EIntfCastError 异常,并显示消息“接口不支持”。 COM是用“导入类型库”导入的,这样使用。
procedure TForm1.FormCreate(Sender: TObject);
begin
FCrypter := CoXXCryptService.Create;
end;
TLB 界面看起来像这样
IXXCryptService = interface(IDispatch)
['{6990FF5F-22E2-4032-8B98-36115DBCEFFF}']
function Encrypt(const password: WideString; const key: WideString): WideString; safecall;
function Decrypt(const password: WideString; const key: WideString): WideString; safecall;
end;
// *********************************************************************//
// DispIntf: IXXCryptServiceDisp
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {6990FF5F-22E2-4032-8B98-36115DBCEFFF}
// *********************************************************************//
IXXCryptServiceDisp = dispinterface
['{6990FF5F-22E2-4032-8B98-36115DBCEFFF}']
function Encrypt(const password: WideString; const key: WideString): WideString; dispid 1;
function Decrypt(const password: WideString; const key: WideString): WideString; dispid 2;
end;
我已经通过注册表检查过,一切似乎都已正确注册,所以我不明白为什么我会遇到这个问题。
这里有人知道可能出现什么问题吗?
编辑:以 64 位编译客户端并且工作正常。另外,它引用了错误的路径,在我调整它之后,我在 .NET x86 客户端上遇到了不同的错误
此操作失败,因为 COM 组件上针对 IID '{6990FF5F-22E2-4032-8B98-36115DBCEFFF 的接口的 QueryInterface 调用}' 由于以下错误而失败:加载类型库/DLL 时出错。 (HRESULT 异常:0x80029C4A (TYPE_E_CANTLOADLIBRARY))
I'm using Microsoft's CSExeCOMServer as a base for setting up a Out-of-process COM server, but it's not working properly. The server is 64 bit, and the client is 32 bit.
Here's the sample interface
[Guid(XXCryptService.InterfaceId), ComVisible(true)/*, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)*/]
public interface IXXCryptService
{
[DispId(1)] string Encrypt(string password, string key);
[DispId(2)] string Decrypt(string password, string key);
}
And the class
[ClassInterface(ClassInterfaceType.None)]
[Guid(XXCryptService.ClassId), ComVisible(true)]
public class XXCryptService : ReferenceCountedObject, IXXCryptService
{
internal const string ClassId =
"C5F6938B-5593-4872-B8C7-B47EE33EABCD";
internal const string InterfaceId =
"6990FF5F-22E2-4032-8B98-36115DBCEFFF";
[EditorBrowsable(EditorBrowsableState.Never)]
[ComRegisterFunction()]
public static void Register(Type t)
{
try
{
COMHelper.RegasmRegisterLocalServer(t);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
[ComUnregisterFunction()]
public static void Unregister(Type t)
{
try
{
COMHelper.RegasmUnregisterLocalServer(t);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
}
public string Encrypt(string password, string key)
{
return "Encrypted";
}
public string Decrypt(string password, string key)
{
return "Decrypted";
}
}
The program runs, but when a client connects it crashes on the client after the server has triggered CreateInstance on the ObjectClassFactory, and returned the object on ppvObject with Marshal.GetComInterfaceForObject(new XXCryptService(), typeof(IXXCryptService)) and returned 0.
Running the client on .NET triggers a "Unable to cast COM object of type 'COMTest.XXCryptService' to interface type 'COMTest.IXXCryptService'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{6990FF5F-22E2-4032-8B98-36115DBCEFFF}' failed due to the following error: Element not found. (Exception from HRESULT: 0x8002802B (TYPE_E_ELEMENTNOTFOUND)).".
[Guid("6990FF5F-22E2-4032-8B98-36115DBCEFFF")]
//[InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IXXCryptService
{
[DispId(1)] string Encrypt(string password, string key);
[DispId(2)] string Decrypt(string password, string key);
}
[ComImport, Guid("C5F6938B-5593-4872-B8C7-B47EE33EABCD")]
class XXCryptService
{
}
class Program
{
static void Main(string[] args)
{
XXCryptService cs = new XXCryptService();
IXXCryptService ics = (IXXCryptService) cs;
Console.WriteLine(ics.Encrypt("Test","Test"));
Console.ReadKey();
}
}
Running the client on Delphi triggers an exception in EIntfCastError with message 'Interface not supported'. COM is imported with "Import Type Library" and used like this.
procedure TForm1.FormCreate(Sender: TObject);
begin
FCrypter := CoXXCryptService.Create;
end;
TLB interface looks like this
IXXCryptService = interface(IDispatch)
['{6990FF5F-22E2-4032-8B98-36115DBCEFFF}']
function Encrypt(const password: WideString; const key: WideString): WideString; safecall;
function Decrypt(const password: WideString; const key: WideString): WideString; safecall;
end;
// *********************************************************************//
// DispIntf: IXXCryptServiceDisp
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {6990FF5F-22E2-4032-8B98-36115DBCEFFF}
// *********************************************************************//
IXXCryptServiceDisp = dispinterface
['{6990FF5F-22E2-4032-8B98-36115DBCEFFF}']
function Encrypt(const password: WideString; const key: WideString): WideString; dispid 1;
function Decrypt(const password: WideString; const key: WideString): WideString; dispid 2;
end;
I have checked through the registry, and everything seems to be registered properly, so I don't understand why I should get this problem.
Anyone here have any clue on what might be the problem?
Edit: Compiled the client in 64bit and that's working properly. Also, it referenced the wrong path, after I adjusted it I got a different error on the .NET x86 client
This operation failed because the QueryInterface call on the COM component for the interface with IID '{6990FF5F-22E2-4032-8B98-36115DBCEFFF}' failed due to the following error: Error loading type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是注册问题,而且事实上,只有当程序集具有与 regasm 相同的目标时,regasm 才会执行。 regasm 应该有一个“/com_oop 之类的”参数,使其注册 LocalServer32 而不是 InprocServer32,并在 64 位系统上同时注册 32 位和 64 位。
为了解决这个问题,我必须暂时将可执行文件(具有相同的路径)编译为32位,运行32位regasm(使用/tlb:..),然后编译回64位,再次运行64位regasm(使用/tlb:..) ),现在对于 64 位可执行文件来说,它可以在 32 位和 64 位上正常工作。
CSExeComServer 有一个注册和注销方法,可以手动删除 InprocServer32 密钥并添加 LocalServer32。为了确保它正常工作,我将更改它,检测它是否在 64 位系统上注册,然后让它在那里正确注册。完成后,我将发布对注册方法所做的更改。
This was a problem with registration and the fact that regasm only execute if the assembly has the same target as regasm. There should be a "/com_oop or something" parameter for regasm to make it register LocalServer32 instead of InprocServer32 and register it for both 32 and 64bit on 64bit systems.
To get around this I had to temporarily compile the executable (with the same path) to 32bit, run the 32bit regasm (with /tlb:..), then compile back to 64bit, run 64bit regasm (with /tlb:.. again), and now it works properly for both 32 and 64bit against the 64bit executable.
CSExeComServer has a register and unregister method where it manually delete the InprocServer32 key and adds a LocalServer32. To make sure this works properly I'm going to change this one, detect if it's registering on a 64bit system, and then have it register this properly there. I'll post the changes I did to the register method when I'm done.
这个问题可能也用这个解决了(有同样的问题,但从 64 位客户端访问 32 位服务器的另一种方式,那么你可以使用 CLSCTX_ACTIVATE_32_BIT_SERVER 代替):
This problem is probably solved with this as well (had the same problem but the other way around accessing a 32bit server from a 64bit client then you can use CLSCTX_ACTIVATE_32_BIT_SERVER instead):
根据您与 IXXCryptService 接口的需要,尝试添加 [ClassInterface(ClassInterfaceType.AutoDispatch)] 或 [ClassInterface(ClassInterfaceType.AutoDual)]
Try adding [ClassInterface(ClassInterfaceType.AutoDispatch)] or [ClassInterface(ClassInterfaceType.AutoDual)] depending on your needs to interface IXXCryptService
我认为在所有情况下通过 COM 混合 32 位和 64 位进程都会失败。
为了能够从 32 位 Delphi 进程访问,DotNet 程序集必须编译为 x86(即 32 位模式),而不是 x64。
AFAIK COM 不会跨越 32/64 位边界。
为了在 64 位和 32 位之间进行通信,您需要另一个技巧,例如 是否可以从 32 位应用程序访问 64 位 dll?
I think mixing 32 and 64 bit process via COM will fail, in all cases.
In order to be accessible from a 32 bit Delphi process, the DotNet assembly has to be compiled as x86 (i.e. in 32 bit mode), not as x64.
AFAIK COM won't cross the 32/64 bit boundary.
In order to communicate between 64 and 32 bit, you would need another trick like the one published in Is it possible to access a 64-bit dll from a 32-bit application?