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

 

今天急着回家,下一篇文章再解釋代碼裏面的意思。

發佈了73 篇原創文章 · 獲贊 2 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章