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

Atle S
  • 199
  • 4
  • 12
  • 1) Why did you comment `[InterfaceType(ComInterfaceType.InterfaceIsDual)]`? We usually define dual interfaces for accessing COM from Delphi. 2) Are your running on a 64 bit Windows? In this case, you'll have to compile and register all COM objects as x86, and run regasm from the 32 bit windows virtual folder. – Arnaud Bouchez Aug 01 '11 at 12:46
  • 1
    Adding InterfaceIsDual does not help. The server is x64 and the client is x86. We need this COM server to be the link between a 32bit system and some 64bit assemblies. I ran regasm for both 32 and 64 bit, and the compile is defined to be x64. Without this setting, it crashes earlier in the process. – Atle S Aug 01 '11 at 13:17

4 Answers4

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);
  • Hi Dario, thanks for your help. This solved my problem. My COM object is written in VC++ only, and I couldn't work out how to launch a 64 bit Out of Process Server from a 32 bit executable. Your answer solved the problem. – Peter Nimmo Jun 26 '14 at 11:23
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.

Atle S
  • 199
  • 4
  • 12
  • Atle, have you read my last comments? CSExeCOMServer uses wrong version of RegAsm – elevener Aug 02 '11 at 07:39
  • Sure, but I registered with regasm manually. Also it needs to register properly for both. It should be available for both 64 and 32bit, and the 32bit regasm does not work when the .exe is compiled as x64. So I have to set the registry values manually. – Atle S Aug 02 '11 at 12:49
  • If you use 64 RegAsm for x64 assembly it's registers for both 32bit and 64bit, you don't have to do anything manually. – elevener Aug 02 '11 at 13:17
  • No, it will not register under the Wow6432Node and no typelib win64 key. This has to be added manually unless you are able to run both regasm. – Atle S Aug 03 '11 at 12:47
  • I removed the wow6432Node parts in registry where I found a reference to the COM server, and it still seems to run on 32bit. This did not work before, so there must be some registry settings I missed. – Atle S Aug 03 '11 at 12:54
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
elevener
  • 1,071
  • 7
  • 20
  • Isn't ClassInterface for classes only? That would only affect any automatic interface generation. This interface is created manually, so the class have ClassInterfaceType.None to stop any auto create. – Atle S Aug 01 '11 at 13:40
  • Sorry, was wrong about ClassInterface. For me it looks like the problem is due to fact that XXCryptService because of [ClassInterface(ClassInterfaceType.None)] exposes only IDispatch, and your client tries to instantiate IUnknown. Don't have time to check right now, I'll try to reproduce problem later if you wouldn't find decision by that moment. – elevener Aug 01 '11 at 14:16
  • Managed to reproduce. Problem is not with the code but with the fact that in CSExeCOMServer.csproj is hardcoded in postbuild wrong RegAsm version. – elevener Aug 02 '11 at 06:08
  • C:\Windows\Microsoft.NET\Framework\v2.0.50727\regasm.exe and so on... Replace both occurences with C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe (only be sure to run it with admin rights and everything works ok). – elevener Aug 02 '11 at 06:16
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?

Community
  • 1
  • 1
Arnaud Bouchez
  • 40,947
  • 3
  • 66
  • 152
  • 1
    As long as the COM server is out of process it should work. We have another project where a 64bit .NET application communicates with a 32bit Delphi COM Server (Out-of-process), and that works without any problems. – Atle S Aug 01 '11 at 13:31
  • There seems to be a 64/32bit issue with this one. But since out-of-process split's it into separate memory, this should work. But what registry keys could be wrong? When I registered it for 32 bit, it would only register when it was 32 bit, so I recompiled it in x86, registered it and compiled it back to 64bit. When I'm connecting the real 64bit assembly to the test project, it won't compile in 32bit anymore, but I only need the registry values. – Atle S Aug 01 '11 at 14:04