|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
ContentsIntroductionThe distributed object has to be properly hosted by the application domain process such as Web Server, Console, Windows Form or Windows Service before using it. The host process requires configuration of a remoting infrastructure to enable consuming the published objects. The remoting configuration can be done programmatically or administratively. Using the config file to administrate an application deployment is a preferable and recommended way to easy mapped the logical application model to its physical implementation. The Remoting Management Console (RMC) is an administrative tool to create and configure a remoting host process built as a windows service. The console has a mechanism to create a windows service on the fly including its configuration file. Using the properly snap-in nodes the config file can be administratively finalized based on the application requirements before the host process starts. On the other hand, the RMC is also very useful tool for administrating (tuning phase) already deployed the distributed application modifying the contents of the host process config files. This article describes a usage and implementation of the Remoting Management Console tool. Features
Basically the RMC snap-in control is divided into the following activities: Host Process
Config File
Inside of the remoting section the following sections are managed:
Drag&Drop FeatureThere are few places where the drag&drop can be used to enter an existing assembly:
Additionally, the drag&drop can be used within the configSection to move or copy nodes in the root. ConceptThe concept of the RMC is based on the following:
Using the windows service to host remoting objects is straightforward. The standard boilerplate generated by .Net wizard has been extended for a remoting registration and un-registration parts like it's shown in the following code snippet: protected override void OnStart(string[] args)
{
if(args.Length == 1) Thread.Sleep(Convert.ToInt32(args[0]));
RemotingConfiguration.Configure(
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
}
protected override void OnStop()
{
foreach(IChannel objChannel in ChannelServices.RegisteredChannels)
ChannelServices.UnregisterChannel(objChannel);
}
When the windows service receives a Start command, the The host process (windows service) must have a unique name registered on the local machine. The RMC has a mechanism to create a unique host process on the fly based on the application requirements. Practically, the host process template source is modified in the variable places before its compiling (see Implementation) Once the host process has been created, the RMC will control its state such as start, stop and restart using the MMC environment. In the case of the existing non-installed host process, the RMC has a drag&drop capability making its entry to the catalog. The other hand, the already installed host processes are scanned and added into the snap-in. Notice that the host process need to have an implementation of the derived Installer class attributed with RunInstaller(true) to make its installation rsp. un-installation service automatically. The initiate config file image is generated from the template The RMC is a stateless snap-in in the MMC environment. Each node is updated on the runtime based on the host process status and contents of the config file. Thanks for MMCLib library developed by http://www.ironringsoftware.com to make my life easy to handle a unmanaged MMC code. I made some slightly modification of the MMCLib for my needs, that's why I included its source in my solution. Basically, the MMCLib handles a snap-in such as creating nodes and processing their events. The following picture show that:
Based on the menu selection, for instance: New -> Remote Object, the snap-in delegates call to the properly event handler (OnNewTask) to perform the specific action. In this case, the windows form is popup to enter necessary attributes requested by the remoting service section in the config file. After pressing the Apply button on the form, the config file is updated and snap-in is notified via its handler - OnUser. This scenario is repeated mostly for all node's activities. UsageThe RMC requires to be installed by the Let me suppose that we have a remote object and its consumer ready to deploy them. Before the actually work, the remoting object is necessary be hosted and published by windows service host process. What we need to do?
Additionally, based on the application requirements:
When the configuration process is done, the host process is ready to start. Using the Event Log we can see a process of the remoting configuration. Based on the detail event log message is easy to figure out which attribute in the config file caused the problem. Now, if we know what the remoting configuration needs, let handle this task do it by RMC. Here are its steps: Step A. Create a new host process
Now we have a host process installed as a windows service and initiate image of the host process config file. The RMC snap-in created automatically static nodes for this host process:
The above nodes represent xml sections in the host process remoting config file. For the next step we are going to administrate them based on the application requirements. Step B. Publishing Remote Object
The other, shortcut way (skipping the steps 1-3) is to use a Drag&Drop feature when the remote object exist. The following steps explain that:
Step C. Configuring Channel
Notes:
Now, we have a basic configuration of the host process for our remote object. Of course, we can continue to make more advance configuration. I assume you have a knowledge of the remoting configuration and MMC to allow you easy figure out usage of the others nodes in the RMC snap-in. Let's continue with our basic configuration. As the next step is to start the host process. Step D. Start Host Process
To Stop or Restart the host process follow the same steps as for Start one. Refreshing snap-inEach static node in the snap-in has a Refresh menu item to perform a updating result view and snap-in at the selected level. There is one special Refresh at the snap-in root. In this case the RMC is invoking the scanner of the installed host processes (windows services) on the local machine and refreshing a completely snap-in. You can use also the key F5 to invoke the refresh task. ImplementationThe RMC Solution is divided into the following projects:
Note that all projects except the RemotingManagement are re-usable, for instance: HostProcessLib can be used for programmatically creating a host process on the fly in your application, etc. The following code snippet show an implementation of the creating a host process on the fly: public string Create(string strServiceName, string strServiceDesc,
bool bStartAutomatic, bool bTrayIcon,
string strAssemblyName)
{
string strAssemblyFile = null;
//--- create service ---
ServiceSrcCode ssc = new ServiceSrcCode();
string strServiceSrcCode = ssc.GetSrcCodeForService(strServiceName,
strServiceDesc,
bStartAutomatic,
bTrayIcon);
string strOutputAssembly = string.Format(@"{0}.exe", strAssemblyName);
// assembly compilation.
string[] strArrayReferences = {
"System.dll",
"System.Data.dll",
"System.ServiceProcess.dll",
"System.Configuration.Install.dll" };
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.AddRange(strArrayReferences);
cp.GenerateExecutable = true;
cp.GenerateInMemory = false;
cp.OutputAssembly = strOutputAssembly;
cp.WarningLevel = 4;
cp.IncludeDebugInformation = false;
ICodeCompiler icc = new CSharpCodeProvider().CreateCompiler();
CompilerResults cr = icc.CompileAssemblyFromSource(cp, strServiceSrcCode);
if(cr.Errors.Count > 0)
{
foreach(string s in cr.Output)
{
Trace.WriteLine(s);
}
throw new Exception(string.Format("Build failed: {0} errors",
cr.Errors.Count));
}
//cr.TempFiles.KeepFiles = true;
strAssemblyFile = cp.OutputAssembly;
return strAssemblyFile;
}
The above function is using the #region windows service template text const string strSrcTmplService = "namespace RemotingHostService" + "{ " + "using System;" + "using System.Collections;" + "using System.ComponentModel;" + "using System.Data;" + "using System.Diagnostics;" + "using System.ServiceProcess;" + "using System.Runtime.Remoting;" + "using System.Runtime.Remoting.Channels;" + "using System.Threading;" + "public class Service : ServiceBase" + "{" + "private Container components = null;" + "public Service()" + "{"+ "components = new Container();" + "this.ServiceName = \"SERVICE_NAME\";" + "}" + "static void Main()" + "{" + "ServiceBase[] ServicesToRun;" + "ServicesToRun = new ServiceBase[] { new Service() };" + "Run(ServicesToRun);" + "}" + "protected override void Dispose(bool disposing)" + "{" + "if(disposing) { if(components != null) components.Dispose(); }" + "base.Dispose(disposing);"+ "}" + "protected override void OnStart(string[] args)" + "{" + "if(args.Length == 1) Thread.Sleep(Convert.ToInt32(args[0]));" + "RemotingConfiguration.Configure(AppDomain.CurrentDomain." + "SetupInformation.ConfigurationFile);" + "}" + "protected override void OnStop()" + "{" + "foreach(IChannel objChannel in ChannelServices.RegisteredChannels)" + "ChannelServices.UnregisterChannel(objChannel);" + "}" + "}\r\n" + "[RunInstaller(true)]" + "public class ProjectInstaller : System.Configuration.Install.Installer" + "{" + "private ServiceProcessInstaller serviceProcessInstaller1;" + "private ServiceInstaller serviceInstaller1;" + "public ProjectInstaller() {InitializeComponent();}" + "private void InitializeComponent()" + "{" + "this.serviceProcessInstaller1 = new ServiceProcessInstaller();" + "this.serviceInstaller1 = new ServiceInstaller();"+ "this.serviceProcessInstaller1.Account = ServiceAccount.LocalSystem;" + "this.serviceInstaller1.ServiceName = \"SERVICE_NAME\"; " + "this.serviceInstaller1.StartType = SERVICE_START;" + "this.Installers.AddRange(new System.Configuration.Install.Installer[]" + "{this.serviceProcessInstaller1, this.serviceInstaller1});" + "}" + "public override void Install(IDictionary stateServer)" + "{" + "Microsoft.Win32.RegistryKey system, currentControlSet, services, " + "service; " + "base.Install(stateServer);" + "system = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(\"System\");" + "currentControlSet = system.OpenSubKey(\"CurrentControlSet\");" + "services = currentControlSet.OpenSubKey(\"Services\");" + "service = services.OpenSubKey(this.serviceInstaller1.ServiceName, " + "true);" + "service.SetValue(\"Description\", \"SERVICE_DESCRIPTION\");" + "SERVICE_TRAYICON" + "}" + "}" + "}"; #endregion The template requires to customize its entries such as As I mentioned early, the RemotingManagement contains many forms and user controls to handle a particular snap-in node. Basically, their implementations has the same design pattern based on the event driven mechanism. You can see it in the following code snippet how the lifetime node handle it: #region Lifetime protected BaseNode CreateLifetimeNodeTree(BaseNode parentNode, string nodeName, bool bRefresh) { FormNode node = new FormNode(this); node.ControlType = Type.GetType("RKiss.RemotingManagement.PropertiesControl"); node.DisplayName = nodeName; node.Tag = parentNode.Tag; node.OpenImageIndex = intLifetimeImage; node.ClosedImageIndex = intLifetimeImage; //---add/insert this node if(bRefresh) node.Insert(parentNode); else parentNode.AddChild(node); node.OnSelectScopeEvent += new NodeNotificationHandler(OnSelectEvent_Lifetime); node.OnQueryPropertiesEvent += new NodeNotificationHandler(OnQueryProperties_Lifetime); //---this is a post-process notification from the user form node.OnUserEvent += new NodeNotificationHandler(OnUserEvent_Lifetime); return node; } private void OnSelectEvent_Lifetime(object sender, NodeEventArgs args) { BaseNode selNode = sender as BaseNode; //---checkpoint Trace.WriteLine(string.Format("OnSelectEvent: sender={0}", selNode.DisplayName)); //---ask snap-in for the following buttons IConsoleVerb icv; selNode.Snapin.ResultViewConsole.QueryConsoleVerb(out icv); icv.SetVerbState(MMC_VERB.PROPERTIES, MMC_BUTTON_STATE.ENABLED, 1); //---status text selNode.Snapin.ResultViewConsole.SetStatusText("The lifetime properties" + "of the remote singleton and activated objects in the application."); } protected void OnQueryProperties_Lifetime(object sender, NodeEventArgs e) { BaseNode selNode = sender as BaseNode; //---checkpoint Trace.WriteLine(string.Format("OnQueryProperties: sender={0}", selNode.DisplayName)); //---action string strConfigFilePath = Convert.ToString(selNode.Tag) + ".config"; LifetimeForm formLT = new LifetimeForm(selNode, selNode.DisplayName, strConfigFilePath); formLT.Show(); } private void OnUserEvent_Lifetime(object sender, NodeEventArgs args) { try { //---inputs BaseNode node = sender as BaseNode; NameValueArgs nva = args as NameValueArgs; string strCheckpoint = nva.Key; string strLifetimeName = Convert.ToString(nva.Val); //---checkpoint Trace.WriteLine(string.Format("User Event: sender={0}, checkpoint={1}, val={2}", node.DisplayName, strCheckpoint, strLifetimeName)); //---refresh and set scope node.Snapin.ResultViewConsole.SelectScopeItem(node.HScopeItem); } catch(Exception ex) { Trace.WriteLine(string.Format("OnUserEvent_Lifetime - failed, " + "error = {0}", ex.Message)); } } #endregion The CreateLifetimeNodeTree method has responsibility to create node in the snap-in and subscribe the requested delegates such as
TestI created a separate solution to test the RMC:
As you can see this test solution doesn't have a host server project, that's why the RMC comes to create one. The client is a simple windows form to ask a remote object for the specific section in the config file. Here is its screen shot:
Note that the msi file will install also this test sample, so it's easy to click for the WindowsFormClient icon in the RMC desktop folder to start test it. VersionThis is a pre-view version of the Remoting Management Console. I decided to release it before finishing all features what I have in my plan:
ConclusionIn this article I described a tool hosted by MMC that allows to administrate any remoting object without writing its host process. The Remoting Management Console tool will create automatically a remoting host process including its configuration file. The RMC becomes very useful tool especially during the deployment phase, where a configuration of the distributed objects need to be tuned based on the deployment environment. The release version with features such as remoting probe and enterprise support to allow administrate the remoting host process remotely will give you a powerful administration tool for your product. | ||||||||||||||||||||