如何用 C# 编写可以远程调用的 DCOM 服务器?

Posted

技术标签:

【中文标题】如何用 C# 编写可以远程调用的 DCOM 服务器?【英文标题】:How to write a DCOM server in C# that you can call remotely? 【发布时间】:2014-03-07 13:10:09 【问题描述】:

给定以下 C# 服务器代码来启动 DCOM 服务器:

using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Runtime.InteropServices;

namespace Test

 // 
 // .NET class, interface exposed through DCOM
 //

 // exposed COM interface
 [GuidAttribute(MyService.guidIMyInterface), ComVisible(true)]
 public interface IMyInterface
 
  string GetDateTime(string prefix); 
 

 // exposed COM class
 [GuidAttribute(MyService.guidMyClass), ComVisible(true)]
 public class CMyClass: IMyInterface
 
  // Print date & time and the current EXE name
  public string GetDateTime(string prefix) 
   
   Process currentProcess = Process.GetCurrentProcess();
   return string.Format("0: 1 [server-side COM call executed on 2]", 
    prefix, DateTime.Now, currentProcess.MainModule.ModuleName);
   
 

 //
 // My hosting Windows service
 //
 internal class MyService : 
  ServiceBase
 
  public MyService()
  
   // Initialize COM security
   Thread.CurrentThread.ApartmentState = ApartmentState.STA;
   UInt32 hResult = ComAPI.CoInitializeSecurity(
    IntPtr.Zero, // Add here your Security descriptor
    -1,
    IntPtr.Zero,
    IntPtr.Zero,
    ComAPI.RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
    ComAPI.RPC_C_IMP_LEVEL_IDENTIFY,
    IntPtr.Zero,
    ComAPI.EOAC_DISABLE_AAA 
    | ComAPI.EOAC_SECURE_REFS 
    | ComAPI.EOAC_NO_CUSTOM_MARSHAL,
    IntPtr.Zero);
   if (hResult != 0)
    throw new ApplicationException(
     "CoIntializeSecurity failed" + hResult.ToString("X"));
  

  // The main entry point for the process
  static void Main()
  
   ServiceBase.Run(new ServiceBase[]  new MyService() );
  
  /// 

  /// On start, register the COM class factory
  /// 

  protected override void OnStart(string[] args)
  
   Guid CLSID_MyObject = new Guid(MyService.guidMyClass);
   UInt32 hResult = ComAPI.CoRegisterClassObject(
    ref CLSID_MyObject, 
    new MyClassFactory(), 
    ComAPI.CLSCTX_LOCAL_SERVER, 
    ComAPI.REGCLS_MULTIPLEUSE, 
    out _cookie);
   if (hResult != 0)
    throw new ApplicationException(
     "CoRegisterClassObject failed" + hResult.ToString("X"));  
  
  /// 

  /// On stop, remove the COM class factory registration
  /// 

  protected override void OnStop()
  
   if (_cookie != 0)
    ComAPI.CoRevokeClassObject(_cookie);
  
  private int _cookie = 0;

  //
  // Public constants
  //
  public const string serviceName = "MyService";
  public const string guidIMyInterface = "e88d15a5-0510-4115-9aee-a8421c96decb";
  public const string guidMyClass = "f681abd0-41de-46c8-9ed3-d0f4eba19891";
 

 //
 // Standard installer 
 //
 [RunInstaller(true)]
 public class MyServiceInstaller : 
  System.Configuration.Install.Installer
 
  public MyServiceInstaller()
  
   processInstaller = new ServiceProcessInstaller();
   serviceInstaller = new ServiceInstaller();
   // Add a new service running under Local SYSTEM
   processInstaller.Account = ServiceAccount.LocalSystem;
   serviceInstaller.StartType = ServiceStartMode.Manual;
   serviceInstaller.ServiceName = MyService.serviceName;
   Installers.Add(serviceInstaller);
   Installers.Add(processInstaller);
  
  private ServiceInstaller serviceInstaller;
  private ServiceProcessInstaller processInstaller;
 

 //
 // Internal COM Stuff
 //

 /// 

 /// P/Invoke calls
 /// 

 internal class ComAPI
 
  [DllImport("OLE32.DLL")]
  public static extern UInt32 CoInitializeSecurity(
   IntPtr securityDescriptor, 
   Int32 cAuth,
   IntPtr asAuthSvc,
   IntPtr reserved,
   UInt32 AuthLevel,
   UInt32 ImpLevel,
   IntPtr pAuthList,
   UInt32 Capabilities,
   IntPtr reserved3
   );
  [DllImport ("ole32.dll")]
  public static extern UInt32 CoRegisterClassObject (
   ref Guid rclsid, 
   [MarshalAs (UnmanagedType.Interface)]IClassFactory pUnkn, 
   int dwClsContext, 
   int flags, 
   out int lpdwRegister);
  [DllImport ("ole32.dll")]
  public static extern UInt32 CoRevokeClassObject (int dwRegister);
  public const int RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6; // Encrypted DCOM communication
  public const int RPC_C_IMP_LEVEL_IDENTIFY = 2;  // No impersonation really required
  public const int CLSCTX_LOCAL_SERVER = 4; 
  public const int REGCLS_MULTIPLEUSE = 1;
  public const int EOAC_DISABLE_AAA = 0x1000;  // Disable Activate-as-activator
  public const int EOAC_NO_CUSTOM_MARSHAL = 0x2000; // Disable custom marshalling
  public const int EOAC_SECURE_REFS = 0x2;   // Enable secure DCOM references
  public const int CLASS_E_NOAGGREGATION = unchecked((int)0x80040110);
  public const int E_NOINTERFACE = unchecked((int)0x80004002);
  public const string guidIClassFactory = "00000001-0000-0000-C000-000000000046";
  public const string guidIUnknown = "00000000-0000-0000-C000-000000000046";
 

 /// 

 /// IClassFactory declaration
 /// 

 [ComImport (), InterfaceType (ComInterfaceType.InterfaceIsIUnknown), 
 Guid (ComAPI.guidIClassFactory)]
 internal interface IClassFactory
 
  [PreserveSig]
  int CreateInstance (IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
  [PreserveSig]
  int LockServer (bool fLock);
 

 /// 

 /// My Class factory implementation
 /// 

 internal class MyClassFactory : IClassFactory
 
  public int CreateInstance (IntPtr pUnkOuter, 
   ref Guid riid, 
   out IntPtr ppvObject)
  
   ppvObject = IntPtr.Zero;
   if (pUnkOuter != IntPtr.Zero)
    Marshal.ThrowExceptionForHR (ComAPI.CLASS_E_NOAGGREGATION);
   if (riid == new Guid(MyService.guidIMyInterface) 
    || riid == new Guid(ComAPI.guidIUnknown))
   
    //
    // Create the instance of my .NET object
    //
    ppvObject = Marshal.GetComInterfaceForObject(
        new CMyClass(), typeof(IMyInterface));
   
   else
    Marshal.ThrowExceptionForHR (ComAPI.E_NOINTERFACE);
   return 0;
  
  public int LockServer (bool lockIt)
  
   return 0;
   
 

我们假设它被编译为 dll 并使用 regasm 注册,然后使用以下 VBS 代码 *本地调用

Dim obj
Set obj = CreateObject( "Test.CMyClass" )
wscript.echo obj.GetDateTime("Current date: ")

现在假设我想在另一台机器上运行这个 vbs 代码并远程调用 DCOM 服务器。 我需要改变什么?

我的问题是:如何用C#编写一个可以远程调用的DCOM服务器?(假设以上步骤都已完成)

【问题讨论】:

【参考方案1】:

您使用组件服务 MMC 或 DCOMCnfg.exe 对其进行配置以进行远程访问。

然后你可以使用 CreateObject 的两个参数形式来创建它。

Set obj = CreateObject( "Test.CMyClass", "SERVERNAME" )

【讨论】:

我确实尝试过 - 但出现错误。也许我做错了什么。您能否详细说明我需要遵循的步骤? @hawkeye,您应该查看错误号 - 它们很有意义。

以上是关于如何用 C# 编写可以远程调用的 DCOM 服务器?的主要内容,如果未能解决你的问题,请参考以下文章

如何用jmx监控tomcat 服务器

如何用C#设计上位机(小白篇)

如何用myeclispe远程调试tomcat

如何用C语言来编写让系统中某个服务停止或重启?

c#调用c++如何用unit8传递值

dll文件如何用C语言生成