Given the following C# server code to start a DCOM server:
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;
}
}
}
Which we assume is compiled as a dll and registered with regasm
and then called with the following VBS code *locally:
Dim obj
Set obj = CreateObject( "Test.CMyClass" )
wscript.echo obj.GetDateTime("Current date: ")
Now assume I want to run this vbs code on another machine and call the DCOM server remotely. What do I need to change?
My question is: How to write a DCOM server in C# that you can call remotely? (Assuming all the above steps have been done)
You use the Component Services MMC, or DCOMCnfg.exe, to configure it for remote access.
Then you can create it using the two-argument form of CreateObject.
Set obj = CreateObject( "Test.CMyClass", "SERVERNAME" )
User contributions licensed under CC BY-SA 3.0