How to write a DCOM server in C# 如何用C#编写DCOM服务器

本文介绍了如何将.NET对象暴露为DCOM服务的过程,并详细解释了实现跨平台、跨语言远程调用的技术细节。

先讲讲.net Remoting与DCOM的区别:.net Remoting 是在DCOM等基础上发展起来的一种技术,它的主要目的是实现跨平台、跨语言、穿透企业防火墙。DCOM是通过TCP/IP通道安全的进程间通信,而.NET remoting 不是。任何进程包括windows服务都能寄宿在DCOM服务器中。

如果你已经十分清楚地知道了COM的话,可以参考下面的几点加深你对DCOM的了解。

1) Your server process will expose a COM class factory that would just create your .NET object.

    你的DCOM服务器进程应该暴露一个COM类工厂以便于创建.net对象

2) In COM you register the class factory using the standard CoRegisterClassObjects API 。

    在COM中尼应该使用标准的API(CoRegisterClassObjects函数)注册类工厂。

3) Make sure you call CoInitializeSecurity on your first process, for example to allow only Administrators to call in 。

   确认在第一个进程中调用CoInitializeSecurity函数,例如尽允许在具有管理员的主机上调用。

4) Register your .NET assemblies with REGASM.EXE. Make sure your .NET class is visible through COM so CCW can be created around it (more details in MSDN on COM Interop section).

    使用REGASM.EXE工具注册.net汇编程序,确认你的.netleu通过COM组件后可见,以便于可以创建CCW。

5) Remove the auto-generated InprocServer32 key after registration (REGASM puts it there but we are going out-of-proc)

  完成注册后移除自动产生的InprocServer32 key。

6) Add the standard LocalServer32 / AppID registry keys.

   添加标准的LocalServer32 / AppID的注册表键值。

[c-sharp]  view plain copy print ?
  1. using System;  
  2. using System.ComponentModel;  
  3. using System.Data;  
  4. using System.Diagnostics;  
  5. using System.ServiceProcess;  
  6. using System.Threading;  
  7. using System.Runtime.InteropServices;  
  8. namespace Test  
  9. {  
  10.  //   
  11.  // .NET class, interface exposed through DCOM  
  12.  //  
  13.  // exposed COM interface  
  14.  [GuidAttribute(MyService.guidIMyInterface), ComVisible(true)]  
  15.  public interface IMyInterface  
  16.  {  
  17.   string GetDateTime(string prefix);   
  18.  }  
  19.  // exposed COM class  
  20.  [GuidAttribute(MyService.guidMyClass), ComVisible(true)]  
  21.  public class CMyClass: IMyInterface  
  22.  {  
  23.   // Print date & time and the current EXE name  
  24.   public string GetDateTime(string prefix)   
  25.   {   
  26.    Process currentProcess = Process.GetCurrentProcess();  
  27.    return string.Format("{0}: {1} [server-side COM call executed on {2}]",   
  28.     prefix, DateTime.Now, currentProcess.MainModule.ModuleName);  
  29.   }   
  30.  }  
  31.  //  
  32.  // My hosting Windows service  
  33.  //  
  34.  internal class MyService :   
  35.   ServiceBase  
  36.  {  
  37.   public MyService()  
  38.   {  
  39.    // Initialize COM security  
  40.    Thread.CurrentThread.ApartmentState = ApartmentState.STA;  
  41.    UInt32 hResult = ComAPI.CoInitializeSecurity(  
  42.     IntPtr.Zero, // Add here your Security descriptor  
  43.     -1,  
  44.     IntPtr.Zero,  
  45.     IntPtr.Zero,  
  46.     ComAPI.RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  
  47.     ComAPI.RPC_C_IMP_LEVEL_IDENTIFY,  
  48.     IntPtr.Zero,  
  49.     ComAPI.EOAC_DISABLE_AAA   
  50.     | ComAPI.EOAC_SECURE_REFS   
  51.     | ComAPI.EOAC_NO_CUSTOM_MARSHAL,  
  52.     IntPtr.Zero);  
  53.    if (hResult != 0)  
  54.     throw new ApplicationException(  
  55.      "CoIntializeSecurity failed" + hResult.ToString("X"));  
  56.   }  
  57.   // The main entry point for the process  
  58.   static void Main()  
  59.   {  
  60.    ServiceBase.Run(new ServiceBase[] { new MyService() });  
  61.   }  
  62.   ///   
  63.   /// On start, register the COM class factory  
  64.   ///   
  65.   protected override void OnStart(string[] args)  
  66.   {  
  67.    Guid CLSID_MyObject = new Guid(MyService.guidMyClass);  
  68.    UInt32 hResult = ComAPI.CoRegisterClassObject(  
  69.     ref CLSID_MyObject,   
  70.     new MyClassFactory(),   
  71.     ComAPI.CLSCTX_LOCAL_SERVER,   
  72.     ComAPI.REGCLS_MULTIPLEUSE,   
  73.     out _cookie);  
  74.    if (hResult != 0)  
  75.     throw new ApplicationException(  
  76.      "CoRegisterClassObject failed" + hResult.ToString("X"));    
  77.   }  
  78.   ///   
  79.   /// On stop, remove the COM class factory registration  
  80.   ///   
  81.   protected override void OnStop()  
  82.   {  
  83.    if (_cookie != 0)  
  84.     ComAPI.CoRevokeClassObject(_cookie);  
  85.   }  
  86.   private int _cookie = 0;  
  87.   //  
  88.   // Public constants  
  89.   //  
  90.   public const string serviceName = "MyService";  
  91.   public const string guidIMyInterface = "e88d15a5-0510-4115-9aee-a8421c96decb";  
  92.   public const string guidMyClass = "f681abd0-41de-46c8-9ed3-d0f4eba19891";  
  93.  }  
  94.  //  
  95.  // Standard installer   
  96.  //  
  97.  [RunInstaller(true)]  
  98.  public class MyServiceInstaller :   
  99.   System.Configuration.Install.Installer  
  100.  {  
  101.   public MyServiceInstaller()  
  102.   {  
  103.    processInstaller = new ServiceProcessInstaller();  
  104.    serviceInstaller = new ServiceInstaller();  
  105.    // Add a new service running under Local SYSTEM  
  106.    processInstaller.Account = ServiceAccount.LocalSystem;  
  107.    serviceInstaller.StartType = ServiceStartMode.Manual;  
  108.    serviceInstaller.ServiceName = MyService.serviceName;  
  109.    Installers.Add(serviceInstaller);  
  110.    Installers.Add(processInstaller);  
  111.   }  
  112.   private ServiceInstaller serviceInstaller;  
  113.   private ServiceProcessInstaller processInstaller;  
  114.  }  
  115.  //  
  116.  // Internal COM Stuff  
  117.  //  
  118.  ///   
  119.  /// P/Invoke calls  
  120.  ///   
  121.  internal class ComAPI  
  122.  {  
  123.   [DllImport("OLE32.DLL")]  
  124.   public static extern UInt32 CoInitializeSecurity(  
  125.    IntPtr securityDescriptor,   
  126.    Int32 cAuth,  
  127.    IntPtr asAuthSvc,  
  128.    IntPtr reserved,  
  129.    UInt32 AuthLevel,  
  130.    UInt32 ImpLevel,  
  131.    IntPtr pAuthList,  
  132.    UInt32 Capabilities,  
  133.    IntPtr reserved3  
  134.    );  
  135.   [DllImport ("ole32.dll")]  
  136.   public static extern UInt32 CoRegisterClassObject (  
  137.    ref Guid rclsid,   
  138.    [MarshalAs (UnmanagedType.Interface)]IClassFactory pUnkn,   
  139.    int dwClsContext,   
  140.    int flags,   
  141.    out int lpdwRegister);  
  142.   [DllImport ("ole32.dll")]  
  143.   public static extern UInt32 CoRevokeClassObject (int dwRegister);  
  144.   public const int RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6; // Encrypted DCOM communication  
  145.   public const int RPC_C_IMP_LEVEL_IDENTIFY = 2;  // No impersonation really required  
  146.   public const int CLSCTX_LOCAL_SERVER = 4;   
  147.   public const int REGCLS_MULTIPLEUSE = 1;  
  148.   public const int EOAC_DISABLE_AAA = 0x1000;  // Disable Activate-as-activator  
  149.   public const int EOAC_NO_CUSTOM_MARSHAL = 0x2000; // Disable custom marshalling  
  150.   public const int EOAC_SECURE_REFS = 0x2;   // Enable secure DCOM references  
  151.   public const int CLASS_E_NOAGGREGATION = unchecked((int)0x80040110);  
  152.   public const int E_NOINTERFACE = unchecked((int)0x80004002);  
  153.   public const string guidIClassFactory = "00000001-0000-0000-C000-000000000046";  
  154.   public const string guidIUnknown = "00000000-0000-0000-C000-000000000046";  
  155.  }  
  156.  ///   
  157.  /// IClassFactory declaration  
  158.  ///   
  159.  [ComImport (), InterfaceType (ComInterfaceType.InterfaceIsIUnknown),   
  160.  Guid (ComAPI.guidIClassFactory)]  
  161.  internal interface IClassFactory  
  162.  {  
  163.   [PreserveSig]  
  164.   int CreateInstance (IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);  
  165.   [PreserveSig]  
  166.   int LockServer (bool fLock);  
  167.  }  
  168.  ///   
  169.  /// My Class factory implementation  
  170.  ///   
  171.  internal class MyClassFactory : IClassFactory  
  172.  {  
  173.   public int CreateInstance (IntPtr pUnkOuter,   
  174.    ref Guid riid,   
  175.    out IntPtr ppvObject)  
  176.   {  
  177.    ppvObject = IntPtr.Zero;  
  178.    if (pUnkOuter != IntPtr.Zero)  
  179.     Marshal.ThrowExceptionForHR (ComAPI.CLASS_E_NOAGGREGATION);  
  180.    if (riid == new Guid(MyService.guidIMyInterface)   
  181.     || riid == new Guid(ComAPI.guidIUnknown))  
  182.    {  
  183.     //  
  184.     // Create the instance of my .NET object  
  185.     //  
  186.     ppvObject = Marshal.GetComInterfaceForObject(  
  187.         new CMyClass(), typeof(IMyInterface));  
  188.    }  
  189.    else  
  190.     Marshal.ThrowExceptionForHR (ComAPI.E_NOINTERFACE);  
  191.    return 0;  
  192.   }  
  193.   public int LockServer (bool lockIt)  
  194.   {  
  195.    return 0;  
  196.   }   
  197.  }  
  198. }  

下面是注册的CMD批处理脚本:

 

[c-sharp]  view plain copy print ?
  1. set EXE_FULL_PATH=%~dp0windowsservice1.exe  
  2. if not exist %EXE_FULL_PATH% @echo Executable %EXE_FULL_PATH% not present in the current directory! & @goto :EOF  
  3. installutil /u %EXE_FULL_PATH%  
  4. installutil %EXE_FULL_PATH%  
  5. regasm %EXE_FULL_PATH% /codebase  
  6. REG.EXE ADD HKCR/AppID/{9922b97d-ce4a-4cc8-a26f-4944708e652d} /v LocalService /t REG_SZ /d MyService /f  
  7. REG.EXE ADD HKCR/CLSID/{F681ABD0-41DE-46C8-9ED3-D0F4EBA19891}/LocalServer32 /ve /t REG_SZ /d %EXE_FULL_PATH% /f  
  8. REG.EXE DELETE HKCR/CLSID/{F681ABD0-41DE-46C8-9ED3-D0F4EBA19891}/InprocServer32 /f  
 

 

And a test VBS script that will exercise our service from a separate process obviously:

 

通过一个独立进程的VBS测试脚本来实践我们的服务

[xhtml]  view plain copy print ?
  1. Dim obj  
  2. Set obj = CreateObject( "Test.CMyClass" )  
  3. wscript.echo obj.GetDateTime("Current date: ")  

 

One more comment: The code above allows everybody to call into the process. This is probably not very useful since you might want to allow only administrators to call into your service. There is a solution though: all you have to do is to pass a certain security descriptor to CoInitializeSecurity that will allow only certain classes of users to call into the process. This is actually not very hard, and I'll probably post a sample code in the future. Now I have to get back to work, so see you for the next time!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值