用C#编写一个进程外的COM组件

简介:

我在以前的一篇文章《COM互操作 - VB 脚本里面使用.NET类型》里面写过如何在COM客户端程序里面使用.NET组件,但是这些.NET组件都属于进程内的组件,即COM客户端需要将CLR.NET组件都加载进自身进程的内存空间里面才能使用。上一次在MSDN中文论坛上看到有网友问如何使用C#编写一个进程外的COM组件,由于在使用regasm.exe注册.NET组件的时候,regasm.exe.NET组件里面发布的COM可见的类型对应CLSID的键值里加上了InprocServer32项,并且设置值为mscoree.dll。这也就是说,.NET的默认实现强制了我们只能在COM里面激活进程内的.NET组件,但是如何用.NET实现进程外的组件呢?难道真的要我们写一个新的COM程序来Host CLR?

答案是否定的,这里是另外一个替代方案,你需要完成下面这些步骤:

1.       C#代码里面自己实现一个ClassFactory,用来激活我们的Com可见的(Com Visible)类型。

2.       调用COM API CoRegisterClassObject将我们自己的ClassFactory注册在COM库里面,以便监听COM的激活申请。

3.       COM端使用完毕以后,可以通过调用CoRevokeClassObject撤销我们ClassFactoryCOM库里面的注册。

4.       如果我们的COM客户端是C++编写的话,并且采用前绑定接口的方式使用我们的Com可见(Com Visible)类型的话,为了能够将接口指针跨越进程边界传输,你还需要将.NET Assembly生成的Tlb文件注册,向COM库注册列集(Marshaling)接口的方法。

NET 代码

TestComVisibleClass.cs

1. using System;

2. using System.Runtime.InteropServices;

3. using System.Windows.Forms;

4.

5. namespace TestComServer

6. {

7.     internal static class ComHelperClass

8.     {

9.         public const string s_IID_ITestComVisible = "C66C0654-49AE-4f2e-8EDA-BD01C8259C20";

10.         public const string s_CLSID_TestComVisibleClass = "12D783BB-33BF-4973-B38B-2A8F0BA926E4";

11.         public static readonly Guid IID_ITestComVisible = new Guid(s_IID_ITestComVisible);

12.         public static readonly Guid CLSID_TestComVisibleClass = newGuid(s_CLSID_TestComVisibleClass);

13.

14.         public const string s_IID_IClassFactory = "00000001-0000-0000-C000-000000000046";

15.         public static readonly Guid IID_IClassFactory = new Guid("00000001-0000-0000-C000-000000000046");

16.         public static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");

17.

18.         [DllImport("ole32.dll")]

19.         public static extern int CoRegisterClassObject(

20.             [MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,

21.             [MarshalAs(UnmanagedType.IUnknown)] object pUnk,

22.             uint dwClsContext,

23.             uint flags,

24.             out uint lpdwRegister);

25.

26.         [DllImport("ole32.dll")]

27.         public static extern int CoRevokeClassObject(uint dwRegister);

28.

29.         [DllImport("ole32.dll")]

30.         public static extern int CoInitializeSecurity(

31.          IntPtr securityDescriptor,

32.          Int32 cAuth,

33.          IntPtr asAuthSvc,

34.          IntPtr reserved,

35.          UInt32 AuthLevel,

36.          UInt32 ImpLevel,

37.          IntPtr pAuthList,

38.          UInt32 Capabilities,

39.          IntPtr reserved3);

40.

41.         public const int RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6; // Encrypted DCOM communication

42.         public const int RPC_C_IMP_LEVEL_IDENTIFY = 2; // No impersonation really required

43.         public const int CLSCTX_LOCAL_SERVER = 4;

44.         public const int REGCLS_MULTIPLEUSE = 1;

45.         public const int EOAC_DISABLE_AAA = 0x1000; // Disable Activate-as-activator

46.         public const int EOAC_NO_CUSTOM_MARSHAL = 0x2000; // Disable custom marshalling

47.         public const int EOAC_SECURE_REFS = 0x2;   // Enable secure DCOM references

48.         public const int CLASS_E_NOAGGREGATION = unchecked((int)0x80040110);

49.         public const int E_NOINTERFACE = unchecked((int)0x80004002);

50.

51.     }

52.

53.     [ComVisible(true)]

54.     [Guid(ComHelperClass.s_IID_ITestComVisible)]

55.     public interface ITestComVisible

56.     {

57.         [DispId(1)]

58.         string TestProperty { getset; }

59.

60.         [DispId(2)]

61.         void TestMethod();

62.     }

63.

64.     [ComVisible(true)]

65.     [Guid(ComHelperClass.s_CLSID_TestComVisibleClass)]

66.     public class TestComVisibleClass : ITestComVisible

67.     {

68.         public string TestProperty { getset; }

69.

70.         public void TestMethod()

71.         {

72.             MessageBox.Show("Test Method");

73.         }

74.     }

75.

76.     // 类厂

77.     [

78.      ComImport,

79.      InterfaceType(ComInterfaceType.InterfaceIsIUnknown),

80.      Guid(ComHelperClass.s_IID_IClassFactory)

81.     ]

82.     internal interface IClassFactory

83.     {

84.         [PreserveSig]

85.         int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);

86.

87.         [PreserveSig]

88.         int LockServer(bool fLock);

89.     }

90.

91.     internal class ComClassFactory : IClassFactory

92.     {

93.         #region IClassFactory Members

94.

95.         public int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject)

96.         {

97.             ppvObject = IntPtr.Zero;

98.

99.             if (pUnkOuter != IntPtr.Zero)

100.                 Marshal.ThrowExceptionForHR(ComHelperClass.CLASS_E_NOAGGREGATION);

101.

102.             if (riid == ComHelperClass.IID_ITestComVisible ||

103.                  riid == ComHelperClass.IID_IUnknown)

104.             {

105.                 ppvObject = Marshal.GetComInterfaceForObject(

106.                     new TestComVisibleClass(), typeof(ITestComVisible));

107.             }

108.             else

109.                 Marshal.ThrowExceptionForHR(ComHelperClass.E_NOINTERFACE);

110.

111.             return 0; // S_OK

112.         }

113.

114.         public int LockServer(bool fLock)

115.         {

116.             return 0; // S_OK

117.         }

118.

119.         #endregion

120.     }

121. }

 

Program.cs

1. using System;

2. using System.Windows.Forms;

3. using System.Runtime.InteropServices;

4.

5. namespace TestComServer

6. {

7.     static class Program

8.     {

9.         private static uint m_ComCookie = 0;

10.

11.         /// <summary>

12.         /// The main entry point for the application.

13.         /// </summary>

14.         [STAThread]

15.         static void Main()

16.         {

17.             Application.EnableVisualStyles();

18.             Application.SetCompatibleTextRenderingDefault(false);

19.

20.             RegisterDcomServer();

21.

22.             Application.ApplicationExit += new EventHandler(Application_ApplicationExit);

23.             Application.Run(new Form1());

24.         }

25.

26.         static void Application_ApplicationExit(object sender, EventArgs e)

27.         {

28.             RevokeDcomServer();

29.         }

30.

31.         private static void RegisterDcomServer()

32.         {

33.             // 做一些安全检查,确保只有一些有权限的人才能调用你的C# Dcom组件

34.             // 如果你对安全性不关心的话,可以删除下面的语句

35.             int hr = ComHelperClass.CoInitializeSecurity(

36.                 IntPtr.Zero, // 这里要输入你的安全描述符

37.                 -1,

38.                 IntPtr.Zero,

39.                 IntPtr.Zero,

40.                 ComHelperClass.RPC_C_AUTHN_LEVEL_PKT_PRIVACY,

41.                 ComHelperClass.RPC_C_IMP_LEVEL_IDENTIFY,

42.                 IntPtr.Zero,

43.                 ComHelperClass.EOAC_DISABLE_AAA | ComHelperClass.EOAC_SECURE_REFS | ComHelperClass.EOAC_NO_CUSTOM_MARSHAL,

44.                 IntPtr.Zero);

45.             if (hr != 0)

46.                 Marshal.ThrowExceptionForHR(hr);

47.

48.             hr = ComHelperClass.CoRegisterClassObject(

49.                 ComHelperClass.CLSID_TestComVisibleClass,

50.                 new ComClassFactory(),

51.                 ComHelperClass.CLSCTX_LOCAL_SERVER,

52.                 ComHelperClass.REGCLS_MULTIPLEUSE,

53.                 out m_ComCookie);

54.             if (hr != 0)

55.                 Marshal.ThrowExceptionForHR(hr);

56.         }

57.

58.         private static void RevokeDcomServer()

59.         {

60.             if (m_ComCookie != 0)

61.                 ComHelperClass.CoRevokeClassObject(m_ComCookie);

62.         }

63.     }

64. }

注册表代码

1. Windows Registry Editor Version 5.00

2.

3. [HKEY_CLASSES_ROOT"TypeLib"{9903F14C-12CE-4c99-9986-2EE3D7D588A8}]

4.

5. [HKEY_CLASSES_ROOT"TypeLib"{9903F14C-12CE-4c99-9986-2EE3D7D588A8}"1.0]

6. @="TestComServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

7.

8. [HKEY_CLASSES_ROOT"TypeLib"{9903F14C-12CE-4c99-9986-2EE3D7D588A8}"1.0"0]

9.

10. [HKEY_CLASSES_ROOT"TypeLib"{9903F14C-12CE-4c99-9986-2EE3D7D588A8}"1.0"0"win32]

11. @="D:""Workspace""Forum""Test""TestComServer""bin""Debug""TestComServer.tlb"

12.

13. [HKEY_CLASSES_ROOT"TypeLib"{9903F14C-12CE-4c99-9986-2EE3D7D588A8}"1.0"FLAGS]

14. @="0"

15.

16. [HKEY_CLASSES_ROOT"TypeLib"{9903F14C-12CE-4c99-9986-2EE3D7D588A8}"1.0"HELPDIR]

17.

18. [HKEY_CLASSES_ROOT"Interface"{C66C0654-49AE-4f2e-8EDA-BD01C8259C20}]

19.

20. [HKEY_CLASSES_ROOT"Interface"{C66C0654-49AE-4f2e-8EDA-BD01C8259C20}"ProxyStubClsid]

21. @="{00020424-0000-0000-C000-000000000046}"

22.

23. [HKEY_CLASSES_ROOT"Interface"{C66C0654-49AE-4f2e-8EDA-BD01C8259C20}"ProxyStubClsid32]

24. @="{00020424-0000-0000-C000-000000000046}"

25.

26. [HKEY_CLASSES_ROOT"Interface"{C66C0654-49AE-4f2e-8EDA-BD01C8259C20}"TypeLib]

27. "Version"="1.0"

28. @="{9903F14C-12CE-4c99-9986-2EE3D7D588A8}"

29.

30. [HKEY_CLASSES_ROOT"CLSID"{12D783BB-33BF-4973-B38B-2A8F0BA926E4}]

31. @="TestComServer.TestComVisibleClass"

32.

33. [HKEY_CLASSES_ROOT"CLSID"{12D783BB-33BF-4973-B38B-2A8F0BA926E4}"Implemented Categories]

34.

35. [HKEY_CLASSES_ROOT"CLSID"{12D783BB-33BF-4973-B38B-2A8F0BA926E4}"Implemented Categories"{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]

36.

37. [HKEY_CLASSES_ROOT"CLSID"{12D783BB-33BF-4973-B38B-2A8F0BA926E4}"LocalServer32]

38. @="D:""Workspace""Forum""Test""TestComServer""bin""Debug""TestComServer.exe"

39.

40. [HKEY_CLASSES_ROOT"CLSID"{12D783BB-33BF-4973-B38B-2A8F0BA926E4}"ProgId]

41. @="TestComServer.TestComVisibleClass"

 

客户端C++代码

1. // TestComClient.cpp : Defines the entry point for the console application.

2. //

3.

4. #include "stdafx.h"

5. #include <windows.h>

6. #import "D:"Workspace"Forum"Test"TestComServer"bin"Debug"TestComServer.tlb" no_namespace, named_guids

7.

8. int _tmain(int argc, _TCHAR* argv[])

9. {

10.    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

11.    assert(SUCCEEDED(hr));

12.

13.    CLSID clsid;

14.    hr = ::CLSIDFromProgIDEx(TEXT("TestComServer.TestComVisibleClass"), &clsid);

15.    assert(SUCCEEDED(hr));

16.   

17.     MULTI_QI mq;

18.    mq.pIID = &IID_ITestComVisible;

19.    mq.pItf = NULL;

20.    mq.hr = S_OK;

21.    hr = ::CoCreateInstanceEx(clsid, NULL, CLSCTX_LOCAL_SERVER, NULL, 1, &mq);

22.    assert(SUCCEEDED(hr));

23.

24.    ITestComVisible *pIt = (ITestComVisible *)mq.pItf;

25.    pIt->TestMethod();

26.    pIt->Release();

27.

28.     CoUninitialize();

29.    return 0;

30. }

 

另外一种客户端,使用VB Script代码

set obj = CreateObject("TestComServer.TestComVisibleClass")  

obj.TestMethod() 

 

今天急着回家,下一篇文章再解释代码里面的意思。

本文转自 donjuan 博客园博客,原文链接: http://www.cnblogs.com/killmyday/archive/2009/02/20/1395096.html  ,如需转载请自行联系原作者


相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
9月前
Echarts实战案例代码(19):利用visualMap视觉映射组件制作五色玫瑰工作进程图
Echarts实战案例代码(19):利用visualMap视觉映射组件制作五色玫瑰工作进程图
150 0
|
8月前
|
C# 开发者
C# 开发者技术:进程间数据共享之管道(Pipes)-异步通信版
主要类 1.NamedPipeClientStream 2.NamedPipeServerStream 解释:命名管道是一种进程间通信的方式,它允许不同进程之间在同一台机器上进行通信
447 2
C# 开发者技术:进程间数据共享之管道(Pipes)-异步通信版
C#开发:执行进程等待
C#开发:执行进程等待
163 0
|
弹性计算 监控 大数据
集群状态显示,进程存在,但是组件状态显示未知
集群状态显示,进程存在,但是组件状态显示未知
|
编解码 Linux C#
C#进程调用FFmpeg操作音视频
C#进程调用FFmpeg操作音视频
200 0
|
调度 C#
C#编程:进程与线程
C#编程:进程与线程
|
API C# Windows
C#多线程(4):进程同步Mutex类
C#多线程(4):进程同步Mutex类
215 0
C#多线程(4):进程同步Mutex类
|
Android开发
【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )(三)
【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )(三)
287 0
【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )(三)