I need to create a C# COM server, and a C# COM Client, and communicate between the two using CoCreateInstance.
This is so that the client can be 64 bit, and the server 32 bit, using the 'DllSurrogate' method described in Hosting a .NET DLL as an Out-Of-Process COM Server (EXE).
However, at the moment I have both in 32 bit, but am struggling to get it working.
There are online samples to create a C# COM server: COM Interop Part 2: C# Server Tutorial
And to create a C# COM client: COM Interop Part 1: C# Client Tutorial
But I haven't found an example that does both. My attempts so far have failed, and I suspect that the Microsoft COM server used for the C# COM client example includes extra specification not in the C# COM server sample.
My attempts so far are below.
The C# client call to CoCreateInstance is apparently successful, but the object returned seems empty (not null), and the attempt to cast it to the interface fails.
Can anyone spot the problem, or provide a fully joined up example?
This is my server code:
// AntCsServer.cs
// Project settings:
// compile as a library
// Select 'Make assembly COM Visible'
// Select 'Register for COM Interop'
using System;
using System.Runtime.InteropServices;
namespace AntCsServer
{
// Since the .NET Framework interface and coclass have to behave as
// COM objects, we have to give them guids.
[Guid("555E2D2B-EE00-47AA-AB2B-39F953F6B339")]
public interface IManagedInterface
{
int PrintHi(string name);
}
[Guid("0190D7A6-8D8D-4031-810A-627BA3EE68A6")]
public class InterfaceImplementation : IManagedInterface
{
public int PrintHi(string name)
{
Console.WriteLine("Hello, {0}!", name);
return 33;
}
}
}
And here is my client code:
using System;
using System.Runtime.InteropServices;
namespace ComClient
{
class Program
{
public const string ComSvrInterface_GUID = "555E2D2B-EE00-47AA-AB2B-39F953F6B339";
public const string ComSvrClass_GUID = "0190D7A6-8D8D-4031-810A-627BA3EE68A6";
[STAThread]
static void Main(string[] args)
{
Ole32Methods.CoInitialize((IntPtr)0);
object instance1 = null;
string sErr1;
bool b1;
IManagedInterface cCom = null;
b1 = Ole32Methods.CreateComObject(ComSvrClass_GUID, ComSvrInterface_GUID, out instance1, out sErr1);
if (b1)
{
cCom = instance1 as IManagedInterface;
}
if (cCom != null)
{
cCom.PrintHi("Santa Claus");
Console.WriteLine("Should have just printed Santa Claus");
}
}
}
// -------------------------------------------
// Reproduce the interface here so we can cast to it
[Guid("555E2D2B-EE00-47AA-AB2B-39F953F6B339")]
public interface IManagedInterface
{
int PrintHi(string name);
}
// -------------------------------------------
public class Ole32Methods
{
[DllImport("ole32.Dll")]
static public extern uint CoCreateInstance(ref Guid clsid,
[MarshalAs(UnmanagedType.IUnknown)] object inner,
uint context,
ref Guid uuid,
[MarshalAs(UnmanagedType.IUnknown)] out object rReturnedComObject);
[DllImport("ole32.dll")]
public static extern int CoInitialize(IntPtr pvReserved);
// ------------------------
public static bool CreateComObject(string sClassGuid, string sInterfaceGuid, out object instance, out string sErr)
{
const uint CLSCTX_INPROC_SERVER = 1;
//const uint CLSCTX_LOCAL_SERVER = 4;
// CLSID of the COM object
Guid clsid = new Guid(sClassGuid);
// GUID of the required interface
//Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
Guid IID_Interface = new Guid(sInterfaceGuid);
instance = null;
uint hResult = Ole32Methods.CoCreateInstance(ref clsid, null,
CLSCTX_INPROC_SERVER, ref IID_Interface, out instance);
// Some error codes. See 'winerror.h for more, and use the following to convert the debug value to Hex: http://www.rapidtables.com/convert/number/decimal-to-hex.htm
const uint S_OK = 0x00000000; //Operation successful
const uint E_NOTIMPL = 0x80004001; //Not implemented
const uint E_NOINTERFACE = 0x80004002; //No such interface supported
const uint E_POINTER = 0x80004003; //Pointer that is not valid
const uint E_ABORT = 0x80004004; //Operation aborted
const uint E_FAIL = 0x80004005; //Unspecified failure
const uint E_UNEXPECTED = 0x8000FFFF; //Unexpected failure
const uint E_ACCESSDENIED = 0x80070005; //General access denied error
const uint E_HANDLE = 0x80070006; //Handle that is not valid
const uint E_OUTOFMEMORY = 0x8007000E; //Failed to allocate necessary memory
const uint E_INVALIDARG = 0x80070057; //One or more arguments are not valid
const uint E_CLASSNOTREG = 0x80040154; // Class not registered
sErr = "";
switch (hResult)
{
case S_OK:
sErr = "";
break;
case E_NOTIMPL:
sErr = "E_NOTIMPL: Not implemented";
break;
case E_NOINTERFACE:
sErr = "E_NOINTERFACE: No such interface supported";
break;
case E_POINTER:
sErr = "E_POINTER: Pointer that is not valid";
break;
case E_ABORT:
sErr = "E_ABORT: Operation aborted";
break;
case E_FAIL:
sErr = "E_FAIL: Unspecified failure";
break;
case E_UNEXPECTED:
sErr = "E_UNEXPECTED: Unexpected failure";
break;
case E_ACCESSDENIED:
sErr = "E_ACCESSDENIED: General access denied error";
break;
case E_HANDLE:
sErr = "E_HANDLE: Handle that is not valid";
break;
case E_OUTOFMEMORY:
sErr = "E_OUTOFMEMORY: Failed to allocate necessary memory";
break;
case E_INVALIDARG:
sErr = "E_INVALIDARG: One or more arguments are not valid";
break;
case E_CLASSNOTREG:
sErr = "E_CLASSNOTREG: Class not registered";
break;
}
return hResult == 0;
}
}
}
You are loading your COM server in-process. This causes the CLR to load your assembly in-proc, so it loads it using standard assembly loading. The resulting object you get from CoCreateInstance
is a literal AntCsServer.InterfaceImplementation
class, which implements the interface in the server DLL, not the interface in your client EXE.
This can be confirmed by checking the type of the object you get back from CoCreateInstance
. If it's a System.__ComObject
, you are getting a real COM object (a proxy object for the .NET object). If not, then the problem is as I described -- you are actually getting an instance of the assembly's type.
User contributions licensed under CC BY-SA 3.0