如何在.NET C#WinService中使用COM服务器(ATL,DLL代理)?

时间:2013-04-05 17:16:06

标签: c# delphi com interop com-interop

我有一个DLL Com Server,实际上只使用一个旧的Delphi exe-application。

COM Server是多年前(不是我)在C ++ ATL中编写的。它实现了回调(事件 - 是否相同?) - 使用传出接口IConnectionPointImpl。类工厂是单件(标有DECLARE_CLASSFACTORY_SINGLETON

现在需要在多个客户端之间共享此COM服务器:Delphi和C#(.NET 2.0,VS2008)。我把它作为DllSurrogate,现在我可以从多个Delphi客户端使用它,使用继承自TOleServer的类,重写GetServer方法始终使用CoCreateInstance(因为{{1}通常都会失败)而且它正在发挥作用。

现在我需要从C#WinService中使用它,我不知道从哪里开始。 我写了一个使用WinApi GetActiveObjectCoCreateInstance的小C#Hello-world - 我能够从COM Server连接到现有实例,但无法订阅事件。

这是VS导入的DLL META-DATA: enter image description here

我不知道这是否正确。 这是近似代码:

DllImport("ole32.dll")

欢迎任何建议,链接和诀窍。

2 个答案:

答案 0 :(得分:1)

似乎我成功连接到并处理DLL(进程内)Com Server的事件。

  1. 我将COM Server作为DLL代理(HowTo here)。
  2. Delphi客户端 - 构成我的COM Wrapper类,继承自TOleServer Class I重写GetServer方法:
  3. function TSWMgr.GetServer: IUnknown; begin OleCheck(CoCreateInstance(ServerData^.ClassId, nil, CLSCTX_LOCAL_SERVER, IUnknown, Result)); end;

    1. C#(Hello-World Client)方面(在咨询了this之类的HowTos之后):
    2. //使用SWLMLib; //使用System.Runtime.InteropServices; //使用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;
              }
          }
      }
      

      也许这不是最好的方式(比如TblImp.exe和/或COM Wrappers),但这种原始方式有效。

答案 1 :(得分:0)

我想你会想要编写如下代码:

Type type = Type.GetTypeFromCLSID(coClassGuid, true);
object instance = Activator.CreateInstance(type);

您可以将此视为与CoCreateInstance类似。

执行QueryInterface的C#COM Interop方式是进行转换或使用as关键字:

IComInterface comInterface = instance as IComInterface;

这将避免您必须直接使用COM API。

我绝不是COM Interop专家,我只需要做一些工作,所以我希望这可以帮助你。