于是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撤销我们ClassFactory在COM库里面的挂号。

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 = new Guid(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 { get; set; }

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 { get; set; }

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

 

今日着急着回家,下一样首文章更解释代码里面的意。