COM Interop, клиент не может найти интерфейс во внешнем COM - PullRequest
4 голосов
/ 01 августа 2011

Я использую 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";
    }

}

Программа запускается, но при подключении клиента происходит сбой на клиенте после того, как сервер вызвал CreateInstance для ObjectClassFactory и возвратил объект в ppvObject с помощью Marshal.GetComInterfaceForObject (new XXCryptService (), typeof (IXXCryptService) и вернул его 0.

Запуск клиента в .NET вызывает «Невозможно привести COM-объект типа« COMTest.XXCryptService »к типу интерфейса« COMTest.IXXCryptService ». Эта операция завершилась неудачно, потому что QueryInterface вызывает компонент COM для интерфейса с IID» {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

Эта операция завершилась неудачно, так как произошел сбой вызова QueryInterface COM-компонента для интерфейса с IID '{6990FF5F-22E2-4032-8B98-36115DBCEFFF}' из-за следующей ошибки: Ошибка загрузки библиотеки типов / DLL. (Исключение из HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY))

Ответы [ 4 ]

1 голос
/ 11 сентября 2013

Эта проблема, вероятно, и с этим решена (возникла та же проблема, но с другой стороны, при доступе к 32-битному серверу из 64-битного клиента, вместо этого вы можете использовать CLSCTX_ACTIVATE_32_BIT_SERVER):

HRESULT hr = CoCreateInstance(CLSID_ZZZ, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_ACTIVATE_64_BIT_SERVER, IID_IZZZ, (void ** )&l_IZZZ);
1 голос
/ 02 августа 2011

Это была проблема с регистрацией и тем, что regasm выполняется, только если сборка имеет ту же цель, что и regasm.Для regasm должен быть параметр «/ com_oop или что-то», чтобы заставить его зарегистрировать LocalServer32 вместо InprocServer32 и зарегистрировать его как для 32, так и для 64-разрядных в 64-разрядных системах.

Чтобы обойти это, мне пришлось временно скомпилировать исполняемый файл(с тем же путем) к 32-битному, запустите 32-битный регазм (с / tlb: ..), затем скомпилируйте обратно в 64-битный, запустите 64-битный регазм (с / tlb: .. снова), и теперь он работает как для 32, так и для64-битные по сравнению с 64-битными исполняемыми файлами.

CSExeComServer имеет метод регистрации и отмены регистрации, при котором он вручную удаляет ключ InprocServer32 и добавляет LocalServer32.Чтобы убедиться, что это работает должным образом, я собираюсь изменить это, определить, регистрируется ли оно в 64-битной системе, и затем сделать так, чтобы оно правильно регистрировало его там.Когда я закончу, я опубликую изменения, которые я внес в метод регистрации.

0 голосов
/ 01 августа 2011

Я думаю, что смешивание 32- и 64-битного процесса через COM не удастся, во всех случаях.

Чтобы быть доступным из 32-битного процесса Delphi, сборка DotNet должна быть скомпилирована как x86 (то есть в 32-битном режиме), а не как x64.

AFAIK COM не будет пересекать 32/64-битную границу.

Для связи между 64 и 32 битами вам понадобится другой трюк, такой как опубликованный в Возможно ли получить доступ к 64-битной dll из 32-битного приложения?

0 голосов
/ 01 августа 2011

Попробуйте добавить [ClassInterface (ClassInterfaceType.AutoDispatch)] или [ClassInterface (ClassInterfaceType.AutoDual)] в зависимости от ваших потребностей для интерфейса IXXCryptService

[ClassInterface(ClassInterfaceType.AutoDual)] 
[Guid(XXCryptService.InterfaceId), ComVisible(true)/*, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)*/]
public interface IXXCryptService
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...