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

     

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

  • 相关阅读:
    Mybatis学习记录
    北京信息科技大学第十一届程序设计竞赛E-- kotori和素因子(深搜)
    eclipse快捷键记录
    牛客小白月赛15A 斑羚飞渡
    台州学院第十二届校赛记录(B,C,E,H,I,J,L)
    3.13 模拟赛
    bzoj 4827 礼物
    bzoj 3252 攻略
    bzoj 5457 城市
    bzoj 3681 Arietta
  • 原文地址:https://www.cnblogs.com/killmyday/p/1395096.html
Copyright © 2020-2023  润新知