COM Interop, client not finding interface in Out-of-process COM

4

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))

c#
.net
delphi
com-interop
out-of-process
asked on Stack Overflow Aug 1, 2011 by Atle S • edited Aug 1, 2011 by Atle S

4 Answers

1

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.

answered on Stack Overflow Aug 2, 2011 by Atle S
1

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):

HRESULT hr = CoCreateInstance(CLSID_ZZZ, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_ACTIVATE_64_BIT_SERVER, IID_IZZZ, (void ** )&l_IZZZ);
0

Try adding [ClassInterface(ClassInterfaceType.AutoDispatch)] or [ClassInterface(ClassInterfaceType.AutoDual)] depending on your needs to interface IXXCryptService

[ClassInterface(ClassInterfaceType.AutoDual)] 
[Guid(XXCryptService.InterfaceId), ComVisible(true)/*, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)*/]
public interface IXXCryptService
answered on Stack Overflow Aug 1, 2011 by elevener
0

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?

answered on Stack Overflow Aug 1, 2011 by Arnaud Bouchez • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0