Reputation: 1998
I have a DLL Com Server, actually used only by one old Delphi exe-application.
COM Server is written many years ago (not by me) in C++ ATL. It implements callbacks (event - is it the same?) - using outgoing interface IConnectionPointImpl
. Class Factory is singleton (marked with DECLARE_CLASSFACTORY_SINGLETON
)
Now it is required that this COM Server have to be shared between more than one clients: both Delphi and C# (.NET 2.0, VS2008). I put it as DllSurrogate
and now I can use it from multiple Delphi clients, using class inherited from TOleServer
, overriding GetServer
method to use always CoCreateInstance
(because GetActiveObject
usually fails) and it's working.
Now I need to consume it from C# WinService and I don't know from where to start.
I wrote a little C# Hello-world which use WinApi CoCreateInstance
and DllImport("ole32.dll")
- I am able to connect to existing instance from COM Server but cannot subscribe to events.
Here is the DLL META-DATA imported by VS:
I don't know if this is correct way. Here is aproximative code:
using System;
using System.Collections.Generic;
using System.Text;
using SWLMLib;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace TestSWLM
{
[Flags]
enum CLSCTX : uint
{
//... defines here CLSCTX
}
class Program
{
[DllImport("ole32.dll", EntryPoint = "CoCreateInstance", CallingConvention = CallingConvention.StdCall)]
static extern UInt32 CoCreateInstance([In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
IntPtr pUnkOuter, UInt32 dwClsContext, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object rReturnedComObject);
public static void AboutExpireHandler(IFeature pFeature, int HoursRemained)
{
Console.WriteLine("AboutExpireHandler, pFeature = {0}", pFeature.Code);
}
static void Main(string[] args)
{
try
{
SWLMLib.ISWMgr lintfSWLMgr = null;
object instance = null;
UInt32 dwRes = CoCreateInstance(new Guid("8EAAFAD7-73F8-403B-A53B-4400E16D8EDF"), IntPtr.Zero, (uint)CLSCTX.CLSCTX_LOCAL_SERVER,
new Guid("00000000-0000-0000-C000-000000000046"), out instance);
SWLMLib.SWMgrClass lSWLMgr = null;
unsafe
{
lintfSWLMgr = (instance as SWLMLib.ISWMgr);
Type liType = instance.GetType();
}
if (lintfSWLMgr != null)
{
IntPtr iuknw = Marshal.GetIUnknownForObject(lintfSWLMgr);
IntPtr ipointer = IntPtr.Zero;
Guid lICPCGuid = typeof(IConnectionPointContainer).GUID;
Guid lICPGuid = typeof(IConnectionPoint).GUID;
Guid lIEv = new Guid("{C13A9D38-4BB0-465B-BF4A-487F371A5538}");
IConnectionPoint lCP = null;
IConnectionPointContainer lCPC = null;
Int32 r = Marshal.QueryInterface(iuknw, ref lICPCGuid, out ipointer);
lCPC = (IConnectionPointContainer)Marshal.GetObjectForIUnknown(ipointer);
lCPC.FindConnectionPoint(ref lIEv, out lCP);
Int32 outID;
lCP.Advise(???, out outID); // HERE I don't know what to do further
lIEvEv.FeatureAboutToExpire += AboutExpireHandler;
}
}
catch (Exception E)
{
Console.WriteLine(E.Message);
throw;
}
Console.ReadLine();
}
}
}
Any Advices, links and know-hows are welcome.
Upvotes: 2
Views: 2985
Reputation: 1998
It seems that I succeeded to connect to- and handle Events of DLL (in-proc) Com Server.
function TSWMgr.GetServer: IUnknown;
begin
OleCheck(CoCreateInstance(ServerData^.ClassId, nil, CLSCTX_LOCAL_SERVER, IUnknown, Result));
end;
//using SWLMLib; //using System.Runtime.InteropServices; //using System.Runtime.InteropServices.ComTypes;
[Flags]
enum ReturnCode : uint
{
S_OK = 0, S_FALSE = 1, REGDB_E_CLASSNOTREG = 0x80040154, CLASS_E_NOAGGREGATION = 0x80040110, E_NOINTERFACE = 0x80004002, E_POINTER = 0x80004003
}
[Flags]
enum CLSCTX : uint
{
CLSCTX_INPROC_SERVER = 0x1, CLSCTX_INPROC_HANDLER = 0x2, CLSCTX_LOCAL_SERVER = 0x4,
//... //others
CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER
}
/// <summary>
/// Sink Class implementig COM Server outgoing interface SWLMLib.ISWMgrEvents
/// </summary>
[ClassInterface(ClassInterfaceType.None)]
class MySink : SWLMLib.ISWMgrEvents
{
public void FeatureAboutToExpire(SWLMLib.IFeature pFeature, int HoursRemained)
{
Console.WriteLine("{0} FeatureAboutToExpire: Feature {1} Hours={2}", DateTime.Now, pFeature.Code, HoursRemained);
Marshal.ReleaseComObject(pFeature); //WTF??? Without this line COM Server object is not released!
}
public void FeatureExpired(SWLMLib.IFeature pFeature)
{
Console.WriteLine("FeatureExpired: Feature {0}", pFeature.Code);
Marshal.ReleaseComObject(pFeature); //Without this line COM Server object is not released!
}
}
class Program
{
//Import "CoCreateInstance" to play with run context of created COM-object (3rd parameter)
[DllImport("ole32.dll", EntryPoint = "CoCreateInstance", CallingConvention = CallingConvention.StdCall)]
static extern UInt32 CoCreateInstance([In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
IntPtr pUnkOuter, UInt32 dwClsContext, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object rReturnedComObject);
static void Main(string[] args)
{
try
{
Guid SWMgrClassObjectGuid = typeof(SWLMLib.SWMgrClass).GUID; //{8EAAFAD7-73F8-403B-A53B-4400E16D8EDF}
Guid IUnknownGuid = new Guid("00000000-0000-0000-C000-000000000046"); //Can it be written in more pretty style?
SWLMLib.ISWMgr lintfSWLMgr = null;
/* This will create IN-PROC Server because, it seems CoCreateInstance will be invoked with CLSCTX_INPROC_SERVER flag settled
Type type = Type.GetTypeFromCLSID(SWMgrClassObjectGuid), true);
object instance0 = Activator.CreateInstance(type);
lintfSWLMgr = (instance0 as SWLMLib.ISWMgr); */
Guid Ev1 = typeof(ISWMgrEvents).GUID;
object instance = null;
unsafe
{
UInt32 dwRes = CoCreateInstance(SWMgrClassObjectGuid,
IntPtr.Zero,
(uint)(CLSCTX.CLSCTX_LOCAL_SERVER), //if OR with CLSCTX_INPROC_SERVER then INPROC Server will be created, because of DLL COM Server
IUnknownGuid,
out instance);
if (dwRes != 0)
{
int iError = Marshal.GetLastWin32Error();
Console.WriteLine("CoCreateInstance Error = {0}, LastWin32Error = {1}", dwRes, iError);
return;
}
lintfSWLMgr = (instance as SWLMLib.ISWMgr);
}
if (lintfSWLMgr != null)
{
//lintfSWLMgr.InitializeMethod(...); //Initialize object
//Find Connection Point for Events
Guid ISWMgrEventsGuid = typeof(SWLMLib.ISWMgrEvents).GUID; //{C13A9D38-4BB0-465B-BF4A-487F371A5538} Interface for Evenets Handling
IConnectionPoint lCP = null;
IConnectionPointContainer lCPC = (instance as IConnectionPointContainer);
lCPC.FindConnectionPoint(ref ISWMgrEventsGuid, out lCP);
MySink lSink = new MySink();
Int32 dwEventsCookie;
lCP.Advise(lSink, out dwEventsCookie);
Console.WriteLine("Waiting for Events Handling...");
Console.WriteLine("Press ENTER to Exit...");
Console.ReadLine(); // Until Eneter is not hit, the events arrive properly
//Here starting to Unsubscribe for Events and Com Objects CleanUP
lCP.Unadvise(dwEventsCookie);
Marshal.ReleaseComObject(lCP);
Marshal.ReleaseComObject(lintfSWLMgr);
}
}
catch (Exception E)
{
Console.WriteLine(E.Message);
throw;
}
}
}
Maybe this is not the best way (like TblImp.exe and or COM Wrappers) but this raw way works.
Upvotes: 1
Reputation: 17454
I think you'll want to write code like the following:
Type type = Type.GetTypeFromCLSID(coClassGuid, true);
object instance = Activator.CreateInstance(type);
You can think of this as analogous to CoCreateInstance
.
The C# COM Interop way of doing QueryInterface
is to do casts or use the as
keyword:
IComInterface comInterface = instance as IComInterface;
This will avoid you having to use the COM APIs directly.
I'm by no means a COM Interop expert, I've just had to do a little work with it, so I'm hoping this helps you out.
Upvotes: 0