4

我使用 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 的接口的 COM 组件调用 QueryInterface '{6990FF5F- 22E2-4032-8B98-36115DBCEFFF}' 由于以下错误而失败:未找到元素。(来自 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 客户端上遇到了不同的错误

此操作失败,因为 IID 为“{6990FF5F-22E2-4032-8B98-36115DBCEFFF}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:加载类型库/DLL 时出错。(来自 HRESULT 的异常:0x80029C4A (TYPE_E_CANTLOADLIBRARY))

4

4 回答 4

1

这是注册的问题,并且只有当程序集具有与 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 位系统上注册,然后让它在那里正确注册。完成后,我将发布我对 register 方法所做的更改。

于 2011-08-02T07:13:08.263 回答
1

这个问题可能也可以通过这个解决(有同样的问题,但是从 64 位客户端访问 32 位服务器的另一种方式,那么您可以使用 CLSCTX_ACTIVATE_32_BIT_SERVER 代替):

HRESULT hr = CoCreateInstance(CLSID_ZZZ, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_ACTIVATE_64_BIT_SERVER, IID_IZZZ, (void ** )&l_IZZZ);
于 2013-09-11T12:42:55.390 回答
0

根据您对 IXXCryptService 接口的需要,尝试添加 [ClassInterface(ClassInterfaceType.AutoDispatch)] 或 [ClassInterface(ClassInterfaceType.AutoDual)]

[ClassInterface(ClassInterfaceType.AutoDual)] 
[Guid(XXCryptService.InterfaceId), ComVisible(true)/*, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)*/]
public interface IXXCryptService
于 2011-08-01T13:26:52.523 回答
0

我认为在所有情况下,通过 COM 混合 32 位和 64 位进程都会失败。

为了能够从 32 位 Delphi 进程访问,DotNet 程序集必须编译为 x86(即 32 位模式),而不是 x64。

AFAIK COM 不会跨越 32/64 位边界。

为了在 64 位和 32 位之间进行通信,您需要另一个技巧,例如在Is it possible to access a 64-bit dll from a 32-bit application?

于 2011-08-01T13:26:55.440 回答