﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-玄铁剑-文章分类-Service</title><link>http://www.cnitblog.com/MartinYao/category/4623.html</link><description>成功的途径：抄，创造，研究，发明...</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 12:06:11 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 12:06:11 GMT</pubDate><ttl>60</ttl><item><title>Web Service Authentication</title><link>http://www.cnitblog.com/MartinYao/articles/28366.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 12 Jun 2007 13:49:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/28366.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/28366.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/28366.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/28366.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/28366.html</trackback:ping><description><![CDATA[<ul class=download>
    <li><a href="http://www.codeproject.com/cs/webservices/WebServiceAuthentication/WebServiceAuthentication_src.zip">Download source files - 32.3 KB</a></li>
</ul>
<p><img height=200 alt="Sample Image - WebServiceAuthentication.gif" src="http://www.codeproject.com/cs/webservices/WebServiceAuthentication/WebServiceAuthentication.gif" width=400></p>
<h2>Introduction</h2>
<p>This is a simple mechanism to authenticate users to a Web Service, using a Time Token and MD5 Hashing to encrypt password.</p>
<h2>Background</h2>
<p>In CodeProject, you can find at least two others' mechanism to authenticate users to a Web Service. <a href="http://www.codeproject.com/script/profile/whos_who.asp?id=35828">Dan_P</a> wrote <a href="http://www.codeproject.com/cs/webservices/authforwebservices.asp">Authentication for Web Services</a> as a <em>Simple authentication for web services using SOAP headers</em>. But the username and password are send in clear text and there is no encryption for the data. <a href="http://www.codeproject.com/script/profile/whos_who.asp?id=57505">HENDRIK R.</a> is the author of <a href="http://www.codeproject.com/webservices/WS-Security.asp">An introduction to Web Service Security using WSE</a>, that is really a complete solution, but too much complicated for my purposes. The username is send in clear text, but it is possible to use Password Digest to encrypt the password. The data are encrypted using XML Encryption specification to encrypt portions of the SOAP messages.</p>
<p>My solution is something in the middle of the above two. The username is send in clear text, but I use MD5 to encrypt the password. I do not need to send sensitive data, so the data returned by the Web Service is not encrypted. </p>
<h2>Using the code</h2>
<p>The basic idea is to send <code>UserName</code> and <code>Password</code> from the client to the Web Service using MD5 Hash Code as encryption system. In this way, the password never travels in clear over the network. The Web Service retrieves the user password from a DB or anything else and uses the same MD5 algorithm to test if the password is correct. To be sure that if someone intercepts the Hash, this can be used to authenticate in a later time, I added a timestamp before hashing the <code>Key</code> string. Last, as we are not always on the same server and/or the client clock may be in a different Time Zone or simply not synchronized, I added the possibility to request a Token containing the time mark to the server.</p>
<p>I provided a sample in ASP.NET C# for the client side, but it is possible to use any language: ASP classical JScript or VBScript, PHP, Python, etc. Anyway, on the client side we need to build up the <code>Key</code> using <code>UserName</code>, <code>Password</code> and the hashed timestamp <code>Token</code> previously got from the same Web Service. We can then call the Service and we will get the answer (or an authentication failure warning) that is displayed on the web page.</p>
<pre lang=cs>private void ButtonUseToken_Click(object sender, System.EventArgs e)
{
string ret;
string UserName, Password, ServiceName, Token;
string Key, ToHash;
UserName=this.TextBoxUserName.Text;
Password=this.TextBoxPwd.Text;
ServiceName=this.TextBoxService.Text;
Token=this.TextBoxToken.Text;
ToHash=UserName.ToUpper()+<span class=cpp-string>"|"</span>+Password+<span class=cpp-string>"|"</span>+Token;
Key=Hash(ToHash)+<span class=cpp-string>"|"</span>+UserName;
ServicePointReference.ServicePoint Authenticate =
new ServicePointReference.ServicePoint();
ret=Authenticate.UseService(Key, ServiceName);
this.ServResponse.Text=ret;
}
</pre>
<p>The MD5 <code>Hash</code> procedure is very simple in C#; this one was written by <a href="http://www.codeproject.com/script/profile/whos_who.asp?id=52123">Vasudevan Deepak Kumar</a> in <a href="http://www.codeproject.com/csharp/Securing_Web_Accounts.asp">Securing Web Accounts</a>.</p>
<pre lang=cs>private string Hash(string ToHash)
{
<span class=cs-comment>// First we need to convert the string into bytes,</span>
<span class=cs-comment>// which means using a text encoder.</span>
Encoder enc = System.Text.Encoding.ASCII.GetEncoder();
<span class=cs-comment>// Create a buffer large enough to hold the string</span>
byte[] data = new byte[ToHash.Length];
enc.GetBytes(ToHash.ToCharArray(), 0, ToHash.Length, data, 0, true);
<span class=cs-comment>// This is one implementation of the abstract class MD5.</span>
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(data);
return BitConverter.ToString(result).Replace(<span class=cpp-string>"-"</span>, <span class=cpp-string>""</span>).ToLower();
}
</pre>
<p>On Web Service server side I implemented just three Web Methods:</p>
<p><code>GetToken</code> is used to get the Time-marked token. The token you get this way, is intended to be used in the basic <code>Authenticate</code> method, or in the <code>UseService</code> that can also verify the access rights for the users authenticated to the requested service. The core of the system is implemented by <code>TestHash</code>. Here the password is hard-coded, but in the sample provided you have also the code to get it from a database:</p>
<div class=precollapse id=premain2 style="WIDTH: 100%"><img id=preimg2 style="CURSOR: hand" height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="2"><span id=precollapse2 style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="2"> Collapse</span></div>
<pre lang=cs id=pre2 style="MARGIN-TOP: 0px">private bool TestHash (string HashStr,
string UserName, int minutes, string ServiceName)
{
string Pwd, ToHash;
string sResult, sResultT, sResultToken;
try
{
<span class=cs-comment>// JUST FOR TEST: the password is hard-coded:</span>
Pwd=<span class=cpp-string>"SeCrEt"</span>;
DateTime dt = DateTime.Now;
System.TimeSpan minute = new System.TimeSpan(0,0,minutes,0,0);
dt = dt-minute;
<span class=cs-comment>//before hashing we have:</span>
<span class=cs-comment>//USERNAME|PassWord|YYYYMMDD|HHMM</span>
ToHash=UserName.ToUpper()+<span class=cpp-string>"|"</span>+Pwd+<span class=cpp-string>"|"</span>+dt.ToString(<span class=cpp-string>"yyyyMMdd"</span>)+
<span class=cpp-string>"|"</span>+dt.ToString(<span class=cpp-string>"HHmm"</span>);
sResult = Hash(ToHash);
<span class=cs-comment>//TokenWeGotBefore</span>
ToHash=dt.ToString(<span class=cpp-string>"yyyyMMdd"</span>)+<span class=cpp-string>"|"</span>+dt.ToString(<span class=cpp-string>"HHmm"</span>);
sResultToken = Hash(ToHash);
<span class=cs-comment>//USERNAME|PassWord|TokenWeGotBefore</span>
ToHash=UserName.ToUpper()+<span class=cpp-string>"|"</span>+Pwd+<span class=cpp-string>"|"</span>+sResultToken;
sResultT = Hash(ToHash);
if ((sResult==HashStr) || (sResultT==HashStr))
return true;
else
if (minutes==0) <span class=cs-comment>// allowed max 2 minutes - 1</span>
<span class=cs-comment>// second to call web service</span>
return TestHash (HashStr, UserName, 1, ServiceName);
else
return false;
}
catch
{
return false;
}
}
</pre>
<p>To request a hashed time-stamped Token to the Web Service the method is:</p>
<pre lang=cs>[WebMethod]
public string GetToken ()
{
string ToHash, sResult;
DateTime dt = DateTime.Now;
ToHash=dt.ToString(<span class=cpp-string>"yyyyMMdd"</span>)+<span class=cpp-string>"|"</span>+dt.ToString(<span class=cpp-string>"HHmm"</span>);
sResult = Hash(ToHash);
return sResult;
}
</pre>
<p>The method that checks the user authentication is also kept very simple; in a real application you normally need to access a database to check the authentication level and may need to return some data to the caller:</p>
<div class=precollapse id=premain4 style="WIDTH: 100%"><img id=preimg4 style="CURSOR: hand" height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="4"><span id=precollapse4 style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="4"> Collapse</span></div>
<pre lang=cs id=pre4 style="MARGIN-TOP: 0px">[WebMethod]
public string UseService (string Key, string ServiceName)
{
string [] HashArray;
string UserName, level;
<span class=cs-comment>// Key string: HASH|User|OptionalData</span>
HashArray=Key.Split('|');
level = <span class=cpp-string>"-1"</span>;    <span class=cs-comment>//defaul level</span>
if (TestHash(HashArray[0], HashArray[1], 0, ServiceName))
{
try
{
UserName=HashArray[1];
<span class=cs-comment>// JUST FOR TEST: the User authentication level is hard-coded</span>
<span class=cs-comment>// but may/shuold be retrieved from a DataBase</span>
switch (UserName)
{
case <span class=cpp-string>"MyUserName"</span>:
level=<span class=cpp-string>"1"</span>;
break;
case <span class=cpp-string>"OtherUser"</span>:
level=<span class=cpp-string>"2"</span>;
break;
default:
level=<span class=cpp-string>"-1"</span>;
break;
}
if (level==<span class=cpp-string>"1"</span>) return <span class=cpp-string>"YOU ARE AUTHORIZED"</span>;
}
catch (Exception exc)
{
return <span class=cpp-string>"Authentication failure: "</span> + exc.ToString();
}
}
return <span class=cpp-string>"Authentication failure"</span>;
}
</pre>
<h2>Points of Interest</h2>
<p><code>TestHash</code> checks to see if the <code>Hash</code> contains a timestamp or an already-hashed token, and calls itself once again in case of failure: if someone is calling the service, let's say, at 11:34:58 the <code>Key</code> is valid from 11:34:00 until 11:35:00, that is during two minutes.</p>
<p>The client side may be implemented in any language: ASP classical, JScript or VBScript, PHP, Python, etc. I have intention to post this code too next time...</p>
<h2>History</h2>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/28366.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-06-12 21:49 <a href="http://www.cnitblog.com/MartinYao/articles/28366.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在client端通过java script调用Web Service </title><link>http://www.cnitblog.com/MartinYao/articles/24718.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Tue, 27 Mar 2007 00:59:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/24718.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/24718.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/24718.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/24718.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/24718.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 以下代码实现了在客户端用java script调用Web Service，通过对Web Service：TimeService中GetTime()方法的调用，在客户端显示服务器当前时间，并且以1秒为间隔自动刷新。		TimeService: GetTime()																		//						Return time on server							...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/24718.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/24718.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-03-27 08:59 <a href="http://www.cnitblog.com/MartinYao/articles/24718.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Self installing .NET service using the Win32 API</title><link>http://www.cnitblog.com/MartinYao/articles/21511.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Thu, 04 Jan 2007 13:57:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21511.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21511.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21511.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21511.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21511.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/csharp/HoytSoft_ServiceBase/HoytSoft_ServiceProcess_src.zip">Download demo project - 30.4 Kb</a>
				</li>
		</ul>
		<p>
				<img height="284" alt="Test service in Service Manager" src="http://www.codeproject.com/csharp/HoytSoft_ServiceBase/ServiceBase1.jpg" width="453" />
		</p>
		<h2>Introduction</h2>
		<p>The Windows service code that ships with the .NET framework and Visual Studio works just fine usually. However, sometimes it's just annoying to have to create an installer project just for a simple service you're writing. Furthermore, Microsoft tends to hide away the Win32 service infrastructure from us. I'm not saying that's a bad thing, but sometimes you just need something a little better. There're some advanced issues with services that make working with the API difficult and so wouldn't it be nice to have something that encapsulates all the functionality, but exposes it to those who need it? The code provides a base class and an attribute for you to use to define your own service.</p>
		<h2>Background</h2>
		<p>A thorough discussion of the intricacies of Win32 services is another subject. This code focuses on bringing it all together and then exposing it via an easy to use class and custom attribute.</p>
		<h2>Using the code</h2>
		<p>Using the code is as simple as deriving from the base class:</p>
		<pre lang="cs">
				<span class="cs-keyword">using</span> System;
<span class="cs-keyword">using</span> HoytSoft.ServiceProcess.Attributes;

<span class="cs-keyword">namespace</span> MyServices {
    <span class="cs-keyword">public</span><span class="cs-keyword">class</span> TestService : HoytSoft.ServiceProcess.ServiceBase {

        <span class="cs-keyword">protected</span><span class="cs-keyword">override</span><span class="cs-keyword">bool</span> Initialize(<span class="cs-keyword">string</span>[] Arguments) {
            <span class="cs-keyword">this</span>.Log(<span class="cpp-string">"Test service initialized correctly, starting up..."</span>);
            <span class="cs-keyword">return</span><span class="cs-keyword">true</span>;
        }
    }
}</pre>
		<p>And then defining an attribute:</p>
		<div class="precollapse" id="premain1" style="WIDTH: 100%">
				<img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" />
				<span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="1"> Collapse</span>
		</div>
		<pre lang="cs" id="pre1" style="MARGIN-TOP: 0px">
				<span class="cs-keyword">using</span> System;
<span class="cs-keyword">using</span> HoytSoft.ServiceProcess.Attributes;

<span class="cs-keyword">namespace</span> MyServices {
    <b>[Service(
    <span class="cpp-string">"HoytSoft_TestService"</span>, 
    DisplayName         = <span class="cpp-string">"HoytSoft Test Service"</span>, 
    Description         = <span class="cpp-string">"Isn't this just absolutely amazing?"</span>,
    ServiceType         = ServiceType.Default,
    ServiceAccessType   = ServiceAccessType.AllAccess,
    ServiceStartType    = ServiceStartType.AutoStart,
    ServiceErrorControl = ServiceErrorControl.Normal,
    ServiceControls     = ServiceControls.Default
    )]</b><span class="cs-keyword">public</span><span class="cs-keyword">class</span> TestService : HoytSoft.ServiceProcess.ServiceBase {

        <span class="cs-keyword">protected</span><span class="cs-keyword">override</span><span class="cs-keyword">bool</span> Initialize(<span class="cs-keyword">string</span>[] Arguments) {
            <span class="cs-keyword">this</span>.Log(<span class="cpp-string">"Test service initialized correctly, starting up..."</span>);
            <span class="cs-keyword">return</span><span class="cs-keyword">true</span>;
        }
    }
}</pre>
		<p>
				<img height="455" alt="Test service in Service Manager" src="http://www.codeproject.com/csharp/HoytSoft_ServiceBase/ServiceBase2.jpg" width="404" />
		</p>
		<p>All the attributes correspond to their equivalent Win32 service descriptions. A lot of the code has been commented, so a glance at the intellisense should give you an idea of what each option is and how it modifies your Windows service.</p>
		<p>It is important that you give a name to your service. It is a required parameter for the <code>Service</code> attribute. The attribute values specified here are used in the <code>ServiceBase</code>'s <code>Main()</code> method. I suppose I should also note that your service should not define its own <code>Main()</code> method since <code>ServiceBase</code> uses its own that reads in <code>Service</code> attributes and then creates objects automatically for you. If the service has not been installed, it will auto-install it for you. To uninstall a service, simply pass in "u" or "uninstall" to the program. To manually do this, go to a DOS prompt and type in: <i>sc delete "MyServiceName"</i>.</p>
		<p>An interesting feature that hasn't been tested at all is the ability to use multiple services in a single application. To do this, be sure to set the <code>ServiceType</code> to <code>ServiceType.ShareProcess</code> or <code>ServiceType.InteractiveProcessShare</code>.</p>
		<p>Now of most importance to you is probably developing, testing, and debugging your service. There is a property on the <code>Service</code> attribute named "<code>Debug</code>". Set this to <code lang="cs"><span class="cs-keyword">true</span></code> and the base class will automatically treat your program like a normal console program so you can develop the rest of the program. When you're ready to test it out as an actual service running on the machine, just switch this to <code lang="cs"><span class="cs-keyword">false</span></code> or take it out and it will work like a service. The program will install the service whether or not you're in debug mode when it's first run. If you try to run the program when it was compiled in Debug mode, you will get an error saying the process couldn't be started. This is by design since to gain all of the debug mode capabilities in Visual Studio, you can't have it start up as a real service. Simply switch the Debug mode, recompile, and it will run as normal.</p>
		<p>You can write to the system event log by making a call to <b><code lang="cs"><span class="cs-keyword">this</span>.Log(<span class="cpp-string">"My message here."</span>)</code></b>:</p>
		<p>The main meat of your code should be the overridden methods:</p>
		<ul class="method">
				<li>
						<code lang="cs">
								<span class="cs-keyword">bool</span> Initialize(<span class="cs-keyword">string</span>[] Arguments)</code>
						<p>"<code>Arguments</code>" are those passed in when called.</p>
				</li>
				<li>
						<code>Start()</code>
				</li>
				<li>
						<code>Stop()</code>
				</li>
				<li>
						<code>Pause()</code>
				</li>
				<li>
						<code>Continue()</code>
				</li>
				<li>
						<code>Shutdown()</code>
				</li>
				<li>
						<code>Interrogate()</code>
				</li>
				<li>
						<code>Install()</code>
				</li>
				<li>
						<code>Uninstall()</code>
				</li>
		</ul>
		<p>All of these should be fairly straightforward. Any questions?</p>
		<h2>Updates</h2>
		<ul>
				<li>
						<b>10/4/05:</b> After the default timeout of 30 seconds (this is a Windows-imposed standard), if the service is not being run by the service control manager (SCM), the program runs like a normal console/Windows program. This is done in the <code>ServiceBase</code>'s main method which will detect when it's not being run as a service and executes the proper methods on the service. 
<p>You just continue to use the service like you normally would through the <code>Initialize()</code> and <code>Start()</code> methods. If you want to test other methods, you can optionally use the following methods from inside <code>Start()</code>:</p><ul class="method"><li><code>TestPause()</code></li><li><code>TestContinue()</code></li><li><code>TestStop()</code></li><li><code>TestShutdown()</code></li><li><code>TestInterrogate()</code></li><li><code>TestInstall()</code></li><li><code>TestUninstall()</code></li></ul><p><code>ServiceBase</code> will take care of calling your methods while attempting to simulate a real situation or user by calling your overridden methods asynchronously.</p></li>
				<li>
						<b>10/12/05:</b> A message was added to the <a href="http://www.codeproject.com/csharp/HoytSoft_ServiceBase.asp" target="_blank">CodeProject article</a> informing me of missing copyright notices in the source code, so I've added them here along with some more updates. This version has a major fix involving running it from a referenced, external DLL. If you put the code inside another DLL and then try to use it, you may notice you can start, but can't pause or stop your service. The fix, actually, was just adding a strong name key (<i>HoytSoft.snk</i>) to the external assembly. After I did this, I was able to run the service normally. 
<p></p><p>As a result, I have divided the solution into two projects - one containing the service code and the other is just a simple example console service app.</p><p><i>Important!</i> The namespace has changed! It was <code>HoytSoft.Service</code>, but it is now <code>HoytSoft.ServiceProcess</code> to more accurately reflect its replacement of the <code>System.ServiceProcess</code> namespace. Please keep in mind that to install/run the service you must have the proper permissions!</p></li>
				<li>
						<b>10/21/05:</b> There were some more problems with referencing the <code>ServiceBase</code> class from a class library. I think the main problem was a delegate that was going out of scope that rendered a callback useless. Lots of people got errors when trying to pause, stop, or do anything on the service when using it as a class library. I was able to reproduce the problem and after making the aforementioned fixes, it worked just fine. 
<p>Also of note is the ability to explicitly and only install the service without having to run the rest of the service. To do this, just start the service and pass in "i" or "install" and it will quickly install and exit. If you start the app normally and it hasn't been installed yet, the app will work like before and install it before proceeding. You should still be able to run it as a console or service app.</p></li>
		</ul>
		<h2>Points of Interest</h2>
		<ul>
				<li>The service installs itself and can uninstall itself through a command line argument ("u" or "uninstall"). 
</li>
				<li>You can run, test, and debug your service from within Visual Studio without having to create a separate installer project or use the <code>ServiceController</code> classes. </li>
		</ul>
		<h2>History</h2>
		<ul>
				<li>Took out "<code>Debug</code>" property from the <code>ServiceAttribute</code> attribute since the code now auto-detects this. (See "Points of Interest" above). </li>
		</ul>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>About DavidHoyt</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">I'm a recent graduate of Brigham Young University in Provo, UT and now working for Lawrence Livermore National Laboratories (LLNL). I've been programming since I was 14 and did the amazing Hoyt family website with an animated gif of a spinning globe. I've come a long way since then and now actually use pictures of people.<br /><br />I've been interested in website development and Windows programming since and I haven't stopped except for two years spent in El Salvador as a religious representative for my church.<br /><br />I've done lots of work with C#/C/C++/Java/Python/JavaScript/Scheme/T-SQL/PL-SQL/Visual Basic/etc., web services, windows apps, services, and web apps. It's been a lot of fun! 
<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=2333214">here</a> to view DavidHoyt's online profile.</p></td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21511.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-04 21:57 <a href="http://www.cnitblog.com/MartinYao/articles/21511.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Webservice Send InBox</title><link>http://www.cnitblog.com/MartinYao/articles/21470.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 14:53:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21470.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21470.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21470.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21470.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21470.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/aspnet/XYEMailService/XYEMail.zip">Download source files - 17.9 Kb</a>
				</li>
		</ul>
		<h2>Introduction</h2>
		<p>This is an <i>ASP.NET</i> web service for sending e-mail messages. When programming with .NET, sending e-mail doesn't seem to be a complicated task at all, you need only to know two simple framework classes, <code>MailMessage</code> and <code>SmtpMail</code>, in the <code>System.Web.Mail</code> namespace. If you want to attach a file to your e-mail message, then you also need to know the <code>MailAttachment</code> class.</p>
		<p>Suppose the <i>SMTP</i> service is enabled on your machine, here is a C# example of sending an e-mail message with attachment.</p>
		<pre lang="cs">MailMessage oMsg = <span class="cs-keyword">new</span> MailMessage();
oMsg.From = <span class="cpp-string">"me@MyServer.Net"</span>;
oMsg.To = <span class="cpp-string">"you@YourServer.Net"</span>;
oMsg.Cc = <span class="cpp-string">"him@HisServer.Net"</span>;
oMsg.Subject = <span class="cpp-string">"Test"</span>;
oMsg.Body = <span class="cpp-string">"This is only a test"</span>;
oMsg.Attachments.Add(<span class="cs-keyword">new</span> MailAttachment(<span class="cpp-string">"c:\temp\Test.txt"</span>));
SmtpMail.Send(oMsg);</pre>
		<p>If you copy/paste/modify the above code, all your programs can have e-mail capability and there is no need for any web service as far as e-mail is concerned.</p>
		<p>However, there are some advantages in providing e-mail capability in a web service, such as:</p>
		<ul>
				<li>A web service can be accessed by all servers on the network. You need only to enable SMTP service on a single machine. In other words, you can have better control over e-mail. 
</li>
				<li>All of your non .NET programs can use the web service to send e-mail as well. So you don't have to write e-mail related code for your C/C++/VB/Java programs (but you do need to know how to call a web service from these programs). 
</li>
				<li>You are not limited to the Windows platform, programs running on other platforms can use the e-mail service. 
</li>
				<li>Programs can access the e-mail service across firewalls without additional firewall configuration (assuming HTTP traffic is allowed). Of course, you need to have authentication in this case so that the service won't be abused. </li>
		</ul>
		<p>My simple e-mail web service has only one method, <code>SendMail</code>, which can be used to send multiple e-mails with a single call. Please note that you cannot use this web service to retrieve e-mail messages.</p>
		<h2>The web service for e-mail</h2>
		<p>To send an e-mail, you need to specify the destination address, the sender address, the list of addresses to copy the e-mail message, the subject line, the message body, and the list of attachment files, etc. Some of the information is not required. Things can get more complicated if you want to send multiple e-mails at once. </p>
		<p>Instead of having multiple web methods that take various numbers of parameters, I decided to implement a single method <code>SendMail</code> that takes only one string parameter and returns a boolean value to indicate success or failure. The string parameter represents an XML document, here is the format:</p>
		<pre lang="xml">&lt;EMailMessageList&gt;
    &lt;EMailMessage&gt;
        &lt;From&gt;&lt;/From&gt;
        &lt;To&gt;&lt;/To&gt;
        &lt;Cc&gt;&lt;/Cc&gt;
        &lt;Bcc&gt;&lt;/Bcc&gt;
        &lt;ReplyTo&gt;&lt;/ReplyTo&gt;
        &lt;Subject&gt;&lt;/Subject&gt;
        &lt;Body&gt;&lt;/Body&gt;
        &lt;Format&gt;&lt;/Format&gt;
        &lt;AttachmentList&gt;
            &lt;Attachment&gt;&lt;/Attachment&gt;
        &lt;/AttachmentList&gt;
    &lt;/EMailMessage&gt;
&lt;/EMailMessageList&gt;</pre>
		<p>The <code>From</code> element is the sender's e-mail address. The <code>To</code> element is the destination address, it could be multiple e-mail addresses separated by semi-colons. The same goes with the <code>Cc</code> and the <code>Bcc</code> elements. The <code>ReplyTo</code> element is the e-mail address to be used when replying to this message, it can be different from the sender's e-mail address. The <code>Subject</code> and <code>Body</code> elements are obvious.</p>
		<p>The <code>Format</code> element specifies the format of the message body, which can be either <code>HTML</code> or <code>TEXT</code> with <code>TEXT</code> as the default. The <code>Attachment</code> element holds the full path of a file on the local system. Please note that you cannot attach a file that is not on the server where the web service resides.</p>
		<p>As you can see, it is possible to pack multiple e-mail messages in the input string parameter and each message can have multiple attachment files. Here is the C# code that uses the web service to send e-mail (for simplicity, the above XML string is saved in the file <i>EMailTemplate.txt</i> and used as a template for XML document in the code).</p>
		<pre lang="cs">
				<span class="cs-comment">// prepare the xml document</span>
XmlDocument oDoc = <span class="cs-keyword">new</span> XmlDocument();
oDoc.Load(<span class="cpp-string">"EMailTemplate.txt"</span>);
oDoc.SelectSingleNode(<span class="cpp-string">"//From"</span>).InnerText = <span class="cpp-string">"me@MyServer.net"</span>;
oDoc.SelectSingleNode(<span class="cpp-string">"//To"</span>).InnerText = <span class="cpp-string">"you@MyServer.net"</span>;
oDoc.SelectSingleNode(<span class="cpp-string">"//Subject"</span>).InnerText = <span class="cpp-string">"Test"</span>;
oDoc.SelectSingleNode(<span class="cpp-string">"//Body"</span>).InnerText = <span class="cpp-string">"This is a test"</span>;
<span class="cs-comment">// send the e-mail message</span>
EMailService oWebSvcProxy = <span class="cs-keyword">new</span> EMailService();
oWebSvcProxy.Url = <span class="cpp-string">"http://MyServer.Net/TheEMailService/EMailService.asmx"</span>;
oWebSvcProxy.SendMail(oDoc.OuterXML);</pre>
		<p>Please note that <code>EMailService</code> in the above code is the proxy class generated when you add a web reference for the e-mail service to your project.</p>
		<p>Comparing to sending e-mails from individual programs, we may have to write a few more lines of code to use the e-mail web service. However, there are some additional advantages besides the ones listed above. For example, the web service writes all errors and debug information into a trace file. In case of failure, you can see exactly what is happening (what input the user is sending to the web service and what is causing the problem, etc.).</p>
		<p>The tracing capability in this web service is similar to my other components posted on Code Project, basically there will be one trace file for each day and the amount of information written to the trace file can be controlled by setting trace level from the <i>web.config</i> file. Also, old trace files will be deleted automatically to save disk space.</p>
		<p>To call the web service from C/C++/VB programs, you can use the Microsoft Toolkit 3.0. I also wrote a COM DLL <a href="http://codeproject.com/com/xysoapclient.asp">XYSoapClient</a> that uses the SOAP client object from the SOAP toolkit to simplify the code.</p>
		<h2>Security and installation</h2>
		<p>Now we have this web service ready, there is always a danger that the service will be exploited and misused. Any program on the same network can call this web service unless you restrict the access to it explicitly.</p>
		<p>Access to the web service can be restricted by configuring it from the <i>Windows Internet Service Manager</i>. It is possible to allow only access from the local machine and deny all other requests. You can grant or deny access for a group of IP addresses. You may also use Windows authentication or require a password. I will not go into details here.</p>
		<p>The included VB script file <i>InstallEMailService.vbs</i> can ease the pain of installation and configuration. First, you need to unzip the downloaded file into a folder on your machine. Then you run the VB script file, which will popup message boxes to ask you the following questions.</p>
		<ol>
				<li>What directory you want to install the web service? 
</li>
				<li>What is the website you want to install the web service? This is because some servers may have multiple websites and each of them is bound to a different IP address (or port number). You can use the "app friendly name" to distinguish websites. Just click OK if this does not apply to you. 
</li>
				<li>What is the port number of the website? Click OK if it is 80 (the default). 
</li>
				<li>Do you want to allow access from all IP addresses? If you choose "yes", then anyone can call the web service. Otherwise, only IP addresses from the same subgroup can access the web service. For example, if your website is bound to IP address <i>205.188.200.159</i>, then only IP addresses of the form <i>205.188.200.*</i> can call the web service you installed. </li>
		</ol>
		<p>The installation script will automatically create virtual directory for the web service on the website you specified, copy files, and set access permissions.</p>
		<p>Thank you for reading <a href="http://codeproject.com/script/Articles/list_articles.asp?userid=246">my articles</a> and using <a href="http://mysite.verizon.net/XiangYangL/" target="_blank">my tools</a>.</p>
		<h2>Recent Updates</h2>
		<p>02/09/2004: Updated the install script file. The previous version does not set permissions properly.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Xiangyang Liu</h2>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21470.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 22:53 <a href="http://www.cnitblog.com/MartinYao/articles/21470.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to Create a Simple Polling Service</title><link>http://www.cnitblog.com/MartinYao/articles/21467.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 14:29:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21467.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21467.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21467.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21467.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21467.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/useritems/PollingService/PollingService_src.zip">Download source files - 22 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/useritems/PollingService/PollingService_demo.zip">Download demo project - 173 Kb</a>
				</li>
		</ul>
		<h2>Introduction</h2>
		<p>This article describes step-by-step, with screen snapshots, how to create a polling service using VB.NET and Visual Studio 2005. It solves the problem of using timers in a service in a quick and simple manner.</p>
		<h2>Background</h2>
		<p>Over the years I have leaned on Code Project to help me get started on many projects that involved programming technologies I was not familiar with at the time. This last project required me to create a service for a set of code I had already written. So I headed over to Code Project to figure out if my friends at Code Project had a good article to do that. I was not disappointed. Mahmoud Nasr had written a highly rated and popular article <a href="http://www.codeproject.com/dotnet/simplewindowsservice.asp">Simple Windows Service Sample</a>.</p>
		<p>Everything went fine until I needed to add the timer part. There are a great many opinions about which timers to use in a service. And a Google search will yield many requests and complaints about all of them. I never did find a satisfactory article that explained how to do this seemingly simple thing. In addition, there were issues with multithreading and reentrancy when the timer fires before your task is ready to start over again.</p>
		<p>So I think I have solved this with a simple and quick solution. So if you want to run a task repeatedly in the background, without worrying about reentrancy, or multiple threading. If you just want to wrap your code in a Windows Service. Here is a very simple straight forward way to do it.</p>
		<h2>Using the code</h2>
		<p>The code I present, basically follows the same steps to create a service that MSDN recommends. And is very similar to Mahmoud's article. The main difference being that it includes code to do polling and shows you where you can drop your code in.</p>
		<p>Executing the demo will install a service called "Polling Service". It will write an event log entry to the Application Log on your computer every 30 seconds. The source code is everything you need to build the demo. It is also the final result of following the steps in this article.</p>
		<h3>1. Create New Service Project</h3>
		<p>Start by creating a new Visual Studio Project. Startup Visual Studio 2005.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/01_CreateNewProject.jpg" />
		<p>Select the "Windows Service" template and name the project.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/02_SelectProjectType.jpg" />
		<p>Drag and drop the "Event Log" component from the toolbox onto your new service designer window.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/03_AddEventLog.jpg" />
		<p>Set the "ServiceName" and "(Name)" property to the name of your new service.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/04_SetServiceProperties.jpg" />
		<p>Set the "Log" property to "Application". Set the "Source" property to the name of your new service.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/05_SetEventLogProperties.jpg" />
		<p>Enter the wrapper code for your service.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/06_EditServiceCode.jpg" />
		<p>You can cut and paste the following code into the code window for your service. Change variable names and message text as appropriate for your service.</p>
		<div class="precollapse" id="premain0" style="WIDTH: 100%">
				<img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" />
				<span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="0"> Collapse</span>
		</div>
		<pre lang="vbnet" id="pre0" style="MARGIN-TOP: 0px">Imports System.Threading

Public Class PollingService
    ' Keep track of worker thread.
    Private <code>m_oPollingThread</code> As New Thread( _
        New System.Threading.ThreadStart(AddressOf PollProcess))

    Protected Overrides Sub OnStart(ByVal args() As String)
        ' Add code here to start your service. This method should set things
        ' in motion so your service can do its work.
        EventLog1.WriteEntry("PollingService is starting.")

        ' Start the thread.
        m_oPollingThread.Start()
    End Sub

    Protected Overrides Sub OnStop()
        ' Add code here to perform any tear-down necessary to stop your service.
        EventLog1.WriteEntry("PollingService is stopping.")

        ' Stop the thread.
        m_oPollingThread.Abort()
    End Sub

    Private Sub PollProcess()
        ' Loops, until killed by OnStop.
        EventLog1.WriteEntry("PollingService service polling thread started.")
        Do
            ' Wait...
            System.Threading.Thread.Sleep(30000) ' 30000 = 30 seconds

            PollingPass()
        Loop
    End Sub

    Private Sub PollingPass()
        Try
            ' Do Stuff Here...
            EventLog1.WriteEntry("PollingService service polling pass executed.")
        Catch ex As System.Exception
            EventLog1.WriteEntry("PollingService encountered an error '" &amp; _
                ex.Message &amp; "'", EventLogEntryType.Error)
            EventLog1.WriteEntry("PollingService service Stack Trace: " &amp; _
                ex.StackTrace, EventLogEntryType.Error)
        End Try
    End Sub

End Class
</pre>
		<h3>Notes About the Code</h3>
		<p>When the service is started, in the "OnStart" method, a new thread is started. You must start a new thread because the service manager controls this process. If you simply called the polling process from the OnStart method, the service mananger would complain that the service does not respond. In other words, the OnStart method did not return control to the service manager.</p>
		<p>That new thread loops indefinitely and the only way to stop it is to issue the Abort command on the thread, which we do when we stop the service in the "OnStop" method.</p>
		<p>The thread process continually executes two commands. The command to wait for a specified period of time. And then the command to run your code.</p>
		<p>In my example, I have added exception checking to write any problems to the event log. The event log is the only way to debug a service during the startup time, as you can only interactively debug a service by attaching to the process "AFTER" it has already started.</p>
		<p>That's it for your service. Pretty simple, eh? Replace the comment "Do Stuff Here..." with the code you want to run on a periodic basis.</p>
		<p>You can change the period that the service waits between executing your code by changing the sleep time. It is specified in milliseconds, there's 1000 milliseconds in one second.</p>
		<h3>2. Add Project Installer</h3>
		<p>The service is done. However, to run it we can't just press the F5 button. We have to install the service. The following describes how to include in the project all the "stuff" needed to install the service on a computer.</p>
		<p>Right click on the designer pane for your service and select the option "Add Installer".</p>
		<img src="http://www.codeproject.com/useritems/PollingService/07_AddInstaller.jpg" />
		<p>Everything is setup for you. You can choose how your service starts by setting the "StartType" property. The default is "Manual". I've set it to "Automatic" in my example.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/08_ConfigureServiceInstaller.jpg" />
		<p>You can also specify which account the service runs under when it is started. Select the "ServiceProcessInstaller" component and set the "Account" property. The default is "User Account" and you will need to specify the username and password at installation time. I have set the example to use "LocalSystem" which does not require a username or password. Most Windows services run this way.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/09_ConfigureProcessInstaller.jpg" />
		<p>Ok, now when your application is run using the /install option, it will install itself as a service on your computer. And when /uninstall is specified it will remove the service from your computer.</p>
		<p>Now all we have to do is build the thing and resolve any build errors. To do that, select the properties of "My Project" in the Solution Explorer.</p>
		<p>Change the "Startup Object" to the name of your service.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/10_SetStartupObject.jpg" />
		<p>Now build your solution.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/11_BuildProject.jpg" />
		<h3>3. Add Setup Project</h3>
		<p>OK, you have a service, let's add a setup procedure to this project to make it easy for others to install and use your funky new service.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/12_AddSetupProject.jpg" />
		<p>Choose the "Setup Wizard" and choose a name. You can use the same name as your setup project.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/13_SetupProject.jpg" />
		<p>Follow the setup wizard prompts.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/14_SetupWizard1.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/15_SetupWizard2.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/16_SetupWizard3.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/17_SetupWizard4.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/18_SetupWizard5.jpg" />
		<p>Now you need to add a custom action so that after all the files are copied over the program is setup as a service.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/19_ViewCustomAction.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/20_AddCustomAction.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/21_SelectApplicationFolder.jpg" />
		<p>We are done setting up the setup project. Now build it.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/22_BuildSetupProject.jpg" />
		<h3>4. Run It</h3>
		<img src="http://www.codeproject.com/useritems/PollingService/23_RunSetup.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/24_RunSetupWizard1.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/25_RunSetupWizard2.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/26_RunSetupWizard3.jpg" />
		<br />
		<img src="http://www.codeproject.com/useritems/PollingService/27_RunSetupWizard4.jpg" />
		<p>Your service has been installed. Now go to the Service Manager and start it. The Service Manager can be found in "Administrative Tools".</p>
		<img src="http://www.codeproject.com/useritems/PollingService/28_FindService.jpg" />
		<p>Start the service. Even though we set it to start automatically. It does not start automatically after we install the service. It will start automatically when the computer starts up.</p>
		<img src="http://www.codeproject.com/useritems/PollingService/29_StartService.jpg" />
		<p>Now examine the event log to verify our service started and is running successfully. The Event Viewer can be found in "Administrative Tools".</p>
		<img src="http://www.codeproject.com/useritems/PollingService/30_EventViewer.jpg" />
		<p>
		</p>
		<img src="http://www.codeproject.com/useritems/PollingService/31_ServiceInstalledMessage.jpg" />
		<p>
		</p>
		<img src="http://www.codeproject.com/useritems/PollingService/32_ServiceStartingMessage.jpg" />
		<p>
		</p>
		<img src="http://www.codeproject.com/useritems/PollingService/33_ServiceThreadStartedMessage.jpg" />
		<p>
		</p>
		<img src="http://www.codeproject.com/useritems/PollingService/34_ServiceStartedMessage.jpg" />
		<p>
		</p>
		<img src="http://www.codeproject.com/useritems/PollingService/35_ServicePollingMessage.jpg" />
		<h3>5. Notes on uninstall</h3>
		<p>This example will continue to write event log messages to your application log every 30 seconds. Once you are convinced it works you will want to uninstall it.</p>
		<p>To do that simply run the same .msi file that you ran to install it in the first place. It will prompt to repair or remove. Choose the remove option.</p>
		<p>Another way to remove your service is through the Add/Remove Software Control panel option. If you have lost or overwritten your .msi file with a new version, this is the only way to remove your service.</p>
		<p>
				<strong>BEFORE UNINSTALLING A SERVICE</strong>: be sure to close the Services Viewer. If you do not and you have your service highlighted in the Service Viewer the uninstall will not be able to remove your service. This is because the Service Viewer "locks" the service that is highlighted. The uninstaller will instead "mark" the service for deletion. When this happens you cannot reinstall the service until the marked service is removed. There is no way to remove the marked service except by restarting the computer.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Mark James Newman</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=1065120">here</a> to view Mark James Newman's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21467.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 22:29 <a href="http://www.cnitblog.com/MartinYao/articles/21467.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Remoting Management Console</title><link>http://www.cnitblog.com/MartinYao/articles/21465.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 13:59:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21465.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21465.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21465.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21465.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21465.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/csharp/RemotingManagementConsole/RemotingManagementConsole_msi.zip">Download msi file - 415 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/csharp/RemotingManagementConsole/RemotingManagementConsole_src.zip">Download source - 1.1 Mb</a>
				</li>
		</ul>
		<h2>Contents</h2>
		<dl>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Introduction">Introduction</a>
				</dd>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Features">Features</a>
				</dd>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Concept">Concept</a>
				</dd>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Usage">Usage</a>
				</dd>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Implementation">Implementation</a>
				</dd>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Test">Test</a>
				</dd>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Version">Version</a>
				</dd>
				<dd>
						<a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Conclusion">Conclusion</a>
				</dd>
		</dl>
		<h2>
				<a name="Introduction">Introduction</a>
		</h2>
		<p>The 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.</p>
		<p>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.</p>
		<h2>
				<a name="Features">Features</a>
		</h2>
		<p>
				<img height="344" alt="Remoting Management Console" src="http://www.codeproject.com/csharp/RemotingManagementConsole/RMC.jpg" width="620" />
		</p>
		<p>Basically the RMC snap-in control is divided into the following activities:</p>
		<h5>Host Process</h5>
		<ul type="square">
				<li>Scanning an installed host processes 
</li>
				<li>Creating and installing a new host process 
</li>
				<li>Creating a host process configuration file 
</li>
				<li>Controlling the host process such as start, stop and restart 
</li>
				<li>Receiving the host process Event Logs </li>
		</ul>
		<h5>Config File</h5>
		<ul type="square" initcollapsed="" dynamicoutline="">
				<li>Creating or modifying the remoting section 
</li>
				<li>Creating or modifying appSettings 
</li>
				<li>Creating or modifying configSections 
</li>
				<li>Declaration of the configSections 
</li>
				<li>Declaration of the Channels 
</li>
				<li>Declaration of the ChannelSinkProviders </li>
		</ul>
		<p>Inside of the remoting section the following sections are managed:</p>
		<ul type="square">
				<li>Lifetime 
</li>
				<li>Service 
</li>
				<li>Channels 
</li>
				<li>ClientProviders 
</li>
				<li>ServerProviders </li>
		</ul>
		<h5>Drag&amp;Drop Feature</h5>
		<p>There are few places where the drag&amp;drop can be used to enter an existing assembly:</p>
		<ul type="square">
				<li>
						<b>HostProcesses</b> result view panel to drop the host process 
</li>
				<li>
						<b>RemoteObjects</b> result view panel to drop the assembly of the remote object </li>
		</ul>
		<p>Additionally, the drag&amp;drop can be used within the <b>configSection</b> to move or copy nodes in the root.</p>
		<h2>
				<a name="Concept">Concept</a>
		</h2>
		<p>The concept of the RMC is based on the following:</p>
		<ul>
				<li>Remoting host process is a windows service 
</li>
				<li>Remoting configuration is driven by the config file 
</li>
				<li>Microsoft Management Console (MMC) to host the RMC snap-in </li>
		</ul>
		<p>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:</p>
		<pre lang="cs">
				<span class="cs-keyword">protected</span>
				<span class="cs-keyword">override</span>
				<span class="cs-keyword">void</span> OnStart(<span class="cs-keyword">string</span>[] args)
{
   <span class="cs-keyword">if</span>(args.Length == <span class="cs-literal">1</span>) Thread.Sleep(Convert.ToInt32(args[<span class="cs-literal">0</span>]));
   RemotingConfiguration.Configure(
                 AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
}
<span class="cs-keyword">protected</span><span class="cs-keyword">override</span><span class="cs-keyword">void</span> OnStop()
{
   <span class="cs-keyword">foreach</span>(IChannel objChannel <span class="cs-keyword">in</span> ChannelServices.RegisteredChannels)
      ChannelServices.UnregisterChannel(objChannel);
}
</pre>
		<p>When the windows service receives a Start command, the <code>OnStart</code> action is invoked to perform a remoting configuration from the config file. On the other side, the <code>OnStop</code> action will clean-up all listeners hosted by this service included their worker threads.</p>
		<p>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 <a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Implementation">Implementation</a>)</p>
		<p>Once the host process has been created, the RMC will control its state such as start, stop and restart using the MMC environment.</p>
		<p>In the case of the existing non-installed host process, the RMC has a drag&amp;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.</p>
		<p>The initiate config file image is generated from the template <code>HostProcessTemplate.exe.config</code> file located in the %windir%/system32 folder. It's very easy to replace it for another one with custom pre-built common configuration. The RMC has many features for administrating this config file using the snip-in layout.</p>
		<p>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 <a href="http://www.ironringsoftware.com/">http://www.ironringsoftware.com</a> 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:</p>
		<p>
				<img height="176" alt="Design pattern between the snap-in node and users form" src="http://www.codeproject.com/csharp/RemotingManagementConsole/image002.gif" width="536" />
		</p>
		<p>Based on the menu selection, for instance: New -&gt; 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.</p>
		<h2>
				<a name="Usage">Usage</a>
		</h2>
		<p>The RMC requires to be installed by the <code>RemotingManagementConsole.msi</code> file. The windows installer will perform all necessary tasks such as RMC snap-in, MMCLib.dll and MMCFormsShim.dll registration included creating its desktop folder. Opening this folder and clicking on the <code>RemotingManagementConsole.msc </code>icon, the Remoting Management Console will show up with the snap-in of all the installed host processes on your machine. The screen snippet has been shown in the previously <a href="http://www.codeproject.com/csharp/remotingmanagementconsole.asp#Features">chapter</a>.</p>
		<p>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.</p>
		<p>What we need to do?</p>
		<ul type="square">
				<li>create a windows host process 
</li>
				<li>publish a remoting object in the remoting service tag 
</li>
				<li>create a channel to listen an incoming remoting messages (IMessages) </li>
		</ul>
		<p>Additionally, based on the application requirements:</p>
		<ul type="square">
				<li>adding server Sinks (formatter, provider) 
</li>
				<li>creating an appSettings for configuration purposes (key/value pairs) 
</li>
				<li>creating specific config sections </li>
		</ul>
		<p>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.</p>
		<p>Now, if we know what the remoting configuration needs, let handle this task do it by RMC. Here are its steps:</p>
		<h5>Step A. Create a new host process</h5>
		<ol>
				<li>Select the <b>Remoting Host Processes</b> node in the snap-in area 
</li>
				<li>Right-click on the node 
</li>
				<li>Select <b>New</b> and click <b>Process</b></li>
				<li>The following Form dialog will show-up 
<p><img height="382" alt="Remoting Management Console" src="http://www.codeproject.com/csharp/RemotingManagementConsole/newprocess.jpg" width="544" /></p></li>
				<li>Change the Name properties, for instance: HostProcess_Sample 
</li>
				<li>Press button <b>CREATE</b></li>
		</ol>
		<p>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:</p>
		<ul type="square">
				<li>lifetime 
</li>
				<li>RemoteObjects 
</li>
				<li>Channels 
</li>
				<li>appSettings 
</li>
				<li>configSections </li>
		</ul>
		<p>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.</p>
		<h5>Step B. Publishing Remote Object</h5>
		<ol>
				<li>Select the <b>RemoteObjects</b> node in the <b>HostProcess_Sample</b> root 
</li>
				<li>Right-click on the node 
</li>
				<li>Select <b>New</b> and click <b>Remote Object</b></li>
				<li>The following Form dialog will show-up 
<p><img height="260" alt="Remoting Management Console" src="http://www.codeproject.com/csharp/RemotingManagementConsole/newobject.jpg" width="460" /></p></li>
				<li>Populate all attributes on the Form 
</li>
				<li>Press button <b>APPLY</b></li>
		</ol>
		<p>The other, shortcut way (skipping the steps 1-3) is to use a Drag&amp;Drop feature when the remote object exist. The following steps explain that:</p>
		<ol>
				<li>Drag the assembly of the Remote Object, which you what to publish it from its folder 
</li>
				<li>Drop the assembly on the RemoteObjects result view (right panel) 
</li>
				<li>follow the above instruction steps 4 - 6 to finalize the entry process. </li>
		</ol>
		<h5>Step C. Configuring Channel</h5>
		<ol>
				<li>Select the <b>Channels</b> node in the <b>HostProcess_Sample</b> root 
</li>
				<li>Right-click on the node 
</li>
				<li>Select <b>New</b> and click <b>Remoting channel</b></li>
				<li>The following Form dialog will show-up 
<p><img height="320" alt="Remoting Management Console" src="http://www.codeproject.com/csharp/RemotingManagementConsole/newchannel.jpg" width="556" /></p></li>
				<li>Populate necessary attributes on the Form (see below notes) 
</li>
				<li>Press button <b>APPLY</b></li>
		</ol>
		<p>
				<b>Notes:</b>
		</p>
		<ul>
				<li>When your application using a standard channel, for instance: tcp, enter the port number (must be unique on the machine) and leave empty the other attributes. 
</li>
				<li>The channel can be configured using the already registered channels (machine.config and host process config) or typing its type in this element. See the checkBox and comboBox features. 
</li>
				<li>On the bottom of the Form is displayed the finally element for config file. 
</li>
				<li>The textBox <b>More</b> can be used to add any specific attributes using the name/value pair, for instance: priority="1" myProperty="myValue" </li>
		</ul>
		<p>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.</p>
		<p>Let's continue with our basic configuration. As the next step is to start the host process.</p>
		<h5>Step D. Start Host Process</h5>
		<ol>
				<li>Select the HostProcess_Sample node in the snap-in area 
</li>
				<li>Right-click on the node 
</li>
				<li>Select All Task and click Start 
</li>
				<li>The Form dialog will show the progress of the starting process 
</li>
				<li>Check the Host Process result view (right panel) for the Event Log Messages </li>
		</ol>
		<p>To Stop or Restart the host process follow the same steps as for Start one.</p>
		<h5>Refreshing snap-in</h5>
		<p>Each static node in the snap-in has a <b>Refresh</b> 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.</p>
		<h2>
				<a name="Implementation">Implementation</a>
		</h2>
		<p>The RMC Solution is divided into the following projects:</p>
		<ul type="square">
				<li>MMCLib this is a modified 3rd party software to handle unmanaged MMC code, <a href="http://www.ironringsoftware.com/">http://www.ironringsoftware.com</a></li>
				<li>ConfigFileLib to handle contents of the configuration file 
</li>
				<li>HostProcessLib to handle host process such as start, stop, create, install, etc. 
</li>
				<li>InstallClassRegAsm is a helper install class for custom action 
</li>
				<li>InstallClassRegsrv32 is a helper install class for custom action 
</li>
				<li>RemotingManagementConsole is a msi installation project 
</li>
				<li>
						<b>RemotingManagement</b> this is a snap-in implementation. There are 17 forms and user controls to handle the snap-in activities. </li>
		</ul>
		<p>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.</p>
		<p>The following code snippet show an implementation of the creating a host process on the fly:</p>
		<div class="precollapse" id="premain1" style="WIDTH: 100%">
				<img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" />
				<span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="1"> Collapse</span>
		</div>
		<pre lang="cs" id="pre1" style="MARGIN-TOP: 0px">
				<span class="cs-keyword">public</span>
				<span class="cs-keyword">string</span> Create(<span class="cs-keyword">string</span> strServiceName, <span class="cs-keyword">string</span> strServiceDesc, 
                     <span class="cs-keyword">bool</span> bStartAutomatic, <span class="cs-keyword">bool</span> bTrayIcon, 
                     <span class="cs-keyword">string</span> strAssemblyName) 
{
   <span class="cs-keyword">string</span> strAssemblyFile = <span class="cs-keyword">null</span>;

   <span class="cs-comment">//--- create service ---</span>
   ServiceSrcCode ssc = <span class="cs-keyword">new</span> ServiceSrcCode();
   <span class="cs-keyword">string</span> strServiceSrcCode = ssc.GetSrcCodeForService(strServiceName, 
                                                       strServiceDesc, 
                                                       bStartAutomatic, 
                                                       bTrayIcon);

   <span class="cs-keyword">string</span> strOutputAssembly = <span class="cs-keyword">string</span>.Format(@<span class="cpp-string">"{0}.exe"</span>, strAssemblyName);

   <span class="cs-comment">// assembly compilation.</span><span class="cs-keyword">string</span>[] strArrayReferences = {
      <span class="cpp-string">"System.dll"</span>, 
      <span class="cpp-string">"System.Data.dll"</span>, 
      <span class="cpp-string">"System.ServiceProcess.dll"</span>, 
      <span class="cpp-string">"System.Configuration.Install.dll"</span> }; 
   CompilerParameters cp = <span class="cs-keyword">new</span> CompilerParameters();
   cp.ReferencedAssemblies.AddRange(strArrayReferences);
   cp.GenerateExecutable = <span class="cs-keyword">true</span>;
   cp.GenerateInMemory = <span class="cs-keyword">false</span>; 
   cp.OutputAssembly = strOutputAssembly; 
   cp.WarningLevel = <span class="cs-literal">4</span>;
   cp.IncludeDebugInformation = <span class="cs-keyword">false</span>; 
   ICodeCompiler icc = <span class="cs-keyword">new</span> CSharpCodeProvider().CreateCompiler();
   CompilerResults cr = icc.CompileAssemblyFromSource(cp, strServiceSrcCode);

   <span class="cs-keyword">if</span>(cr.Errors.Count &gt; <span class="cs-literal">0</span>)
   {
      <span class="cs-keyword">foreach</span>(<span class="cs-keyword">string</span> s <span class="cs-keyword">in</span> cr.Output) 
      {
         Trace.WriteLine(s);
      }
      <span class="cs-keyword">throw</span><span class="cs-keyword">new</span> Exception(<span class="cs-keyword">string</span>.Format(<span class="cpp-string">"Build failed: {0} errors"</span>, 
                                         cr.Errors.Count)); 
   }
   <span class="cs-comment">//cr.TempFiles.KeepFiles = true;</span>
   strAssemblyFile = cp.OutputAssembly;

   <span class="cs-keyword">return</span> strAssemblyFile;
}
</pre>
		<p>The above function is using the <code>ServiceSrcCode </code>class to generate a properly source code of the requested host process from its template:</p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="2"> Collapse</span>
		</div>
		<pre id="pre2" style="MARGIN-TOP: 0px">
				<span class="cpp-preprocessor">#region windows service template text</span>
				<span class="cpp-keyword">const</span> string strSrcTmplService = <span class="cpp-string">"namespace RemotingHostService"</span> + 
<span class="cpp-string">"{ "</span> +
   <span class="cpp-string">"using System;"</span> + 
   <span class="cpp-string">"using System.Collections;"</span> +
   <span class="cpp-string">"using System.ComponentModel;"</span> +
   <span class="cpp-string">"using System.Data;"</span> +
   <span class="cpp-string">"using System.Diagnostics;"</span> +
   <span class="cpp-string">"using System.ServiceProcess;"</span> +
   <span class="cpp-string">"using System.Runtime.Remoting;"</span> +
   <span class="cpp-string">"using System.Runtime.Remoting.Channels;"</span> +
   <span class="cpp-string">"using System.Threading;"</span> +
   <span class="cpp-string">"public class Service : ServiceBase"</span> + 
   <span class="cpp-string">"{"</span> +
      <span class="cpp-string">"private Container components = null;"</span> +
      <span class="cpp-string">"public Service()"</span> +
      <span class="cpp-string">"{"</span>+
         <span class="cpp-string">"components = new Container();"</span> +
         <span class="cpp-string">"this.ServiceName = \"</span>SERVICE_NAME\<span class="cpp-string">";"</span> +
      <span class="cpp-string">"}"</span> +
      <span class="cpp-string">"static void Main()"</span> +
      <span class="cpp-string">"{"</span> + 
         <span class="cpp-string">"ServiceBase[] ServicesToRun;"</span> +
         <span class="cpp-string">"ServicesToRun = new ServiceBase[] { new Service() };"</span> +
         <span class="cpp-string">"Run(ServicesToRun);"</span> +
      <span class="cpp-string">"}"</span> +
      <span class="cpp-string">"protected override void Dispose(bool disposing)"</span> +
      <span class="cpp-string">"{"</span> +
         <span class="cpp-string">"if(disposing) { if(components != null) components.Dispose(); }"</span> +
         <span class="cpp-string">"base.Dispose(disposing);"</span>+
       <span class="cpp-string">"}"</span> +
       <span class="cpp-string">"protected override void OnStart(string[] args)"</span> +
       <span class="cpp-string">"{"</span> +
           <span class="cpp-string">"if(args.Length == 1) Thread.Sleep(Convert.ToInt32(args[0]));"</span> + 
           <span class="cpp-string">"RemotingConfiguration.Configure(AppDomain.CurrentDomain."</span> + 
                                     <span class="cpp-string">"SetupInformation.ConfigurationFile);"</span> +
       <span class="cpp-string">"}"</span> +
       <span class="cpp-string">"protected override void OnStop()"</span> +
       <span class="cpp-string">"{"</span> +
          <span class="cpp-string">"foreach(IChannel objChannel in ChannelServices.RegisteredChannels)"</span> + 
             <span class="cpp-string">"ChannelServices.UnregisterChannel(objChannel);"</span> +
       <span class="cpp-string">"}"</span> +
   <span class="cpp-string">"}\r\n"</span> +
   <span class="cpp-string">"[RunInstaller(true)]"</span> +
   <span class="cpp-string">"public class ProjectInstaller : System.Configuration.Install.Installer"</span> +
   <span class="cpp-string">"{"</span> +
      <span class="cpp-string">"private ServiceProcessInstaller serviceProcessInstaller1;"</span> +
      <span class="cpp-string">"private ServiceInstaller serviceInstaller1;"</span> +
      <span class="cpp-string">"public ProjectInstaller() {InitializeComponent();}"</span> +
      <span class="cpp-string">"private void InitializeComponent()"</span> +
      <span class="cpp-string">"{"</span> +
        <span class="cpp-string">"this.serviceProcessInstaller1 = new ServiceProcessInstaller();"</span> +
        <span class="cpp-string">"this.serviceInstaller1 = new ServiceInstaller();"</span>+
        <span class="cpp-string">"this.serviceProcessInstaller1.Account = ServiceAccount.LocalSystem;"</span> +
        <span class="cpp-string">"this.serviceInstaller1.ServiceName = \"</span>SERVICE_NAME\<span class="cpp-string">"; "</span> +
        <span class="cpp-string">"this.serviceInstaller1.StartType = SERVICE_START;"</span> +
        <span class="cpp-string">"this.Installers.AddRange(new System.Configuration.Install.Installer[]"</span> +
        <span class="cpp-string">"{this.serviceProcessInstaller1, this.serviceInstaller1});"</span> +
      <span class="cpp-string">"}"</span> + 
      <span class="cpp-string">"public override void Install(IDictionary stateServer)"</span> +
      <span class="cpp-string">"{"</span> +
        <span class="cpp-string">"Microsoft.Win32.RegistryKey system, currentControlSet, services, "</span> +
               <span class="cpp-string">"service; "</span> + 
        <span class="cpp-string">"base.Install(stateServer);"</span> +
        <span class="cpp-string">"system = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(\"</span>System\<span class="cpp-string">");"</span> +
        <span class="cpp-string">"currentControlSet = system.OpenSubKey(\"</span>CurrentControlSet\<span class="cpp-string">");"</span> +
        <span class="cpp-string">"services = currentControlSet.OpenSubKey(\"</span>Services\<span class="cpp-string">");"</span> +
        <span class="cpp-string">"service = services.OpenSubKey(this.serviceInstaller1.ServiceName, "</span> + 
                <span class="cpp-string">"true);"</span> +
        <span class="cpp-string">"service.SetValue(\"</span>Description\<span class="cpp-string">", \"</span>SERVICE_DESCRIPTION\<span class="cpp-string">");"</span> +
        <span class="cpp-string">"SERVICE_TRAYICON"</span> +
      <span class="cpp-string">"}"</span> +
   <span class="cpp-string">"}"</span> +
<span class="cpp-string">"}"</span>;
<span class="cpp-preprocessor">#endregion</span></pre>
		<p>The template requires to customize its entries such as <code>SERVICE_NAME, SERVICE_START, SERVICE_DESCRIPTION and SERVICE_TRAYICON </code>to finalize the host process code. This task is performed using the string.replace function. </p>
		<p>As I mentioned early, the <b>RemotingManagement</b> 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 <b>lifetime</b> node handle it:</p>
		<div class="precollapse" id="premain3" style="WIDTH: 100%">
				<img id="preimg3" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="3" />
				<span id="precollapse3" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="3"> Collapse</span>
		</div>
		<pre id="pre3" style="MARGIN-TOP: 0px">
				<span class="cpp-preprocessor">#region Lifetime</span>
				<span class="cpp-keyword">protected</span> BaseNode CreateLifetimeNodeTree(BaseNode parentNode, 
                                           string nodeName, <span class="cpp-keyword">bool</span> bRefresh)
{
   FormNode node = <span class="cpp-keyword">new</span> FormNode(<span class="cpp-keyword">this</span>);
   node.ControlType
                = Type.GetType(<span class="cpp-string">"RKiss.RemotingManagement.PropertiesControl"</span>);
   node.DisplayName = nodeName; 
   node.Tag = parentNode.Tag;
   node.OpenImageIndex = intLifetimeImage;
   node.ClosedImageIndex = intLifetimeImage;

   <span class="cpp-comment">//---add/insert this node</span><span class="cpp-keyword">if</span>(bRefresh)
      node.Insert(parentNode);
   <span class="cpp-keyword">else</span>
     parentNode.AddChild(node);

   node.OnSelectScopeEvent
                       += <span class="cpp-keyword">new</span> NodeNotificationHandler(OnSelectEvent_Lifetime);
   node.OnQueryPropertiesEvent
                   += <span class="cpp-keyword">new</span> NodeNotificationHandler(OnQueryProperties_Lifetime);

   <span class="cpp-comment">//---this is a post-process notification from the user form</span>
   node.OnUserEvent += <span class="cpp-keyword">new</span> NodeNotificationHandler(OnUserEvent_Lifetime);

   <span class="cpp-keyword">return</span> node;
}

<span class="cpp-keyword">private</span><span class="cpp-keyword">void</span> OnSelectEvent_Lifetime(object sender, NodeEventArgs args)
{
   BaseNode selNode = sender as BaseNode;

   <span class="cpp-comment">//---checkpoint</span>
   Trace.WriteLine(string.Format(<span class="cpp-string">"OnSelectEvent: sender={0}"</span>, 
                   selNode.DisplayName));

   <span class="cpp-comment">//---ask snap-in for the following buttons </span>
   IConsoleVerb icv; 
   selNode.Snapin.ResultViewConsole.QueryConsoleVerb(out icv);
   icv.SetVerbState(MMC_VERB.PROPERTIES, MMC_BUTTON_STATE.ENABLED, <span class="cpp-literal">1</span>);

   <span class="cpp-comment">//---status text</span>
   selNode.Snapin.ResultViewConsole.SetStatusText(<span class="cpp-string">"The lifetime properties"</span> + 
       <span class="cpp-string">"of the remote singleton and activated objects in the application."</span>);
}

<span class="cpp-keyword">protected</span><span class="cpp-keyword">void</span> OnQueryProperties_Lifetime(object sender, NodeEventArgs e)
{
   BaseNode selNode = sender as BaseNode;

   <span class="cpp-comment">//---checkpoint</span>
   Trace.WriteLine(string.Format(<span class="cpp-string">"OnQueryProperties: sender={0}"</span>, 
                                 selNode.DisplayName));

   <span class="cpp-comment">//---action</span>
   string strConfigFilePath = Convert.ToString(selNode.Tag) + <span class="cpp-string">".config"</span>;
   LifetimeForm formLT = <span class="cpp-keyword">new</span> LifetimeForm(selNode, selNode.DisplayName, 
                                           strConfigFilePath);
   formLT.Show();
}

<span class="cpp-keyword">private</span><span class="cpp-keyword">void</span> OnUserEvent_Lifetime(object sender, NodeEventArgs args)
{
   <span class="cpp-keyword">try</span> 
   {
      <span class="cpp-comment">//---inputs</span>
      BaseNode node = sender as BaseNode;
      NameValueArgs nva = args as NameValueArgs;
      string strCheckpoint = nva.Key;
      string strLifetimeName = Convert.ToString(nva.Val);

      <span class="cpp-comment">//---checkpoint</span>
      Trace.WriteLine(string.Format(<span class="cpp-string">"User Event: sender={0}, checkpoint={1},
         val={2}"</span>, node.DisplayName, strCheckpoint, strLifetimeName));

      <span class="cpp-comment">//---refresh and set scope </span>
      node.Snapin.ResultViewConsole.SelectScopeItem(node.HScopeItem); 
   }
   <span class="cpp-keyword">catch</span>(Exception ex) 
   {
      Trace.WriteLine(string.Format(<span class="cpp-string">"OnUserEvent_Lifetime - failed, "</span> + 
                      <span class="cpp-string">"error = {0}"</span>,  ex.Message));
   }
}
<span class="cpp-preprocessor">#endregion</span></pre>
		<p>The CreateLifetimeNodeTree method has responsibility to create node in the snap-in and subscribe the requested delegates such as</p>
		<ul type="square">
				<li>OnSelectEvent_Lifetime to handle activities when user click this node 
</li>
				<li>OnQueryProperties_Lifetime to handle a request to pop-up a properties form 
</li>
				<li>OnUserEvent_Lifetime to handle a post-process of the user form </li>
		</ul>
		<h2>
				<a name="Test">Test</a>
		</h2>
		<p>I created a separate solution to test the RMC:</p>
		<ul type="square">
				<li>InterfaceSample - interface contract 
</li>
				<li>RemotingObjectSample - remote object 
</li>
				<li>WindowsFormClient - consumer </li>
		</ul>
		<p>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:</p>
		<p>
				<img height="326" alt="Remoting Management Console" src="http://www.codeproject.com/csharp/RemotingManagementConsole/client.jpg" width="460" />
		</p>
		<p>Note that the <b>msi</b> 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.</p>
		<h2>
				<a name="Version">Version</a>
		</h2>
		<p>This 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:</p>
		<ul type="square">
				<li>incorporate the Remoting Probe 
</li>
				<li>help project 
</li>
				<li>drag&amp;drop pre-configured channels and sinks 
</li>
				<li>creating a library of the custom channels and sinks 
</li>
				<li>enterprise version 
</li>
				<li>improve users interface </li>
		</ul>
		<h2>
		</h2>
		<h2>
				<a name="Conclusion">Conclusion</a>
		</h2>
		<p>In 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.</p>
		<p>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.</p>
		<p>[1] <a href="http://msdn.microsoft.com/library/default.asp?url=/downloads/list/netdevgeneral.asp">Format for .NET Remoting Configuration Files</a></p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Roman Kiss</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=24570">here</a> to view Roman Kiss's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21465.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 21:59 <a href="http://www.cnitblog.com/MartinYao/articles/21465.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Remote SMTP Interface</title><link>http://www.cnitblog.com/MartinYao/articles/21464.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 13:55:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21464.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21464.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21464.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21464.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Download server project - 52 Kb														Download client project - 48 Kb														Download Extended MAPI wrapper project - 51 Kb								Table Of Contents						Introductio...&nbsp;&nbsp;<a href='http://www.cnitblog.com/MartinYao/articles/21464.html'>阅读全文</a><img src ="http://www.cnitblog.com/MartinYao/aggbug/21464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 21:55 <a href="http://www.cnitblog.com/MartinYao/articles/21464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Automate routine tasks with Windows Services</title><link>http://www.cnitblog.com/MartinYao/articles/21463.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 13:19:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21463.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21463.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21463.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21463.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21463.html</trackback:ping><description><![CDATA[
		<div>I know very few application developers who spend 100 percent of their time with actual programming work. We often have to devote a portion of our time to system maintenance, as well as the usual array of meetings. Find out how the .NET Framework's Windows Services (formerly known as NT services) allows you to program and schedule system tasks. </div>
		<p>
				<span class="subhead1">Welcome to the world of Windows Services</span>
				<br />In the past, you had to be proficient in C++ to develop a Windows Service. Thankfully, the .NET platform opens the process to all .NET developers--albeit C#, VB.NET, J#, and so forth. These services can be automatically started when the computer boots, can be paused and restarted, and do not show any user interface. </p>
		<p>You can create a service as a Microsoft Visual Studio .NET project, defining code within it that controls what commands are sent to the service and what actions should be taken when those commands are received. Commands sent to a service include starting, pausing, resuming, and stopping the service, and executing custom commands. </p>
		<p>After you create and build the application, you can install it by running the command-line utility InstallUtil.exe and passing the path to the service's executable file, or by using Visual Studio's deployment features. You can then use the Services Control Manager to start, stop, pause, resume, and configure your service. Let's take a closer look at Windows Service classes. </p>
		<p>
				<span class="subhead1">Windows Service classes</span>
				<br />You can create Windows Services with the help of the ServiceBase class in the System.ServiceProcess namespace. This class contains the following subset of events that interact with the Windows Service Control Manager (SCM): </p>
		<p>
		</p>
		<div>
				<ul>
						<li>
								<b>OnContinue:</b> Fires when a continue command is sent to the service by the SCM. It specifies actions to take when a service resumes normal functioning after being paused. 
</li>
						<li>
								<b>OnPause:</b> Fires when a pause command is sent to the service by the SCM. It specifies actions to take when a service pauses. 
</li>
						<li>
								<b>OnPowerEvent:</b> Fires when the computer's power status has changed. This applies to laptop computers when they go into suspended mode, which isn't the same as a system shutdown. 
</li>
						<li>
								<b>OnShutdown:</b> Fires when the system is shutting down. It specifies what should happen immediately prior to the system shutting down. 
</li>
						<li>
								<b>OnStart:</b> Fires when a start command is sent to the service by the SCM or when the operating system starts (for a service that starts automatically). It specifies actions to take when the service starts. An array of String objects is the event's lone parameter. 
</li>
						<li>
								<b>OnStop:</b> Fires when a stop command is sent to the service by the SCM. It specifies actions to take when a service stops running. </li>
				</ul>
		</div>
		<p>Only the OnStart event accepts any type of parameter. The main starting point of a Windows Service execution is the Main method, which accepts no parameters. Now you're ready to create your own service. </p>
		<p>
				<span class="subhead1">Creating a Windows Service</span>
				<br />At this point, you know which class and events to utilise, so let's create a sample service. Visual Studio .NET makes it simple to create a service using the code of choice: </p>
		<p>
		</p>
		<ol>
				<li>To create a new Windows Service, pick the Windows Service option from your Visual Studio Projects (choose appropriate language), give your service a name, and click OK. 
</li>
				<li>A Windows Service project is created with the default project name of WindowsService1 and a class name of Service1. <br />using System;<br />using System.Collections;<br />using System.ComponentModel;<br />using System.Data;<br />using System.Diagnostics;<br />using System.ServiceProcess; <br /><br />namespace WindowsService1<br />{<br /><br />public class Service1 : System.ServiceProcess.ServiceBase <br />{<br />          private EventLog log = null;<br />          private System.ComponentModel.Container components = null;<br />         public Service1() <br />        {<br />              InitializeComponent();<br />         }<br /><br />        static void Main() <br />         {<br />             System.ServiceProcess.ServiceBase[] ServicesToRun;<br />             ServicesToRun = new System.ServiceProcess.ServiceBase[] { new Service1()           };<br />             System.ServiceProcess.ServiceBase.Run(ServicesToRun);<br />        }<br /><br />private void InitializeComponent() {<br />        components = new System.ComponentModel.Container();<br />         this.ServiceName = "Service1";<br />}<br /><br />protected override void Dispose( bool disposing ) {<br />     if( disposing ) {<br />        if (components != null) {<br />           components.Dispose();<br />       }<br />    } <br />    base.Dispose( disposing );<br />    log.WriteEntry("Service dispose", EventLogEntryType.Information);<br />    log.Close();<br />}<br /><br />protected override void OnStart(string[] args) {<br />      log = new EventLog("Builder.com");<br />     log.WriteEntry("Service starting", EventLogEntryType.Information);<br />}<br /><br />protected override void OnStop() {<br />     log.WriteEntry("Service stopping.", EventLogEntryType.Information);<br />  } <br />}<br /> } </li>
		</ol>
		<p>Notice that the code execution entry point (Main method) creates a new instance of the ServiceBase class (an object array) and adds an instance of your class to it. The use of the array demonstrates the ability to run more than one process within the service (notice the thread marker within the VB.NET code). </p>
		<p>
				<span class="subhead1">Just the beginning</span>
				<br />You now have a basic Windows Service, but you still need to install and run it on a Windows computer. Also, the code will start, but what if you want or need it to run continuously based on a type of trigger? </p>
		<p>I'll cover these topics in part 2, in which I'll show you how to install, run and schedule the service. </p>
		<p>I covered the inner workings of a Windows Service developed with the .NET Framework. This included a somewhat rudimentary Windows Service application with both VB.NET and C#. This week, we'll install and execute the Windows Service. (Note: This story contains the complete code for part one and part two.) </p>
		<p>
				<em>
						<span class="subhead1">Windows service installation</span>
						<br />While console, Windows Form, and ASP.NET applications are easy to install by copying files, you must explicitly install Windows Service applications. Visual Studio .NET simplifies the installation process for Windows server applications. It displays a link in the Properties window of the Windows Service called Add Installer. When you select the link, the IDE adds the necessary installer classes to your project as part of a separate module. </em>
		</p>
		<p>
				<em>This includes two classes: System.ServiceProcess.ServiceProcessInstaller and System.ServiceProcess.ServiceInstaller. Several properties may be set using the Properties window of each class. The most important property is Account within the ServiceProcessInstaller class. It specifies the Windows account under which the service runs (security context). The following options are available: </em>
		</p>
		<p>
		</p>
		<ul>
				<li>
						<em>
								<b>LocalService:</b> Service has extensive local privileges and presents the computer's credentials to remote servers. </em>
				</li>
				<li>
						<em>
								<b>LocalSystem:</b> Service has limited local privileges and presents anonymous credentials to remote servers. </em>
				</li>
				<li>
						<em>
								<b>NetworkService:</b> Service has limited local privileges and presents the computer's credentials to remote servers. </em>
				</li>
				<li>
						<em>
								<b>User:</b> A local or network account is specified. You may specify the necessary username and password via properties, or you may type them during installation. The Service uses the security context of the specified user account. </em>
				</li>
		</ul>
		<p>
				<em>When an installer is added to the Service, the ProjectInstaller class is added. Defining properties via each class's Properties window results in an associated line of code in the corresponding ProjectInstaller class. The following code shows a sample installer for our C#-based Windows service with various properties defined, including the Account set to LocalService and ServiceName set to BuilderService. <br /></em>
		</p>
		<p>
				<em>using System;<br />using System.Collections;<br />using System.ComponentModel;<br />using System.Configuration.Install;<br />namespace WindowsService1 {<br />[RunInstaller(true)]<br />public class ProjectInstaller : System.Configuration.Install.Installer {<br />    public System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;<br />    private System.ServiceProcess.ServiceInstaller testInstaller;<br />    private System.ComponentModel.Container components = null;<br />    public ProjectInstaller() {<br />              InitializeComponent();<br />    }<br /><br />protected override void Dispose( bool disposing ) {<br />     if(disposing) {<br />       if(components != null) {<br />           components.Dispose();<br />     } <br />   }<br />    base.Dispose( disposing );<br />}<br />#region Component Designer generated code<br /><br />private void InitializeComponent() {<br />     this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();<br />     this.testInstaller = new System.ServiceProcess.ServiceInstaller();<br />    this.serviceProcessInstaller1.Account =  System.ServiceProcess.ServiceAccount.LocalService;<br />    this.serviceProcessInstaller1.Password = null;<br />   this.serviceProcessInstaller1.Username = null;<br />   this.testInstaller.DisplayName = "Test Installer";<br />   this.testInstaller.ServiceName = "BuilderService";<br />  this.testInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;<br />  this.Installers.AddRange(<br />               new System.Configuration.Install.Installer[] {<br />                    this.serviceProcessInstaller1,<br />                     this.testInstaller});<br />  }<br />#endregion<br />} } </em>
		</p>
		<p>
				<em>With the installers added to the project, you may now install the Windows Service and run it on the target computer. Another approach within our project is how the service is started. There are three options: </em>
		</p>
		<p>
		</p>
		<ul>
				<li>
						<em>
								<b>Manual:</b> The user starts the Service. </em>
				</li>
				<li>
						<em>
								<b>Disabled:</b> The Service isn't available for use. </em>
				</li>
				<li>
						<em>
								<b>Automatic:</b> The Service starts automatically when the host computer starts. </em>
				</li>
		</ul>
		<p>
				<em>In our example, I have the Service start automatically. With the Service properly compiled, we install it on the target computer via the Microsoft .NET Framework Installation utility (InstallUtil.exe) command-line tool. It allows you to easily install and uninstall our service. The compiled file (executable) or assembly is passed to it. Also, it provides the following command-line switches: </em>
		</p>
		<p>
		</p>
		<ul>
				<li>
						<em>/LogFile=[filename] - Contains the log indicating installation success/failure. The default is the AssemblyName.InstallLog. </em>
				</li>
				<li>
						<em>/LogToConsole=(true|false) - Signals whether console output is enabled. </em>
				</li>
				<li>
						<em>/ShowCallStack - Signals whether call stack is displayed if an exception is encountered. </em>
				</li>
				<li>
						<em>/u - Uninstalls the Service. </em>
				</li>
		</ul>
		<p>
				<em>With our sample Service, the following line takes care of the installation: </em>
		</p>
		<p>
				<span class="code">
						<em>InstallUtil WindowsService.exe </em>
				</span>
		</p>
		<p>
				<em>The line is run in the directory containing the assembly; otherwise, it would require the complete path to the assembly. After you install the new Service, it's located in the Services window of the Computer Management applet. </em>
		</p>
		<p>
				<em>
						<span class="subhead1">Triggering execution</span>
						<br />Once you properly install the Service, it's triggered based upon the Service setting. Unfortunately, it's only executed when loaded or run by a user. You may augment this by adding a timer to the code, so the agent executes on a scheduled basis. The next VB.NET code listing shows our Service with a Timer object added. The timer is started when the Service starts, and it stops when the Service stops. </em>
		</p>
		<p>
				<em>Imports System.Diagnostics<br />Imports System.ServiceProcess<br />Imports System.Timers<br />Public Class Service1<br />Inherits System.ServiceProcess.ServiceBase<br />Private log As EventLog<br />Private t As Timer<br />Public Sub New()<br />MyBase.New()<br />InitializeComponent()<br />End Sub<br />Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)<br />If disposing Then<br />If Not (components Is Nothing) Then<br />components.Dispose()<br />End If<br />End If<br />log.WriteEntry("Service dispose", EventLogEntryType.Information)<br />log.Close()<br />MyBase.Dispose(disposing)<br />End Sub<br />&lt;MTAThread()&gt; _<br />Shared Sub Main()<br />Dim ServicesToRun() As System.ServiceProcess.ServiceBase<br />ServicesToRun = New System.ServiceProcess.ServiceBase() {New Service1}<br />System.ServiceProcess.ServiceBase.Run(ServicesToRun)<br />End Sub<br />Private components As System.ComponentModel.IContainer<br />&lt;System.Diagnostics.DebuggerStepThrough()&gt; Private Sub InitializeComponent()<br />components = New System.ComponentModel.Container<br />Me.ServiceName = "Service1"<br />End Sub<br />Protected Overrides Sub OnStart(ByVal args() As String)<br />log = New EventLog("Builder.com")<br />log.WriteEntry("Service Starting", EventLogEntryType.Information)<br />t = New Timer(3600000)<br />AddHandler t.Elapsed, AddressOf TimerFired<br />With t<br />.AutoReset = True<br />.Enabled = True<br />.Start()<br />End With<br />End Sub<br />Protected Overrides Sub OnStop()<br />log.WriteEntry("Service Stopping", EventLogEntryType.Information)<br />t.Stop()<br />t.Dispose()<br />End Sub<br />Private Sub TimerFired(ByVal sender As Object, ByVal e As ElapsedEventArgs)<br />' Deal with firing<br />End Sub<br />End Class. <br /></em>
		</p>
		<p>
				<em>
						<span class="subhead1">Use the right tool </span>
						<br />A Windows Service is an excellent application choice when working with administrative tasks or automating routine tasks. Add it to your toolbox and use it when the situation arises. </em>
		</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21463.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 21:19 <a href="http://www.cnitblog.com/MartinYao/articles/21463.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How To Host .NET Remoting Objects In Windows Service Application</title><link>http://www.cnitblog.com/MartinYao/articles/21460.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 12:08:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21460.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21460.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21460.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21460.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21460.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td width="100%">
										<table width="100%">
												<tbody>
														<tr valign="top">
																<td class="SmallText" nowrap="">
																</td>
																<td nowrap="" align="right">
																		<a name="__top">
																		</a>
																		<table>
																				<tbody>
																						<tr>
																								<td class="smallText" align="right">28 votes for this article.</td>
																								<td>
																										<table cellspacing="0" cellpadding="0" border="2">
																												<tbody>
																														<tr>
																																<td>
																																		<img height="5" src="http://www.codeproject.com/script/images/red.gif" width="20" border="0" />
																																</td>
																																<td>
																																		<img height="5" src="http://www.codeproject.com/script/images/red.gif" width="20" border="0" />
																																</td>
																																<td>
																																		<img height="5" src="http://www.codeproject.com/script/images/red.gif" width="20" border="0" />
																																</td>
																																<td>
																																		<img height="5" src="http://www.codeproject.com/script/images/red.gif" width="11" border="0" />
																																		<img height="5" src="http://www.codeproject.com/script/images/white.gif" width="9" border="0" />
																																</td>
																																<td>
																																		<img height="5" src="http://www.codeproject.com/script/images/white.gif" width="20" border="0" />
																																</td>
																														</tr>
																												</tbody>
																										</table>
																								</td>
																						</tr>
																						<tr>
																								<td class="smallText" align="right" colspan="2">
																										<a title="Calculated as rating x Log10(# votes)" href="http://www.codeproject.com/script/articles/top_articles.asp?st=2">Popularity: 5.12</a>. Rating: <b>3.54</b> out of 5.</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
						<tr>
								<td class="ArticlePane">
										<span id="intelliTXT">
												<div id="contentdiv">
														<!-- Article Starts -->
														<ul class="download">
																<li>
																		<a href="http://www.codeproject.com/Purgatory/WinServiceHost/WinServiceHost_Demo.zip">Download demo project - 22 Kb</a>
																</li>
														</ul>
														<p>
																<img height="124" alt="Sample Image - WinServiceHost.jpg" src="http://www.codeproject.com/Purgatory/WinServiceHost/WinServiceHost.jpg" width="316" />
														</p>
														<p nd="1">For sometime I have been working on Remoting projects using .NET but every time I will always end up creating a console application to run as a remote server and host the remoting object in that. This definitely is not going to be case in real world applications. Most of the time you will require that your remote object is hosted in a server that is running all the time and does not require any user intervention like in Console application. You have couple of options for hosting e.g. WebServer, Managed Executables (e.g. Console App) or Component Services.</p>
														<p nd="2">This article is not about what is .NET remoting. You can read about that in .NET documentation or in my other article <a href="http://www.codeproject.com/dotnet/remotespy.asp">.NET Remoting Spied On</a>. This article is a combination of how to write a Windows Service Application formerly known as NT Service and how to host your remoting object in that service.</p>
														<h3>How To Create Windows Service Application</h3>
														<p nd="3">.NET online documentation has a complete section on “<i>Creating and Configuring Windows Service Applications</i>”. This chapter contains very good information on Introduction, Architecture, Security Issues, Install/Uninstall issues. So it would be a waste of time and space if I start discussing those. But there are certain things that are not very clear in that documentation. So I will try to explain those and show what all tools are available are in VS.Net to write Windows Service Applications.</p>
														<p nd="4">In VS.Net, if you click on New/Project, you will see that under Visual C# Project types, there is one named Windows Service. This is the type you want to pick if want to write service application. This does not mean that you can write Windows Service applications using C# only. You can do it in any language. This article is going to focus on using C# only. </p>
														<p>
																<img height="387" alt="" src="http://www.codeproject.com/Purgatory/WinServiceHost/WinService_IDE1.jpg" width="531" />
														</p>
														<p nd="5">.NET framework provides <code nd="6">System.ServiceProcess </code>namespace that contains all the classes you need to write and install service applications. Every Windows Service application class has to be derived from <code nd="7">ServiceProcess.SystemBase</code>. For the demo project with this article, the definition looks as follows.</p>
														<pre nd="8">
																<span class="cpp-keyword">public</span>
																<span class="cpp-keyword">class</span> PS_RemoteSrvrSrvc : System.ServiceProcess.ServiceBase </pre>
														<p nd="9">By default wizard provides you with bare minimum skeleton for service application. It provides <code nd="10">OnStart</code> and <code nd="11">OnStop</code> methods, which are essential components of a service application. <code nd="12">OnStart</code> method gets called when the service starts automatically when system starts or if it is manually started from Service Control Manager. <code nd="13">OnStop</code> gets called when the service stops. There are other methods that your service application can implement but that depends on the architecture and options you set for the application. E.g. you can implement <code nd="14">OnPause</code>, <code nd="15">OnContinue</code>, <code nd="16">OnShutdown</code> methods. But if your service application does not support pause and continue, then there is no need to implement these methods. You can set the characteristics of your application by setting the values for Properties supported by <code nd="17">ServiceBase</code> class and I would strongly recommend that you take a look at these properties in the documentation. Since debugging a Windows Service application is not a trivial job, setting of <code nd="18">EventLog</code> properties is very important. And I would recommend using EventLog extensively in initial phase of your project so that you can log your trace messages in the event log.</p>
														<h3>How To Install Windows Service Application</h3>
														<p nd="19">The section on “<i>Creating and Configuring Windows Service Applications</i>” in documentation says that you need to add installer components to install the service on your system so that it gets added to Service Control Manager (SCM). .NET framework documentation covers this topic in “Configuring and Deploying your .NET application”. Lets see what needs to be done to add installer component for our Windows Service application.</p>
														<p nd="20">.NET framework provided “<b>installutil</b>. In the folder where the executable for your service application resides, run this utility to install or uninstall the application. Following is the syntax for install and uninstall.</p>
														<pre nd="21">installutil myService.exe
installutil /u myService
</pre>
														<p nd="22">When <code nd="23">installutil</code> utility runs, it looks for a class with attribute <code nd="24">RunInstallerAttribute </code>set to <code><span class="cpp-keyword">true</span></code>. Therefore the first step in adding service installer to your application is to add a class with this attribute. In the demo project I have added <code nd="25">PS_ServiceInstall</code> derived from <code nd="26">System.Configuration.Install.Installer </code>to <code nd="27">PS_RemoteServer</code> namespace and have set the <code nd="28">RunInstallerAttribute</code> attribute.</p>
														<pre nd="29">[RunInstallerAttribute(<span class="cpp-keyword">true</span>)]
    <span class="cpp-keyword">public</span><span class="cpp-keyword">class</span> PS_ServiceInstall : Installer
{
.
.
}</pre>
														<p nd="30">In the constructor of this Installer class you can add the logic for installation<code nd="31">. System.ServiceProcess.ServiceInstaller</code> and <code nd="32">System.ServiceProcess.ServiceProcessInstaller</code> classes provide the functionality to add the Service and ServiceProcess installer component. Follow the following simple steps to add a bare minimum installer for your Windows Service application.</p>
														<ol style="MARGIN-TOP: 0in" type="1">
																<li nd="33">Add a class derived from <code nd="34">System.Configuration.Install.Installer</code> class. 
</li>
																<li nd="35">Set the <code nd="36">RunInstallerAttribute</code> attribue for this class to True. 
</li>
																<li nd="37">In the constructor, create new instance of <code nd="38">ServiceProcessInstaller</code> per service application and new instance of <code nd="39">ServiceInstaller</code> class for each service in the application. 
</li>
																<li nd="40">Add these installer objects to the <code nd="41">InstallerCollection</code> of your Installer class. </li>
														</ol>
														<p nd="43">
																<code nd="42">ServiceInstaller</code> and <code nd="44">ServiceProcessInstaller</code> has bunch of properties that can be used to set the attributes of the service. <code nd="45">ServiceInstaller</code> has the following properties.</p>
														<ul>
																<li nd="47">
																		<code nd="46">DisplayName</code>: Sets the friendly name that identifies the service to the user 
</li>
																<li nd="49">
																		<code nd="48">ServiceName</code>: Sets the name that system uses to identify the service. 
</li>
																<li nd="51">
																		<code nd="50">ServicesDependedOn</code>: Specify the services that must be running for this service to run. 
</li>
																<li nd="53">
																		<code nd="52">StartType</code>: Sets how and when the service will be started. The possible values for this property are Automatic, Manual or Disabled. Meaning that service will start automatically when system starts, the user will manually start the service from SCM or service will start in disabled mode. </li>
														</ul>
														<p nd="55">
																<code nd="54">ServiceProcessInstaller</code> has some very important properties that concern the security issues with your service application.</p>
														<p>
														</p>
														<ul>
																<li nd="57">
																		<code nd="56">RunUnderSystemAccount</code>: Specify if the service will be run under computer’s system account or under a certain user’s account. If this property is set to true then the service application will start under system account. This means it can start at system reboot without any user being logged on the system. But if this property is set to false, then during installation user will be presented with a login dialog to enter the user name and password that should be used for authentication. 
</li>
																<li nd="59">
																		<code nd="58">UserName</code>: Sets the user name under which service will run. It is only required if <code nd="60">RunUnderSystemAccount</code> property is set as false. 
</li>
																<li nd="62">
																		<code nd="61">Password</code>: Sets the password associated with the user account under which service will run. Like <code nd="63">UserName</code> property, this is only required if <code nd="64">RunUnderSystemAccount</code> property is set as false. 
</li>
																<li nd="66">
																		<code nd="65">HelpText</code>: Sets the help text displayed for service installation options. </li>
														</ul>
														<p nd="67">The methods associated with these two classes are called by Install utility during install and uninstall process. You don’t have to specifically call any of these methods in your Installer class.</p>
														<pre nd="68">
																<span class="cpp-keyword">public</span> PS_ServiceInstall()
{
        <span class="cpp-keyword">this</span>.m_ServiceInstaller = <span class="cpp-keyword">new</span> ServiceInstaller ();
        <span class="cpp-keyword">this</span>.m_ServiceInstaller.StartType = ServiceStart.Manual;
        <span class="cpp-keyword">this</span>.m_ServiceInstaller.ServiceName = <span class="cpp-string" nd="69">"RemoteSystemInfo"</span>;
        <span class="cpp-keyword">this</span>.m_ServiceInstaller.DisplayName = <span class="cpp-string" nd="70">"Remote System Info"</span>;
        Installers.Add (<span class="cpp-keyword">this</span>.m_ServiceInstaller);

        <span class="cpp-keyword">this</span>.m_ProcessInstaller = <span class="cpp-keyword">new</span> ServiceProcessInstaller ();
        <span class="cpp-keyword">this</span>.m_ProcessInstaller.RunUnderSystemAccount = <span class="cpp-keyword">true</span>;
        Installers.Add (<span class="cpp-keyword">this</span>.m_ProcessInstaller);
}</pre>
														<h3>How to host .NET Remoting Object</h3>
														<p nd="71">For hosting .NET Remoting objects in a server, you need to do following objects.</p>
														<p>
														</p>
														<ol>
																<li nd="72">Create and register the channel of transport, the object will use for marshalling, with ChannelServices. E.g. you can use TCP, HTTP or SMTP channels. 
</li>
																<li nd="73">Register your object with RemotingServices. </li>
														</ol>
														<p nd="74">Before you can access the remote object, the server should be running and the above-mentioned tasks should have been completed. Therefore the best place to do this is in OnStart method of your service.</p>
														<div class="precollapse" id="premain4" style="WIDTH: 100%">
																<img id="preimg4" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="4" />
																<span id="precollapse4" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="75" preid="4"> Collapse</span>
														</div>
														<pre id="pre4" style="MARGIN-TOP: 0px" nd="76">
																<span class="cpp-keyword">protected</span> override <span class="cpp-keyword">void</span> OnStart(string[] args)
{
EventLog.WriteEntry(<span class="cpp-string" nd="77">"RemoteSystemInfo: OnStart -- Entering"</span>);
        TCPChannel tcpChannel = <span class="cpp-keyword">new</span> TCPChannel (<span class="cpp-literal">8085</span>);
        EventLog.WriteEntry(<span class="cpp-string" nd="78">"RemoteSystemInfo: OnStart -- Created Channel"</span>);

        ChannelServices.RegisterChannel (tcpChannel);
        EventLog.WriteEntry(<span class="cpp-string" nd="79">"RemoteSystemInfo:"</span> + 
                 <span class="cpp-string" nd="80">" OnStart -- Registered Channel"</span>);

        RemotingServices.RegisterWellKnownType (<span class="cpp-string" nd="81">"PS_RemoteSrvr"</span>, 
             <span class="cpp-string" nd="82">"PS_RemoteSrvr.PS_SystemInfoSrvr"</span>,
             <span class="cpp-string" nd="83">"PS_SystemInfoSrvr"</span>, 
             WellKnownObjectMode.SingleCall);
        EventLog.WriteEntry(<span class="cpp-string" nd="84">"RemoteSystemInfo:"</span> + 
             <span class="cpp-string" nd="85">" OnStart -- RegisterWellKnownType Done"</span>);
        EventLog.WriteEntry (<span class="cpp-string" nd="86">"RemoteSystemInfo: OnStart -- Leaving"</span>);
}</pre>
														<p nd="87">The above code is all that is required to host a simple .NET remote object in Windows Service Application. You may require some extra steps or procedure depending on the nature of the remoting object and method you will use to configure it. But idea is that you should accomplish all the tasks on <code nd="88">OnStart</code> method of service.</p>
														<h3>How To Use Demo Project</h3>
														<p nd="89">The demo project associated with this article is not doing any thing fancy. I wrote a utility class that gets the OS information. I have added a method <code nd="90">GetOSInfo</code> to my remoting object <code nd="91">PS_SystemInfoSrvr</code>. This method creates a new instance of <code nd="92">PS_OSInfo</code> object, which is implemented in <code nd="93">PS_SystemInfoUtil</code> project, and returns to the client. And then client can call the methods and properties implemented by <code nd="94">PS_OSInfo</code> class. I have included the configuration file, PS_SystemInfo.cfg, that client can use to configure access to the remote server. The client application is implemented in <code nd="95">PS_RemoteSystemInfo</code> project. Following code shows how the client application configures and accesses the remoting object.</p>
														<pre nd="96">
																<span class="cpp-keyword">private</span>
																<span class="cpp-keyword">void</span> ConfigureRemoteServer ()
{
        <span class="cpp-keyword">try</span>
        {
               RemotingServices.ConfigureRemoting (<span class="cpp-string" nd="97">"PS_RemoteSystemInfo.cfg"</span>);
               <span class="cpp-keyword">this</span>.m_RemoteInfoSrvr = (PS_SystemInfoSrvr)
                   Activator.GetObject (typeof (PS_SystemInfoSrvr),
                   <span class="cpp-string" nd="98">"tcp://sultan:8085/PS_SystemInfoSrvr"</span>);

               PS_SystemOS sysOS = <span class="cpp-keyword">this</span>.m_RemoteInfoSrvr.GetOSInfo ();
               string strSystemDir = sysOS.SystemFolder;
               string strOS = sysOS.OS;
               string strVersion = sysOS.ServicePack;
        }
        <span class="cpp-keyword">catch</span> (RemotingException e)
        {
               Trace.WriteLine (e.Message);
        }
}</pre>
														<p nd="99">I have used TCP as my transport mechanism and “sultan” is my remote server where the service application hosting the remoting object is running. For you application you will need to provide the name or ip address of your server in configuration file or in <code nd="100">GetObject</code> call. If you want to run Client and Service app on same machine then simply use “localhost” as server name.</p>
														<p nd="101">To install the service application, copy <i>PS_RemoteSrvr.exe</i> and <i>PS_SystemInfoUtil.dll</i> to the folder where you want to install it. On the command line, run <code nd="102">installutil</code> utility in that folder. Then open the Service Control Manager. There you will see the service application. It will not be started yet, because I have set the start type as Manual. Right click on it and Start it. Now your remoting object has been hosted in the service application. As long as the service application is running, your client application can access it.</p>
														<p nd="103">On the client machine, you don’t need to run the <code nd="104">instalutil</code> utility. Simply run the <i>PS_RemoteSystemInfo.exe</i>. Put a break point in the <code nd="105">ConfigureRemoteServer</code> method and then you can see Remoting in action.</p>
														<h3>Do I Need VS.Net To write Service Application</h3>
														<p nd="106">You can definitely write Windows Service Application without VS.Net. You just need to create a class derived from <code nd="107">System.ServiceProcess.ServiceBase</code> class. Only advantage of using VS.Net is that it creates a skeleton for service application.</p>
														<h3>What Additional References Need To Be Added To Project</h3>
														<p nd="108">Remoting is implemented in <code nd="109">Service.Runtime.Remoting</code> so that needs to be added to your service as well as client application.</p>
														<!-- Article Ends -->
												</div>
										</span>
										<script src="/script/togglePre.js" type="text/javascript">
										</script>
										<h2>Naveen K Kohli</h2>
										<div style="OVERFLOW: hidden">
												<table border="0">
														<tbody>
																<tr valign="top">
																		<td class="smallText" nowrap="">
																				<br />
																		</td>
																		<td class="smallText">
																				<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=3204">here</a> to view Naveen K Kohli's online profile.</p>
																		</td>
																</tr>
														</tbody>
												</table>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21460.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 20:08 <a href="http://www.cnitblog.com/MartinYao/articles/21460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Host a Remote Object in a Windows Service</title><link>http://www.cnitblog.com/MartinYao/articles/21459.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 12:07:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21459.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21459.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21459.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21459.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21459.html</trackback:ping><description><![CDATA[
		<h3 class="dtH1">J.D. Meier, Alex Mackman, Michael Dunner, and Srinath Vasireddy<br />Microsoft Corporation</h3>
		<p>Published: November 2002</p>
		<p>Last Revised: January 2006</p>
		<p>
				<b>Applies to:</b>
		</p>
		<ul type="disc">
				<li>Remoting (Microsoft® .NET Framework 1.1) </li>
		</ul>
		<p>See the "<a onclick="javascript:Track('ctl00_LibFrame_ctl01|ctl00_LibFrame_ctl03',this);" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/securityguidanceindex.asp">patterns &amp; practices Security Guidance for Applications Index</a>" for links to additional security resources.</p>
		<p>See the <a onclick="javascript:Track('ctl00_LibFrame_ctl01|ctl00_LibFrame_ctl04',this);" href="http://msdn2.microsoft.com/en-us/library/aa302415.aspx">Landing Page</a> for a starting point and complete overview of <i>Building Secure ASP.NET Applications</i>.</p>
		<p>
				<b>Summary:</b> Objects called using the .NET Remoting infrastructure can be hosted by ASP.NET, custom executables or Windows services. This How To shows you how to host a remote object in a Windows service and call it from an ASP.NET Web application. (7 printed pages)</p>
		<h4 class="dtH1">Contents</h4>
		<p>
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_topic1">Notes</a>
				<br />
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_topic3">Summary of Steps</a>
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_step1">
						<br />Step 1. Create the Remote Object Class</a>
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_step2">
						<br />Step 2. Create a Microsoft Windows&amp;#174; Service Host Application</a>
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_step3">
						<br />Step 3. Create a Windows Account to Run the Service</a>
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_step4">
						<br />Step 4. Install the Windows Service</a>
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_step5">
						<br />Step 5. Create a Test Client Application</a>
				<br />
				<a href="http://msdn2.microsoft.com/en-us/library/aa302410.aspx#secnetht15_topic4">Additional Resources</a>
		</p>
		<p>This How To describes how to host a remote object in a Windows service and call it from an ASP.NET Web application.</p>
		<h2 class="dtH1">
				<a name="secnetht15_topic1">
						<!---->
				</a>Notes</h2>
		<ul type="disc">
				<li>Remote objects (that is, .NET objects accessed remotely using .NET Remoting technology) can be hosted in Windows services, custom executables, or ASP.NET. 
</li>
				<li>Clients communicate with remote objects hosted in custom executables or Windows services by using the TCP channel. 
</li>
				<li>Clients communicate with remote objects hosted in ASP.NET by using the HTTP channel. 
</li>
				<li>If security is the prime concern, host objects in ASP.NET and use the HTTP channel. This allows you to benefit from the underlying security features of ASP.NET and IIS. </li>
		</ul>
		<blockquote class="dtBlock">For information about how to host a remote object in ASP.NET (with IIS), see article <a onclick="javascript:Track('ctl00_LibFrame_ctl01|ctl00_LibFrame_ctl05',this);" href="http://support.microsoft.com/default.aspx?scid=kb;en-us;312107">Q312107, "HOW TO: Host a Remote Object in Microsoft Internet Information Services</a>."</blockquote>
		<ul type="disc">
				<li>If performance is the prime concern, host objects in a Windows service and use the TCP channel. This option provides no built-in security. </li>
		</ul>
		<h2 class="dtH1">
				<a name="secnetht15_topic3">
						<!---->
				</a>Summary of Steps</h2>
		<p>This How To includes the following steps. </p>
		<ul type="disc">
				<li>
						<b>Step 1. Create the Remote Object Class</b>
				</li>
				<li>
						<b>Step 2. Create a Microsoft Windows</b>®<b> Service Host Application</b></li>
				<li>
						<b>Step 3. Create a Windows Account to Run the Service</b>
				</li>
				<li>
						<b>Step 4. Install the Windows Service</b>
				</li>
				<li>
						<b>Step 5. Create a Test Client Application</b>
				</li>
		</ul>
		<h3 class="dtH1">
				<a name="secnetht15_step1">
						<!---->
				</a>Step1. Create the Remote Object Class</h3>
		<p>This procedure creates a simple remote object class. It provides a single method called <b>Add</b> that will add two numbers together and return the result.</p>
		<p>
				<b>To create the remote object class</b>
		</p>
		<ol type="1">
				<li>Start Microsoft Visual Studio® .NET and create a new Microsoft Visual C#® Class Library project called <b>RemoteObject</b>. 
</li>
				<li>Use Solution Explorer to rename class1.cs as Calculator.cs. 
</li>
				<li>In Calculator.cs, rename <b>Class1</b> as <b>Calculator</b> and rename the default constructor accordingly. 
</li>
				<li>Derive the <b>Calculator</b> class from <b>MarshalByRefObject</b> to make the class remotable. 
<div class="code" id="ctl00_LibFrame_ctl06_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl06');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl06" space="preserve">public class Calculator : MarshalByRefObject
</pre></div></li>
				<li>Add the following public method to the <b>Calculator</b> class. 
<div class="code" id="ctl00_LibFrame_ctl07_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl07');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl07" space="preserve">public int Add( int operand1, int operand2 )
{
  return operand1 + operand2;
}
</pre></div></li>
				<li>On the <b>Build</b> menu, click <b>Build</b><b>Solution</b>. </li>
		</ol>
		<h3 class="dtH1">
				<a name="secnetht15_step2">
						<!---->
				</a>Step 2. Create a Windows Service Host Application</h3>
		<p>This procedure creates a Windows service application, which will be used to host the remote object. When the service is started it will configure the TCP remoting channel to listen for client requests.</p>
		<blockquote class="dtBlock">
				<b class="le">Note</b>   This procedure uses an Installer class and the Installutil.exe command line utility to install the Windows service. To uninstall the service, run Installutil.exe with the <b>/u</b> switch. As an alternative, you could use a Setup and Deployment Project to help install and uninstall the Windows service.</blockquote>
		<p>
				<b>To create a Windows Service host application</b>
		</p>
		<ol type="1">
				<li>Add a new Visual C# Windows Service project called <b>RemotingHost</b> to the current solution. 
</li>
				<li>Use Solution Explorer to rename Service1.cs as RemotingHost.cs. 
</li>
				<li>In RemotingHost.cs, rename the <b>Service1</b> class as <b>HostService</b> and rename the default constructor accordingly. 
</li>
				<li>At the top of the file, add the following <b>using</b> statement beneath the existing <b>using</b> statements. 
<div class="code" id="ctl00_LibFrame_ctl08_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl08');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl08" space="preserve">using System.Runtime.Remoting;
</pre></div></li>
				<li>Locate the <b>Main</b> method and replace the existing line of code that initializes the <b>ServicesToRun</b> variable with the following. 
<div class="code" id="ctl00_LibFrame_ctl09_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl09');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl09" space="preserve">ServicesToRun = new System.ServiceProcess.ServiceBase[] { 
                                            new HostService() };
</pre></div></li>
				<li>Locate the <b>InitializeComponent</b> method and set the <b>ServiceName</b> property to <b>RemotingHost.</b><div class="code" id="ctl00_LibFrame_ctl10_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl10');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl10" space="preserve">this.ServiceName = "RemotingHost";
</pre></div></li>
				<li>Locate the <b>OnStart</b> method and add the following line of code to configure remoting. The fully qualified path to the configuration file will be passed as a start parameter to the service. 
<div class="code" id="ctl00_LibFrame_ctl11_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl11');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl11" space="preserve">RemotingConfiguration.Configure(args[0]);
</pre></div></li>
				<li>Add a new C# class file to the project and name it <b>HostServiceInstaller</b>. 
</li>
				<li>Add an assembly reference to the System.Configuration.Install.dll assembly. 
</li>
				<li>Add the following <b>using</b> statements to the top of <b>HostServiceInstaller</b> beneath the existing <b>using</b> statement.<b></b><div class="code" id="ctl00_LibFrame_ctl12_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl12');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl12" space="preserve">using System.ComponentModel;
using System.ServiceProcess;
using System.Configuration.Install;
</pre></div></li>
				<li>Derive the <b>HostServiceInstaller</b> class from the <b>Installer </b>class. 
<div class="code" id="ctl00_LibFrame_ctl13_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl13');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl13" space="preserve">public class HostServiceInstaller : Installer
</pre></div></li>
				<li>Add the <b>RunInstaller</b> attribute at the class level as follows. 
<div class="code" id="ctl00_LibFrame_ctl14_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl14');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl14" space="preserve"> [RunInstaller(true)]
public class HostServiceInstaller : Installer
</pre></div></li>
				<li>Add the following two private member variables to the <b>HostServiceInstaller</b> class. The objects will be used when installing the service. 
<div class="code" id="ctl00_LibFrame_ctl15_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl15');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl15" space="preserve">private ServiceInstaller HostInstaller;
private ServiceProcessInstaller HostProcessInstaller;
</pre></div></li>
				<li>Add the following code to the constructor of the <b>HostServiceInstaller</b> class. 
<div class="code" id="ctl00_LibFrame_ctl16_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl16');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl16" space="preserve">HostInstaller = new ServiceInstaller();
HostInstaller.StartType = 
  System.ServiceProcess.ServiceStartMode.Manual;
HostInstaller.ServiceName = "RemotingHost";
HostInstaller.DisplayName = "Calculator Host Service";
Installers.Add (HostInstaller); 
HostProcessInstaller = new ServiceProcessInstaller();
HostProcessInstaller.Account = ServiceAccount.User;
Installers.Add (HostProcessInstaller);
</pre></div></li>
				<li>Within Solution Explorer, right-click <b>RemotingHost</b>, point to <b>Add</b>, and then click <b>Add New Item</b>. 
</li>
				<li>In the <b>Templates</b> list, click <b>Text</b><b>File</b> and name the file <b>app.config</b>. 
<p>Configuration files with the name app.config are automatically copied by Visual Studio .NET as part of the build process to the output folder (for example, &lt;<i>projectdir</i>&gt;\bin\debug) and renamed as &lt;<i>applicationname</i>&gt;.config. </p></li>
				<li>Click <b>OK</b> to add the new configuration file. 
</li>
				<li>Add the following configuration elements to the new configuration file. 
<div class="code" id="ctl00_LibFrame_ctl17_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl17');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl17" space="preserve">&lt;configuration&gt;
&lt;system.runtime.remoting&gt;
  &lt;application name="RemoteHostService"&gt;
    &lt;service&gt;
      &lt;wellknown type="RemoteObject.Calculator, RemoteObject" 
                 objectUri="RemoteObject.Calculator" 
                   mode="Singleton" /&gt;
    &lt;/service&gt;
    &lt;channels&gt;
      &lt;channel ref="tcp" port="8085"&gt;
        &lt;serverProviders&gt;
          &lt;formatter ref="binary" /&gt;
        &lt;/serverProviders&gt;
      &lt;/channel&gt;
    &lt;/channels&gt;
  &lt;/application&gt;
&lt;/system.runtime.remoting&gt;
&lt;/configuration&gt;
</pre></div></li>
				<li>On the <b>Build</b> menu, click <b>Build Solution</b>. </li>
		</ol>
		<h3 class="dtH1">
				<a name="secnetht15_step3">
						<!---->
				</a>Step 3. Create a Windows Account to Run the Service</h3>
		<p>This procedure creates a Windows account used to run the Windows service.</p>
		<p>
				<b>To create a Windows account to run the service</b>
		</p>
		<ol type="1">
				<li>Create a new local user account called <b>RemotingAccount</b>. Enter a password and select the <b>Password never expires</b> check box. 
</li>
				<li>In the <b>Administrative</b><b>Tools</b> programs group, click <b>Local Security Policy</b>. 
</li>
				<li>Use the <b>Local</b><b>Security</b><b>Policy</b> tool to give the new account the <b>Log on as a service</b> privilege. </li>
		</ol>
		<h3 class="dtH1">
				<a name="secnetht15_step4">
						<!---->
				</a>Step 4. Install the Windows Service</h3>
		<p>This procedure installs the Windows service using the installutil.exe utility and then start the service.</p>
		<p>
				<b>To install the Windows service</b>
		</p>
		<ol type="1">
				<li>Open a command window and change directory to the Bin\Debug directory beneath the <b>RemotingHost</b> project folder. 
</li>
				<li>Run the installutil.exe utility to install the service. 
<div class="code" id="ctl00_LibFrame_ctl18_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl18');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl18" space="preserve">installutil.exe remotinghost.exe
</pre></div></li>
				<li>In the <b>Set</b><b>Service</b><b>Login</b> dialog box, enter the user name and password of the account created earlier in procedure 3 and click <b>OK</b>. 
<p>View the output from the installutil.exe utility and confirm that the service is installed correctly. </p></li>
				<li>Copy the RemoteObject.dll assembly into the <b>RemotingHost</b> project output directory (that is, RemotingHost\Bin\Debug). 
</li>
				<li>From the <b>Administrative</b><b>Tools</b> program group, start the <b>Services</b> MMC snap-in. 
</li>
				<li>In the <b>Services</b> list, right-click <b>Calculator Host Service</b>, and then click <b>Properties</b>. 
</li>
				<li>Enter the full path to the service's configuration file (remotinghost.exe.config) into the <b>Start parameters</b> field. 
<blockquote class="dtBlock"><b class="le">Note</b>   A quick way to do this is to select and copy the <b>Path to executable</b> field and paste it into the <b>Start</b><b>parameters</b> field. Then append the ".config" string.<b></b></blockquote></li>
				<li>Click <b>Start</b> to start the service. 
</li>
				<li>Confirm that the service status changes to <b>Started</b>. 
</li>
				<li>Click <b>OK</b> to close the <b>Properties</b> dialog box. </li>
		</ol>
		<h3 class="dtH1">
				<a name="secnetht15_step5">
						<!---->
				</a>Step 5. Create a Test Client Application</h3>
		<p>This procedure creates a test console application that is used to call the remote object within the Windows service.</p>
		<p>
				<b>To create a test client application</b>
		</p>
		<ol type="1">
				<li>Add a new Visual C# Console application called <b>RemotingClient</b> to the current solution. 
</li>
				<li>Within Solution Explorer, right-click <b>RemotingClient</b>, and then click <b>Set as StartUp Project</b>. 
</li>
				<li>Add an assembly reference to the System.Runtime.Remoting.dll assembly. 
</li>
				<li>Add a project reference to the <b>RemoteObject</b> project. 
</li>
				<li>Add the following <b>using</b> statements to the top of class1.cs beneath the existing <b>using</b> statements. 
<div class="code" id="ctl00_LibFrame_ctl19_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl19');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl19" space="preserve">using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using RemoteObject;
</pre></div></li>
				<li>Add the following test code to the <b>Main</b> method to call and invoke the <b>Calculator</b> object hosted by the Windows service. 
<div class="code" id="ctl00_LibFrame_ctl20_"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage"></div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl20');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl20" space="preserve">TcpChannel chan = new TcpChannel();
ChannelServices.RegisterChannel(chan);
Calculator calc = (Calculator)Activator.GetObject(
                         typeof(RemoteObject.Calculator),
                 "tcp://localhost:8085/RemoteObject.Calculator");
if (calc == null) 
  System.Console.WriteLine("Could not locate server");
else 
  Console.WriteLine("21 + 21 is : " + calc.Add(21,21) );
</pre></div></li>
				<li>On the <b>Build</b> menu, click <b>Build</b><b>Solution</b>. 
</li>
				<li>Run the client application and confirm that the correct result is displayed in the console output window. </li>
		</ol>
		<h2 class="dtH1">
				<a name="secnetht15_topic4">
						<!---->
				</a>Additional Resources</h2>
		<p>For information about how to host a remote object in ASP.NET (with IIS), see article <a onclick="javascript:Track('ctl00_LibFrame_ctl01|ctl00_LibFrame_ctl21',this);" href="http://support.microsoft.com/default.aspx?scid=kb;en-us;312107">Q312107, "HOW TO: Host a Remote Object in Microsoft Internet Information Services</a>".</p>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21459.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 20:07 <a href="http://www.cnitblog.com/MartinYao/articles/21459.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Microsoft Robotics Studio (MSRS) and Windows Communication Foundation (WCF) Work Together</title><link>http://www.cnitblog.com/MartinYao/articles/21458.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 10:07:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21458.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21458.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21458.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21458.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21458.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/WCF/MsrsWcfWorkTogthr/_MsrsWcfWorkTogether_demo.zip">Download demo project - 58.4 Kb </a>
				</li>
				<li>
						<a href="http://www.codeproject.com/WCF/MsrsWcfWorkTogthr/_MsrsWcfWorkTogether_src.zip">Download source - 73.7 Kb</a>
				</li>
		</ul>
		<h2>Introduction</h2>
		<p nd="1">
				<a href="http://www.microsoft.com/robotics" target="_blank">Microsoft Robotics Studio</a> (MSRS), the first prerelease of which was announced several months ago, is going to become a powerful tool not just for robots control but also for a wide range of service-oriented applications. Its Concurrency and Coordination Runtime (CCR) considerably simplifies development of simultaneous and interrelated tasks and increases applications' responsiveness. <font size="-1" nd="2">Decentralized System Services </font>(DSS) provides developers with light-weighted services-based tools to write and coordinate distributed applications. </p>
		<p nd="3">Although MSRS supports Web user interfaces and even allows to run WinForms from inside of the DSS service, communication of DSS services with a non-MSRS application may be desirable. Particularly, such communication is required when MSRS-based servers respond to remote non-MSRS clients. This article presents a sample of such a communication. The connection is made using .NET 3.0 <a href="http://msdn2.microsoft.com/en-us/netframework/aa663324.aspx" target="_blank">Windows Communication Foundation</a> (WCF, formerly known as "Indigo") services. Some basic familiarity with MSRS and WCF is essential. Apart from Microsoft documentation, several useful links are given in the References paragraph at the end of this article. </p>
		<h2>Code Sample </h2>
		<p>
				<img height="501" src="http://www.codeproject.com/WCF/MsrsWcfWorkTogthr/MsrsWcfWorkTogether.gif" width="590" border="0" />
		</p>
		<h2>Description</h2>
		<p nd="4">The code sample for the article consists of four projects (see the picture). Three of them, namely, <i>PerformanceMonitor</i>, <i>FileWatcher</i>, and <i>Coordinator</i>, are DSS of the same DSS host (node). The services have nothing to do with the actual robots control. Instead, in full compliance with their names<i>, PerformanceMonitor</i> periodically measures the <i>% Processor Time</i> performance counter, <i>FileWatcher</i> listens on changes in a given disk directory, and <i>Coordinator </i>coordinates activities of the formers and acts as gateway for this DSS node to the outside world. The shining outside world is represented by the forth project <i>TheForm </i>that is a WinForms application. <i>TheForm </i>and<i> Coordinator </i>communicate with WCF services. Each of the parties implements its own self-containing WCF service, and creates a proxy to connect the counterpart. <i>TheForm</i> implements a WCF service<i> Notification</i> with a <i>http://&lt;machine&gt;:19191/Notification</i> URI containing endpoints for the notification itself and the service metadata export. This service provides an interface (in the WCF parlance <code nd="5">[ServiceContract]</code>)<code nd="6"> TheForm.INotification</code> implemented by the <code nd="7">TheForm.Notification</code> class. The service is defined in <i>App.config</i> file and opened in the <code nd="8">StartNotificationService()</code> method of the class <code nd="9">TheForm.Helper</code>. The DSS <i>Coordinator</i> has its own self-containing WCF service <i>Coordinator </i><i>http://&lt;machine&gt;:19190/Coordinator</i> with <code nd="10">[ServiceContract]</code><code nd="11">Robotics.Coordinator.ICoordinator</code> (file <i>CoordinatorServiceType.cs</i>) opened in an imperative form in the <code nd="12">Robotics.Coordinator.</code><code nd="13">CoordinatorServiceHost</code> class. The client proxies for the above WCF services were generated using the <i>SvcUtil.exe</i> utility. Appropriate command files are included in the <i>Coordinator </i>(file <i>NotificationProxyGenerator.cmd</i>) and the <i>TheForm</i> (file <i>CoordinatorProxyGenerator.cmd</i>) Visual Studio projects. The commands in these files extract service metadata during service runtime.</p>
		<p nd="14">The WCF service hosted by the <i>Coordinator</i> DSS receives commands sent by <i>TheForm </i>UI console. For simplicity, it has only one<code nd="15"> [OperationContract]</code><code nd="16">ICoordinator.Command()</code> with two parameters: a command name string and a typeless (of <code lang="cs" nd="17">object </code>type) command data specific to each command.  There are only three such commands in the sample: "notify me" to make <i>Coordinator</i> DSS a client of the <i>TheForm</i>'s <i>Notification</i> WCF service, "set monitoring period" for <i>PerformanceMonitor </i>DSS, and "set watched directory" for <i>FileWatcher </i>DSS. The "notify me" command causes the DSS <i>Coordinator</i> to become a client of the <i>Notification</i> WCF service and call <code nd="18">[OperationContract]</code><code nd="19">TheForm.INotification.Notify()</code> when the notification event occurred. </p>
		<p nd="20">As it was stated above, the <i>Coordinator</i> DSS controls activities of other DSS services in this solution, gets notifications from them and, in its turn, notifies <i>TheForm</i> application. Thus, the <i>Coordinator</i> DSS sets a period for <i>PerformanceMonitor </i>and watches the directory for<i> FileWatcher</i>. This is achieved by including references to the <i>PerformanceMonitor </i>and <i>FileWatcher</i> proxies (<i>FileWatcher.Y2006.M09.Proxy.dll</i> and <i>PerformanceMonitor.Y2006.M09.Proxy.dll</i>, respectively), and posting the <code nd="21">Replace</code> verb to their main ports to replace the services states with new data (see classes implementing the interface <code nd="22">IDssCommand</code> in the file <i>Commands.cs</i>). <code nd="23">ReplaceHandler()</code> of <i>PerformanceMonitor </i>updates <code nd="24">PerformanceMonitorState.timeout</code> effectively changing monitoring period. Similarly <code nd="25">ReplaceHandler() </code>of <i>FileWatcher </i>updates <code nd="26">FileWatcherState.watcherRootDir</code> setting a new directory to listen on. Both <i>PerformanceMonitor </i>and <i>FileWatcher</i> DSS send notification to their subscription ports. The mechanism of communication between different DSS within one DSS node is explained (or rather "shown") in the <a href="http://msdn.microsoft.com/robotics/learn/service/" target="_blank">ServiceTutorials</a> shipped with Microsoft Robotics Studio CTP. See also comments in the sample's code.</p>
		<p nd="27">In this sample, I don't care about DSS security restrictions. So in order to allow DSS to use the performance counter, listen to the changes in the disk directories, and communicate with WCF means, security restrictions are lifted for now (by commenting out the <code nd="28">[PermissionSet]</code> attribute of the <code nd="29">DsspServiceBase</code>-derived classes in all DSS). For simplicity, only synchronous calls to WCF services are used in the sample. Since the sample is intended to show just the concept, the error handling is primitive. </p>
		<h2>Installation of Code Source</h2>
		<p nd="30">First make sure that .NET 3.0 (currently RC August 2006) and MSRS (currently CTP October 2006) are installed on your machine. Then unzip sources to a <i>_MsrsWcfWorkTogether</i> directory just under MSRS main directory, start  Microsoft Robotics Studio Command Prompt, go to <i>_MsrsWcfWorkTogether</i> directory and run <i>DevEnv.cmd</i> file. VS 2005 will be opened with <i>WCFConn.sln</i> loaded to it. Check the project properties. Output path for all projects should be <i>..\..\..\bin\services\</i>. For DSS services, the setting should be as follows:</p>
		<p nd="31">Start external program: &lt;Microsoft Robotics Studio main directory&gt;\bin\DssHost.exe</p>
		<p nd="32">Command line arguments: -port:50000 -tcpport:50001 -manifest:"_MsrsWcfWorkTogether/WCFConn/Coordinator/Coordinator.manifest.xml"</p>
		<p nd="33">Working directory: &lt;Microsoft Robotics Studio main directory&gt;</p>
		<p nd="34">Build solution in the following order. First, build <i>FileWatcher </i>and<i> PerformanceMonitor </i>projects. Then make sure that <i>Coordinator</i> has proper references to <i>FileWatcher.Y2006.M09.Proxy.dll</i> and <i>PerformanceMonitor.Y2006.M09.Proxy.dll </i>and build<i> Coordinator </i>project<i>. </i>Build <i>TheForm</i> project<i>. </i>Use files<i> RunTheForm.cmd </i>and<i> RunCoordinator.cmd </i>to run appropriate applications. For future MSRS releases, use the recommended converting procedure.</p>
		<h2>Installation of Demo</h2>
		<p nd="35">Unzip demo to directory <i>&lt;Microsoft Robotics Studio main directory&gt;\bin\services</i> and execute <i>RunCoordinatorFromSvc.cmd</i> and <i>RunTheFormFromSvc.cmd </i>files.</p>
		<p nd="36">
				<b>Note.</b> This demo project works for the version of MSRS and WCF specified above. </p>
		<h2>Conclusion</h2>
		<p nd="37">A sample of collaboration between MSRS service and a WinForms application by means of WCF services is presented. WCF provides the developer with a range of transport adapter channels (HTTP, TCP, Named Pipes, Peer to Peer and MSMQ) as a means of transporting messages between the sender and receiver. Thus, the ability to communicate using WCF considerably extends the capabilities of MSRS services, particularly to serve as a server engine. </p>
		<h2>References</h2>
		<ul>
				<li nd="38">Sara Morgan Rea.<i><a href="http://www.devx.com/dotnet/Article/32729" target="_blank">An Introduction to Programming Robots with Microsoft Robotics Studio</a></i>. 
</li>
				<li nd="39">Jeffrey Richter. <i><a href="http://msdn.microsoft.com/msdnmag/issues/06/09/ConcurrentAffairs/default.aspx" target="_blank">Concurrent Affairs: Concurrency and Coordination Runtime</a></i>. 
</li>
				<li nd="40">Ingo Rammer. <i><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/NETremoteWCF.asp" target="_blank">From .NET Remoting to the Windows Communication Foundation (WCF)</a></i>. 
</li>
				<li nd="41">Yasser Shohoud. <i><a href="http://blogs.msdn.com/yassers/archive/2005/10/12/480175.aspx" target="_blank">Meet the WCF Channel Model - Part 1</a></i>. </li>
		</ul>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Igor Ladnik</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<img src="http://www.codeproject.com/script/profile/images/%7B6A7E3D38-787B-4B48-BAED-2484B05CBC1E%7D.jpg" />
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=47188">here</a> to view Igor Ladnik's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21458.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 18:07 <a href="http://www.cnitblog.com/MartinYao/articles/21458.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Globalization Patterns in WCF</title><link>http://www.cnitblog.com/MartinYao/articles/21457.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 10:05:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21457.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21457.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21457.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21457.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/WCF/WSI18N/WSI18N_src.zip">Download source - 19.2 Kb</a>
				</li>
		</ul>
		<h2>Introduction</h2>
		<p nd="1">As business becomes more global every day, there is an emerging need to make applications and services multi-lingual and culturally aware. The .NET framework already provides comprehensive support for internationalization, but it is not always clear how to apply this support to the design of services. In this article, I will describe some of the available globalization patterns for web services, and how to successfully apply them using the extensibility points provided by WCF. In addition, I will align some code samples with the recently released WCF working draft for Web Services Internationalization (WS-I18N).</p>
		<h2>Globalization Patterns</h2>
		<p nd="2">There are at least three different globalization patterns that can be applied to a WCF service during the design or development phase. These patterns are mutually exclusive; they can not be used together, so it is essential to know their differences before starting work.</p>
		<ul>
				<li nd="3">Locale Neutral: In this pattern, most aspects of the services are not locale-affected. This is the simplest case, and additional considerations are not required. For example, a “Calculator” service that performs arithmetic operations. 
</li>
				<li nd="4">Service Determined: Here, the service always runs in a determined locale, which can be the host’s default locale, or a locale specifically configured for that service. For example, a web service that always returns messages in English. 
</li>
				<li nd="5">Client Influenced: In this case, the service may run in a locale provided by the client application. As the “WS-I18N” states, the service is “influenced” because it can either consider the locale in which it is running or not depending on how the service is being implemented. </li>
		</ul>
		<p nd="6">These patterns do not take into account situations in which different service implementations and data contracts are used to serve clients applications in a multilingual or cross-cultural setting.</p>
		<p nd="7">Let’s look at each of these patterns in detail.</p>
		<h2>Locale Neutral</h2>
		<p nd="8">This is the simplest case, there is no need to consider or attend to any globalization feature.</p>
		<pre lang="cs" nd="9">[ServiceContract(Namespace=<span class="cpp-string" nd="10">"http://Microsoft.ServiceModel.Samples"</span>)]
public interface ICalculator
{
    [OperationContract]
    double Add(double n1, double n2);

    [OperationContract]
    double Subtract(double n1, double n2);
}
    
<span class="cs-comment" nd="11">// Service class which implements the service contract.</span>
public class CalculatorService : ICalculator
{
    public double Add(double n1, double n2)
    {
        return n1 + n2;
    }

    public double Subtract(double n1, double n2)
    {
        return n1 - n2;
    }
}</pre>
		<p nd="12">The code above is quite straightforward. It only performs some simple arithmetic operations, but, as you can see, these operations are not influenced by any locale. The operation’s output will always be the same, whether the host’s locale is English or French.</p>
		<h2>Service Determined</h2>
		<p nd="13">Service determined scenarios are useful when all the clients for the service are well-known, and all of them agree on the use of a fixed locale, for instance, American English. This specific locale is usually the host’s default locale, or a locale specifically configured for the service. All the threads running in the host have a default <code nd="14">System.Globalization.CultureInfo</code> instance, where you can get the proper locale, and use it for different purposes, like number formatting, calendaring, or retrieving specific messages from a resource file.</p>
		<p nd="15">You can get that instance through the static properties <code nd="16">CultureInfo.CurrentCulture</code> and <code nd="17">Thread.CurrentThread.CurrentCulture</code>. The bad news is that there is a second instance of <code nd="18">CultureInfo</code> per thread, namely the UI display culture, that you can read using the static properties <code nd="19">CultureInfo.CurrentUICulture</code> and <code nd="20">Thread.CurrentThread.CurrentUICulture</code>.</p>
		<p nd="21">As you prepare to code a service, you will probably decide to use <code nd="22">CultureInfo.CurrentUICulture</code> because the display culture is typically used by the <code nd="23">ResourceManager</code> class for resource loading in WinForm applications.</p>
		<p nd="24">Let’s take a look to some code for a simple “HelloWorld” scenario:</p>
		<pre lang="cs" nd="26">
				<span class="cs-comment" nd="25">// Define a service contract.</span>
[ServiceContract(Namespace=<span class="cpp-string" nd="27">"http://Microsoft.ServiceModel.Samples"</span>)]
public interface IHelloWorld
{
    [OperationContract]
    string HelloWorld();
}

<span class="cs-comment" nd="28">// Service class which implements the service contract.</span>
public class HelloWorldService : IHelloWorld
{
    public string HelloWorld()
    {
        return Messages.HelloWorld;
    }
}</pre>
		<p nd="29">In the code above, I defined a simple contract and implementation for a service that only returns a “Hello World” message. The <code nd="30">Messages</code> class seen here is a typed resource class, one of the new features in .NET 2.0. If you create a resource file in your application, “<i>Messages.resx</i>” for instance, you can use the tool “<i>resgen.exe</i>” to automatically generate a typed class, with a property for each message in the resource file. In addition, this typed class automatically loads the resource file corresponding to the current thread culture, i.e., the one you can get using the static properties <code nd="31">CultureInfo.CurrentCulture</code> and <code nd="32">Thread.CurrentThread.CurrentCulture</code>. This is definitely good news, since you no longer have to worry about loading the right resource file and getting the messages from it. The “<i>Messages.resx</i>” file in the application looks like this:</p>
		<table cellspacing="0" cellpadding="3" border="1">
				<tbody>
						<tr>
								<td>
										<b>Name</b>
								</td>
								<td>
										<b>Value</b>
								</td>
								<td>
										<b>Comments</b>
								</td>
						</tr>
						<tr>
								<td nd="33">HelloWorld</td>
								<td nd="34">Hello World!!!</td>
								<td nd="35"> </td>
						</tr>
				</tbody>
		</table>
		<p nd="36">As a result, after executing the service, the client application will always receive the same “Hello World!!!” message, whether it is able to understand that message or not.</p>
		<h2>Client Influenced</h2>
		<p nd="37">This scenario is perhaps the most complex of the three, because the client needs to pass the expected locale in some way, and the service must decide whether it accepts that locale or not. The client and service also need to agree on the format and mechanism to exchange that locale. There are two ways to exchange the international preferences between the client and the service: an out-of-band mechanism through a message header, or an additional argument in the message body. In my opinion, the first option is usually preferable because it does not imply modifying each operation to include an additional argument. Since the Web Services Internationalization (WS-I18N) specification describes a mechanism to implement this scenario, I have chosen to align some code samples with the latest draft of this specification. In the following paragraphs, I will discuss the main aspects of this specification, and will then focus on a concrete implementation for WCF.</p>
		<h2>Web Services Internationalization (WS-I18N)</h2>
		<p nd="38">WS-I18N provides a mechanism to pass international preferences to a service invoked through SOAP, and to understand the format and language of any message returned. In other words, the main feature of this recently released specification is to influence the service with some cultural preferences specified by the client application. These preferences include:</p>
		<ul>
				<li nd="39">Locale or language preference 
</li>
				<li nd="40">Time zone 
</li>
				<li nd="41">Optional information </li>
		</ul>
		<h3>WS-I18N Elements</h3>
		<p nd="42">The main component of the WS-I18N specification is the “<code nd="43">International</code>” element. This element is a SOAP header that provides a mechanism for attaching international preferences and contextual information to a SOAP message targeted towards a specific receiver or SOAP actor. Thus, a SOAP message may have multiple “<code nd="44">International</code>” headers, each one for a different receiver (it is not possible to specify two different “<code nd="45">International</code>” headers for the same actor). The following sample shows a simple “<code nd="46">International</code>” header:</p>
		<pre lang="xml" nd="47">&lt;i18n:international S:mustUnderstand=”true” S:actor=”http://myorg.uri”&gt;
    &lt;locale&gt;en-US&lt;/locale&gt;
    &lt;tz&gt;GTM-0300&lt;/tz&gt;
    &lt;preferences&gt;
        ...
    &lt;/preferences&gt;
&lt;/i18n:international&gt;</pre>
		<p nd="48">As you can se in the above sample, some additional elements are specified in the “<code nd="49">International</code>” header. Let’s discuss all of them in detail:</p>
		<ul>
				<li nd="51">
						<code nd="50">Locale</code>: The “<code nd="52">locale</code>” element represents the requested user locale. The content of this element must be either a valid language tag (RFC3066bis) or one of the values “$default” or “$neutral” (same as Invariant Culture in .NET). For example, “en-US” for American English, or “es” for Spanish. 
</li>
				<li nd="54">
						<code nd="53">Tz</code>: The “<code nd="55">tz</code>” element represents the time zone of the client application or requester. 
</li>
				<li nd="57">
						<code nd="56">Preferences</code>: The “<code nd="58">preferences</code>” element represents a way to specify optional information and international preferences. </li>
		</ul>
		<h2>WS-I18N Implementation for WCF</h2>
		<p nd="59">So far, we have looked at three different globalization patterns, and have gone through a brief overview of the WS-I18N specification. We are now ready to start coding our implementation for WCF.</p>
		<h3>Creating the Contract Definition for the “International” Header</h3>
		<p nd="60">As the first step, we need to create a Data Contract for the “<code nd="61">International</code>” header. The data contract definition for a message header in WCF follows the same rules as that for normal messages. In the following code, we will see how to create the data contract for the desired header:</p>
		<pre lang="cs" nd="62">[DataContract(Name=”International”, 
  Namespace=”http:<span class="cs-comment" nd="63">//www.w3.org/2005/09/ws-i18n”)]  </span>
public class International
{
    private string locale;
    private string tz;
    private List&lt;Preferences&gt; preferences;

    public International()
    {
    }

    [DataMember(Name = “Locale”)] 
    public string Locale
    {
        get { return locale; }
        set { locale = value; }
    }

    [DataMember(Name = “TZ”)] 
    public string Tz
    {
        get { return tz; }
        set { tz = value; }
    }

    [DataMember(Name=”Preferences”)]
    public List&lt;Preferences&gt; Preferences
    {
        get { return preferences; }
        set { preferences = value; }
    }
}</pre>
		<p nd="64">Here, I have defined a class with a property for each element in the header. Since locale and time zone are simple types, we can represent them with <code lang="cs"><span class="cs-keyword" nd="65">string</span></code> properties. However, the “<code nd="66">Preferences</code>” element, by definition, can contain any custom information or XML data, so, we need to create a specific data type for that element.</p>
		<pre lang="cs" nd="67">public class Preferences : IXmlSerializable
{
    private XmlNode anyElement;

    public XmlNode AnyElement
    {
        get { return anyElement; }
        set { anyElement = value; }
    }    
        
    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        XmlDocument document = new XmlDocument();
        anyElement = document.ReadNode(reader);
    }

    public void WriteXml(XmlWriter writer)
    {
        anyElement.WriteTo(writer);
    }
}</pre>
		<p nd="68">The serializer for data contracts in WCF supports serialization with <code nd="69">IXmlSerializable</code> types, but not with an <code nd="70">XmlNode</code> type. Fortunately, we can overcome this obstacle by defining a class that exposes an <code nd="71">XmlNode</code> property and implements the <code nd="72">IXmlSerializable</code> interface, so we know how to write and read that XML from an XML stream.</p>
		<h3>Adding the “International” Header to the SOAP Messages</h3>
		<p nd="73">Once we have the header definition, as second step, we need to include that header in every message that goes from the client application to the server. In WCF, we have two ways to perform this task: we can explicitly define a message contract for the service and then, include the “<code nd="74">International</code>” header in that contract, or we can use some extensibility point to automatically add the header in every message at runtime. In my opinion, the first option is quite more rigid than the second one, since we have to modify or create a message contract for each operation in order to include the header. On other hand, the “<code nd="75">International</code>” header is explicitly exposed in the WSDL definition for the service. We will explore both alternatives in the following paragraphs, so you can choose the one you consider better.</p>
		<p nd="76">Let’s start working first on the explicit version, that is, including the “<code nd="77">International</code>” header in a service message contract.</p>
		<pre lang="cs" nd="78">[MessageContract()]
public class HelloWorldRequest
{
    [MessageHeader()] 
    public International International; 
}</pre>
		<p nd="79">To make things simpler, I only included the class representing the “<code nd="80">International</code>” header that we previously defined. This message contract does not specify any additional parameter in the message body or header. The message implementation for this service is also quite simple; just take a look at the code below:</p>
		<pre lang="cs" nd="82">
				<span class="cs-comment" nd="81">// Service class which implements the service contract.</span>
public class HelloWorldService : IHelloWorld
{
    public HelloWorldResponse HelloWorldWithMessages(HelloWorldRequest request)
    {
        if (request.International != null)
        {
            Messages.Culture = new CultureInfo(request.International.Locale);
        }

        HelloWorldWithMessagesResponse response = 
            new HelloWorldWithMessagesResponse();

        response.Result = Messages.HelloWorld;
        return response;
    }
}</pre>
		<p nd="83">First of all, we have to verify that the “<code nd="84">International</code>” header came with the request message, otherwise, we will only receive an ugly “<code nd="85">NullArgument</code>” exception at the moment we use the header. Once we know that the header is there, we can use some of its properties, such as the <code nd="86">Locale</code> or <code nd="87">Tz</code>, to influence the locale settings in the service. In this sample, I am only changing the locale for the typed resource class, so it will try to load the proper resource file and return a “HelloWorld” message. Note: This code can generate an exception if the resource class is not able to find the file for the specified locale.</p>
		<p nd="88">As I said before, using one of the WCF extensibility points is another way to include the header in the request messages to the service. Basically, WCF is divided into two layers, the lower-level channel layer that permits you to gain control over the messaging aspect of the applications, and the service layer that makes possible to build high-level applications without the need to deal with lower level aspects of the implementation. While we can develop extensions to plug our code in both layers, in this case, the service layer is usually the best option for simplicity sake. In the service layer, this can be done by means of Message Inspectors. As its name says, a Message Inspector is the mechanism provided by WCF to intercept and change SOAP messages. This is what we want to do in order to insert the “<code nd="89">International</code>” header in each message. The WCF application layer infrastructure provides two kinds of message inspectors: client message inspectors, which run on the client side, and service message inspectors, which do the same on the service side. For this reason, you will find two different interfaces, <code nd="90">IClientMessageInspector</code> for client inspectors, and <code nd="91">IDispatchMessageInspector</code> for service inspectors.</p>
		<pre lang="cs" nd="92">public interface IClientMessageInspector
{
    void AfterReceiveReply(ref Message reply, object correlationState);
    object BeforeSendRequest(ref Message request, IClientChannel channel);
}</pre>
		<p nd="93">The method names on this interface say everything: the <code nd="94">BeforeSendRequest</code> method is executed just before sending a request message to the service, and the <code nd="95">AfterReceiveReply</code>, just after receiving the response message from the service. Since WCF may run different instances of the same inspector before sending the message and after receiving it, in order to keep a state between both methods, the object correlation state is provided. The <code nd="96">IDispatchMessageInspector</code> interface is quite similar, and looks as follows:</p>
		<pre lang="cs" nd="97">public interface IDispatchMessageInspector
{
    object AfterReceiveRequest(ref Message request, IClientChannel channel, 
                               InstanceContext instanceContext);
    void BeforeSendReply(ref Message reply, object correlationState);
}</pre>
		<p nd="98">As part of our sample, we will have to implement both interfaces, a client inspector to add the custom header on the client side, and the service inspector to remove it on the service side.</p>
		<p nd="99">First, we need to have our custom message inspector, which we will name <code nd="100">InternationalizationMessageInspector</code>, implementing the <code nd="101">IClientMessageInspector</code> and <code nd="102">IDispatchMessageInspector</code> interfaces. We will provide a constructor to specify the locale and time zone that can be included in the header.</p>
		<pre lang="cs" nd="103">public class InternationalizationMessageInspector : 
             IClientMessageInspector, IDispatchMessageInspector
{
    public InternationalizationMessageInspector(string locale, string timeZone)
    {
        this.locale = locale;
        this.timeZone = timeZone;
    }</pre>
		<p nd="104">Now, let’s turn to the rest of the implementation.</p>
		<pre lang="cs" nd="105">public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
    International internationalHeader = new International();
            
    if(!String.IsNullOrEmpty(locale))
        internationalHeader.Locale = locale;

    if (!String.IsNullOrEmpty(timeZone))
        internationalHeader.Tz = timeZone;

    MessageHeader header = MessageHeader.CreateHeader(
                           WSI18N.ElementNames.International, 
                           WSI18N.NamespaceURI, internationalHeader);

    request.Headers.Add(header);
    return null;
}</pre>
		<p nd="106">The <code nd="107">BeforeSendRequest</code> implementation creates an instance of our “<code nd="108">International</code>” header class, and afterwards, it adds that instance to the <code nd="109">Headers</code> collection in the message received as a parameter in the method. On the other side, the message inspector should get the header from the message and process it.</p>
		<pre lang="cs" nd="110">public object AfterReceiveRequest(ref Message request, 
       IClientChannel channel, InstanceContext instanceContext)
{
    int index = request.Headers.FindHeader(
          WSI18N.ElementNames.International, WSI18N.NamespaceURI);

    request.Headers.UnderstoodHeaders.Add(request.Headers[index]);

    return null;
}</pre>
		<p nd="111">In this case, I only moved the header to the “<code nd="112">UnderstoodHeaders</code>” collection. This implicitly removes the header from the <code nd="113">Headers</code> collection, so if the header was marked as “<code nd="114">MustUnderstand</code>”, WCF does not throw an exception. There is no need to perform any additional process on the header. Finally, our service implementation must check whether the “<code nd="115">International</code>” header is in the “<code nd="116">UnderstoodHeaders</code>” collection or not, and execute some code after verifying that condition. The code I implemented to find the header in the service looks as follows:</p>
		<pre lang="cs" nd="117">public International GetHeaderFromIncomeMessage()
{
    MessageHeaders headers = 
      OperationContext.Current.IncomingMessageHeaders;

    foreach (MessageHeaderInfo uheader in headers.UnderstoodHeaders)
    {
          if (uheader.Name == “International” &amp;&amp; uheader.Namespace == 
                                  “http:<span class="cs-comment" nd="118">//www.w3.org/2005/09/ws-i18n”)</span>
        {
            International internationalHeader = 
              headers.GetHeader&lt;International&gt;(
              uheader.Name, uheader.Namespace);

            return internationalHeader;
        }
    }

    return null;
}</pre>
		<p nd="119">Then, I used the helper method to get the “<code nd="120">International</code>” header and set the resource manager with the locale preferences specified there.</p>
		<pre lang="cs" nd="121">[Microsoft.ServiceModel.Samples.InternationalizationAttribute()]
public string HelloWorld()
{
    International internationalHeader = 
       International.GetHeaderFromIncomeMessage();

    if (internationalHeader != null)
    {
        Messages.Culture = 
           new CultureInfo(internationalHeader.Locale);
    }
            
    return Messages.HelloWorld;
}</pre>
		<p nd="122">The service contract in this scenario is exactly the same as the one I used in the “Service Determined” pattern, no message contract or header definition is required. You probably noticed the existence of a “<code nd="123">InternationalizationAttribute</code>” definition just above the operation signature. That attribute is a <code nd="124">IOperationBehavior</code> implementation required to inject our message inspector at runtime in the message inspectors for the service. A <code nd="125">IOperationBehavior</code> is another extensibility point provided by WCF, it can be mainly used to customize the process of building a channel.</p>
		<div class="precollapse" id="premain14" style="WIDTH: 100%">
				<img id="preimg14" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="14" />
				<span id="precollapse14" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="126" preid="14"> Collapse</span>
		</div>
		<pre lang="cs" id="pre14" style="MARGIN-TOP: 0px" nd="127">public class InternationalizationAttribute : Attribute, IOperationBehavior
{
    private string locale;
    private string timeZone;

    public string Locale
    {
        get { return locale; }
        set { locale = value; }
    }

    public string TimeZone
    {
        get { return timeZone; }
        set { timeZone = value; }
    }

    public void ApplyClientBehavior(OperationDescription 
           operationDescription, ClientOperation clientOperation)
    {
        clientOperation.Parent.MessageInspectors.Add(new 
          InternationalizationMessageInspector(locale, timeZone)); 
    }

    public void ApplyDispatchBehavior(OperationDescription 
           operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Parent.MessageInspectors.Add(new 
                  InternationalizationMessageInspector()); 
    }
}</pre>
		<p nd="128">The “<code nd="129">ApplyClientBehavior</code>” and “<code nd="130">ApplyDispatchBehavior</code>” methods modify the client and service channels to include our MessageInspector implementation.</p>
		<h2>Conclusion</h2>
		<p nd="131">In this article, we saw three different patterns that can be applied in the design or development of a WCF service. In order to know which of these patterns is best for your service, you need to know whether the service is locale neutral or not, and the requirements for the client applications (the service consumers). We also discussed a concrete implementation of the WS-I18N specification using some of the extensibility points provided by WCF.</p>
		<h2>History</h2>
		<p nd="132">Uses the initial version for the WCF September RC release.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>About Pablo Cibraro</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">Pablo Cibraro is an independent consultant and expert on Microsoft Technologies.<br />He has over eight years of experience designing and developing software solutions for a broad range of corporate clients, principally in Argentina. He was recently awarded as MVP in the “Windows Server System – Connected System Developer” category.<br />Pablo can be reached via his weblog at <a href="http://weblogs.asp.net/cibrax">http://weblogs.asp.net/cibrax</a><br /><p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=120034">here</a> to view Pablo Cibraro's online profile.</p></td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 18:05 <a href="http://www.cnitblog.com/MartinYao/articles/21457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows Communication Foundation (WCF) Service Client</title><link>http://www.cnitblog.com/MartinYao/articles/21456.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 10:03:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21456.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21456.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21456.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21456.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21456.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/WCF/WCFServiceClient/WCFServiceSource.zip">Download WCF service source - 4.1 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/WCF/WCFServiceClient/WCFClientSource.zip">Download WCF client source - 18 Kb</a>
				</li>
		</ul>
		<p>
				<img height="267" src="http://www.codeproject.com/WCF/WCFServiceClient/article.gif" width="430" />
		</p>
		<h2>Introduction</h2>
		<p nd="1">WCF (Windows Communication Foundation) is a great framework for developing service oriented applications. This article explains the steps needed to use a WCF service from a client application.</p>
		<h2>Background</h2>
		<p nd="2">The best starter guide for WCF I could find was <a href="http://wcf.netfx3.com/content/BuildingHelloWorld.aspx" target="_blank">this</a>. It guides you step-by-step to create a WCF service and a client application. However, the steps needed to create the client application and call the methods exposed by the service did not sound so handy. The article says that before the client application can use the service, we should run <i>svcutil.exe</i> to generate the proxy classes. These proxy classes are then used to access the methods exposed by the service.</p>
		<p nd="3">There must be a better option. This article explains how to add a <i>Web Reference</i> to a WCF service in a client application.</p>
		<h2>Using the code</h2>
		<p nd="4">Create a WCF service as per the steps explained in the <a href="http://wcf.netfx3.com/content/BuildingHelloWorld.aspx" target="_blank">Hello World</a> sample. If you do not want to create the service from scratch, you can download the <i>WCF Service Source.zip</i> attached with this article.</p>
		<p nd="5">Once you have the service ready, compile and run it. You will see the console window open up. At this stage, the service is ready, and client applications can call methods exposed by the service.</p>
		<p nd="6">Most of you who have worked with .NET web services would certainly agree with me that <i>Web References</i> are the easiest way to invoke a web service. I was trying to do the same with the WCF service. However, I saw that the <i>Service Discovery Operation</i> failed to recognize the service because <b>Metadata Publishing</b> is not enabled.</p>
		<p nd="7">The following configuration settings show how to enable <b>Metadata Publishing</b> on a WCF service:</p>
		<pre lang="xml" nd="8">&lt;?xml version="1.0" encoding="utf-8" ?&gt; 
&lt;configuration&gt;
 &lt;system.serviceModel&gt; 
  &lt;services&gt;
   &lt;service name="HelloService.HelloService" 
       behaviorConfiguration="HelloService.HelloService" /&gt;
  &lt;/services&gt;
  &lt;behaviors&gt;
   &lt;serviceBehaviors&gt;
    &lt;behavior name="HelloService.HelloService" &gt;
     &lt;serviceMetadata httpGetEnabled="true" /&gt;
    &lt;/behavior&gt;
   &lt;/serviceBehaviors&gt;
  &lt;/behaviors&gt;
 &lt;/system.serviceModel&gt;
&lt;/configuration&gt;</pre>
		<p nd="9">The above configuration settings will enable <b>Metadata Publishing</b> on the service.</p>
		<p nd="10">Now, let us create a client application which accesses the <i>HelloService</i>. Either create an application from scratch, or you could use the <i>WCF Client Source.zip</i> attached with this article.</p>
		<p nd="11">Follow the steps below to create a client application:</p>
		<ol>
				<li nd="12">Create a VB.NET Console Application (or C# if you prefer it). 
</li>
				<li nd="13">Add a <i>Web Reference</i> to the service with the following URL: <i>http://localhost/hello</i> (or the correct URL if you have modified the default service location). </li>
		</ol>
		<p nd="14">The following code shows how to access the WCF service exposed by the server:</p>
		<pre lang="vbnet" nd="15">Dim o As New HelloService.HelloService
System.Console.WriteLine(<span class="vb-string" nd="16">"Response from WCF Service: {0}"</span>, o.sayHi())</pre>
		<h2>Points of Interest</h2>
		<p nd="17">The example above shows how easy it is to access a WCF service. The client application can simply add a <i>Web Reference</i> to the service, and can use it just like any one would do with an ASP.NET web service.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>jacob.sebastian</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=3281667">here</a> to view jacob.sebastian's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21456.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 18:03 <a href="http://www.cnitblog.com/MartinYao/articles/21456.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WCF (Windows Communication Foundation) Example</title><link>http://www.cnitblog.com/MartinYao/articles/21455.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 10:02:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21455.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21455.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21455.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21455.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/WCF/wcf_bohansen/WCFExample_demo.zip">Download demo project - 12.9 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/WCF/wcf_bohansen/WCFExample_src.zip">Download source - 48.6 Kb</a>
				</li>
		</ul>
		<p>
				<img height="312" alt="Sample Image" src="http://www.codeproject.com/WCF/wcf_bohansen/servicegui.jpg" width="600" />
		</p>
		<h2>Introduction</h2>
		<p nd="1">The reason for publishing this article is that I think that a lot of people are going to use the new .NET 3.0 WCF. It is simple to use, and seems very powerful. I have spend <b>a lot</b> of time debugging this program, and I hope that this article will prevent other people from getting the same headache that I had because of a funny security exception that I would get all the time. Use the code directly or as an inspiration. I assume that the reader is familiar with client/server coding, and will therefore not get into details. It is said that WCF is very dynamic regarding the transfer methods, and can be configured to use almost <b>any</b> communication standard which makes it suitable for many client/server applications. It does not matter if you use HTTP, TCP etc., to transfer data, and the optional SSL / Encryption makes the WCF even more suited for large scale solutions. 
</p>
		<h2>Background</h2>
		<p nd="2">The project is straightforward, really. The source contains some files that I used to develop the application. The source contains the ServiceLibrary that is the contract compiled into a simple DLL. Don't confuse the difference between the <b>contract</b> and the <b>proxy</b>. The contract is a sort of a way to communicate, and the proxy is a way to get access to another remote contract. The other two libraries are almost self-explaining, the WCFClient and the WCFService. The goal was to create a dynamic <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/WCF/wcf_bohansen.asp#" target="_blank" itxtdid="3060001">client server</a> solution that is <b>not</b> bound by configuration files etc., but is more dynamic. Plus don't underestimate the fun writing the code :) Thanks to Gedebuk, Denmark. 
</p>
		<p nd="3">The <i>Proxy.cs</i> code is auto-generated by <i>svcutil.exe</i>. If you need to re-create the <i>Proxy.cs</i>, start the service and run the command: <i>"C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\svcutil.exe" http://localhost:8001/MyService /out:Proxy.cs</i>.</p>
		<h2>Using the code</h2>
		<p nd="4">The code consists of two folders that are loaded into one project file. 
</p>
		<h2>How did I create the project?</h2>In this section, I will describe how I created the project. The steps are listed in sequence and are numbered. 
<h3>1) Creating the ServiceLibrary.cs</h3><p nd="6">I created the project by starting off with the <b><code>ServiceLibrary</code></b>. This library is compiled into a DLL which is used on the client (or clients for that matter). In WCF terms, this is the <b>contract</b> that the client must obey in order to be able to communicate with the server. This <b>contract</b> is in fact an <em>interface</em> that the client(s) communicates through. The <code nd="7">ServiceLibrary</code> contains all the methods that can be called by the client. In this example, it also holds the implementation for the service. The <b><code>DataContract</code></b> has the mark <code nd="8">[DataContract]</code> that indicates that it's an object that can be transferred. In WCF, it does not matter if it is a simple type, like a string, or a complex type like an object that is transferred over the wire. Below is a copy/paste from my <b>contract</b>. </p><div class="precollapse" id="premain0" style="WIDTH: 100%"><img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" /><span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="9" preid="0"> Collapse</span></div><pre lang="cs" id="pre0" style="MARGIN-TOP: 0px" nd="11"><span class="cs-keyword" nd="10">using</span> System;
<span class="cs-keyword" nd="12">using</span> System.Collections.Generic;
<span class="cs-keyword" nd="13">using</span> System.Text;
<span class="cs-keyword" nd="14">using</span> System.ServiceModel;
<span class="cs-keyword" nd="15">using</span> System.Runtime.Serialization;

<span class="cs-keyword" nd="16">namespace</span> ServiceLibrary
{
    <span class="cs-comment" nd="17">// You have created a class library to</span><span class="cs-comment" nd="18">// define and implement your WCF service.</span><span class="cs-comment" nd="19">// You will need to add a reference to this</span><span class="cs-comment" nd="20">// library from another project and add </span><span class="cs-comment" nd="21">// the code to that project to host the</span><span class="cs-comment" nd="22">// service as described below.  Another way</span><span class="cs-comment" nd="23">// to create and host a WCF service is by</span><span class="cs-comment" nd="24">// using the Add New Item, WCF Service </span><span class="cs-comment" nd="25">// template within an existing project such</span><span class="cs-comment" nd="26">// as a Console Application or a Windows </span><span class="cs-comment" nd="27">// Application.</span>

    [ServiceContract()]
    <span class="cs-keyword" nd="28">public</span><span class="cs-keyword" nd="29">interface</span> IService1
    {
        [OperationContract]
        <span class="cs-keyword" nd="30">string</span> MyOperation1(<span class="cs-keyword" nd="31">string</span> myValue);
        [OperationContract]
        <span class="cs-keyword" nd="32">string</span> MyOperation2(DataContract1 dataContractValue);
        [OperationContract]
        <span class="cs-keyword" nd="33">string</span> HelloWorld(<span class="cs-keyword" nd="34">string</span> str);
    }

    <span class="cs-keyword" nd="35">public</span><span class="cs-keyword" nd="36">class</span> service1 : IService1
    {
        <span class="cs-keyword" nd="37">public</span><span class="cs-keyword" nd="38">string</span> MyOperation1(<span class="cs-keyword" nd="39">string</span> myValue)
        {
            <span class="cs-keyword" nd="40">return</span><span class="cpp-string" nd="41">"Hello: "</span> + myValue;
        }
        <span class="cs-keyword" nd="42">public</span><span class="cs-keyword" nd="43">string</span> MyOperation2(DataContract1 dataContractValue)
        {
            <span class="cs-keyword" nd="44">return</span><span class="cpp-string" nd="45">"Hello: "</span> + dataContractValue.FirstName;
        }
        <span class="cs-keyword" nd="46">public</span><span class="cs-keyword" nd="47">string</span> HelloWorld(<span class="cs-keyword" nd="48">string</span> str)
        {
            <span class="cs-keyword" nd="49">return</span><span class="cpp-string" nd="50">"Helloworld from "</span> + str;
        }
    }

    [DataContract]
    <span class="cs-keyword" nd="51">public</span><span class="cs-keyword" nd="52">class</span> DataContract1
    {
        <span class="cs-keyword" nd="53">string</span> firstName;
        <span class="cs-keyword" nd="54">string</span> lastName;

        [DataMember]
        <span class="cs-keyword" nd="55">public</span><span class="cs-keyword" nd="56">string</span> FirstName
        {
            <span class="cs-keyword" nd="57">get</span> { <span class="cs-keyword" nd="58">return</span> firstName; }
            <span class="cs-keyword" nd="59">set</span> { firstName = value; }
        }
        [DataMember]
        <span class="cs-keyword" nd="60">public</span><span class="cs-keyword" nd="61">string</span> LastName
        {
            <span class="cs-keyword" nd="62">get</span> { <span class="cs-keyword" nd="63">return</span> lastName; }
            <span class="cs-keyword" nd="64">set</span> { lastName = value; }
        }
    }
}</pre><p nd="65">The code is something that the Visual Studio project generates for me. I've only added the HelloWorld part of this code. The VS environment did also generate a <em>how-to</em> on <b>Adding a Service Reference</b>. I've skipped that and deleted those parts. You can either configure the program from a configuration file that is a part of Visual Studio, or add the configuration in the code, or code the configuration. I have chosen to configure the service inside the code. One would argument about the ease of changing an XML file instead of recompiling the project if changes in the client/server relation occurs, but I have chosen to do this in order for the program to be able to configure itself in future implementations. This code will act as a core function in a rather large client/server solution where computers will contact other computers randomly via WCF. This requires the program to be able to change the runtime, and is the main reason for this code to be controlled <em>in-code</em>. 
</p><h3>2) Creating the Service-Host Application</h3><p nd="66">The service-host application is the program that holds the service that has the actual objects. This is the <b>server</b> if you like to call it that. 
</p><p nd="67">I created a Windows Forms project and added the following code: </p><div class="precollapse" id="premain1" style="WIDTH: 100%"><img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" /><span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="68" preid="1"> Collapse</span></div><pre lang="cs" id="pre1" style="MARGIN-TOP: 0px" nd="70"><span class="cs-keyword" nd="69">using</span> System;
<span class="cs-keyword" nd="71">using</span> System.Collections.Generic;
<span class="cs-keyword" nd="72">using</span> System.ComponentModel;
<span class="cs-keyword" nd="73">using</span> System.Data;
<span class="cs-keyword" nd="74">using</span> System.Drawing;
<span class="cs-keyword" nd="75">using</span> System.Text;
<span class="cs-keyword" nd="76">using</span> System.Windows.Forms;
<span class="cs-keyword" nd="77">using</span> System.Net;
<span class="cs-keyword" nd="78">using</span> System.ServiceModel;
<span class="cs-keyword" nd="79">using</span> System.ServiceModel.Description;

<span class="cs-keyword" nd="80">namespace</span> WCFService
{
    <span class="cs-keyword" nd="81">public</span> partial <span class="cs-keyword" nd="82">class</span> MainForm : Form
    {
        <span class="cs-keyword" nd="83">private</span> ServiceHost host = <span class="cs-keyword" nd="84">null</span>;
        <span class="cs-keyword" nd="85">private</span><span class="cs-keyword" nd="86">string</span> urlMeta, urlService = <span class="cpp-string" nd="87">""</span>;

        <span class="cs-keyword" nd="88">public</span> MainForm()
        {
            InitializeComponent();
            Append(<span class="cpp-string" nd="89">"Program started ..."</span>);
        }

        <span class="cs-keyword" nd="90">void</span> host_Opening(<span class="cs-keyword" nd="91">object</span> sender, EventArgs e)
        {
            Append(<span class="cpp-string" nd="92">"Service opening ... Stand by"</span>);
        }

        <span class="cs-keyword" nd="93">private</span><span class="cs-keyword" nd="94">void</span> button1_Click(<span class="cs-keyword" nd="95">object</span> sender, EventArgs e)
        {
            <span class="cs-keyword" nd="96">try</span>
            {
                <span class="cs-comment" nd="97">// Returns a list of ipaddress configuration</span>
                IPHostEntry ips = Dns.GetHostEntry(Dns.GetHostName());

                <span class="cs-comment" nd="98">// Select the first entry. I hope it's this maschines IP</span>
                IPAddress _ipAddress = ips.AddressList[<span class="cs-literal" nd="99">0</span>];

                <span class="cs-comment" nd="100">// Create the url that is needed to specify</span><span class="cs-comment" nd="101">// where the service should be started</span>
                urlService = <span class="cpp-string" nd="102">"net.tcp://"</span> + 
                    _ipAddress.ToString() + <span class="cpp-string" nd="103">":8000/MyService"</span>;

                <span class="cs-comment" nd="104">// Instruct the ServiceHost that the type</span><span class="cs-comment" nd="105">// that is used is a ServiceLibrary.service1</span>
                host = <span class="cs-keyword" nd="106">new</span> ServiceHost(<span class="cs-keyword" nd="107">typeof</span>(ServiceLibrary.service1));
                host.Opening += <span class="cs-keyword" nd="108">new</span> EventHandler(host_Opening);
                host.Opened += <span class="cs-keyword" nd="109">new</span> EventHandler(host_Opened);
                host.Closing += <span class="cs-keyword" nd="110">new</span> EventHandler(host_Closing);
                host.Closed += <span class="cs-keyword" nd="111">new</span> EventHandler(host_Closed);

                <span class="cs-comment" nd="112">// The binding is where we can choose what</span><span class="cs-comment" nd="113">// transport layer we want to use. HTTP, TCP ect.</span>
                NetTcpBinding tcpBinding = <span class="cs-keyword" nd="114">new</span> NetTcpBinding();
                tcpBinding.TransactionFlow = <span class="cs-keyword" nd="115">false</span>;
                tcpBinding.Security.Transport.ProtectionLevel = 
                   System.Net.Security.ProtectionLevel.EncryptAndSign;
                tcpBinding.Security.Transport.ClientCredentialType = 
                   TcpClientCredentialType.Windows;
                tcpBinding.Security.Mode = SecurityMode.None;
                <span class="cs-comment" nd="116">// &lt;- Very crucial</span><span class="cs-comment" nd="117">// Add a endpoint</span>
                host.AddServiceEndpoint(<span class="cs-keyword" nd="118">typeof</span>(
                   ServiceLibrary.IService1), tcpBinding, urlService);

                <span class="cs-comment" nd="119">// A channel to describe the service.</span><span class="cs-comment" nd="120">// Used with the proxy scvutil.exe tool</span>
                ServiceMetadataBehavior metadataBehavior;
                metadataBehavior = 
                  host.Description.Behaviors.Find&lt;ServiceMetadataBehavior&gt;();
                <span class="cs-keyword" nd="121">if</span> (metadataBehavior == <span class="cs-keyword" nd="122">null</span>)
                {
                    <span class="cs-comment" nd="123">// This is how I create the proxy object</span><span class="cs-comment" nd="124">// that is generated via the svcutil.exe tool</span>
                    metadataBehavior = <span class="cs-keyword" nd="125">new</span> ServiceMetadataBehavior();
                    metadataBehavior.HttpGetUrl = <span class="cs-keyword" nd="126">new</span> Uri(<span class="cpp-string" nd="127">"http://"</span> + 
                          _ipAddress.ToString() + <span class="cpp-string" nd="128">":8001/MyService"</span>);
                    metadataBehavior.HttpGetEnabled = <span class="cs-keyword" nd="129">true</span>;
                    metadataBehavior.ToString();
                    host.Description.Behaviors.Add(metadataBehavior);
                    urlMeta = metadataBehavior.HttpGetUrl.ToString();
                }

                host.Open();
            }
            <span class="cs-keyword" nd="130">catch</span> (Exception ex1)
            {
                Console.WriteLine(ex1.StackTrace);
            }
        }

        <span class="cs-keyword" nd="131">private</span><span class="cs-keyword" nd="132">void</span> button2_Click(<span class="cs-keyword" nd="133">object</span> sender, EventArgs e)
        {
            host.Close();
        }

        <span class="cs-keyword" nd="134">void</span> host_Closed(<span class="cs-keyword" nd="135">object</span> sender, EventArgs e)
        {
            Append(<span class="cpp-string" nd="136">"Service closed"</span>);
        }

        <span class="cs-keyword" nd="137">void</span> host_Closing(<span class="cs-keyword" nd="138">object</span> sender, EventArgs e)
        {
            Append(<span class="cpp-string" nd="139">"Service closing ... stand by"</span>);
        }

        <span class="cs-keyword" nd="140">void</span> host_Opened(<span class="cs-keyword" nd="141">object</span> sender, EventArgs e)
        {
            Append(<span class="cpp-string">"Service opened."</span>);
            Append(<span class="cpp-string">"Service URL:\t"</span> + urlService);
            Append(<span class="cpp-string">"Meta URL:\t"</span> + urlMeta + <span class="cpp-string">" (Not that relevant)"</span>);
            Append(<span class="cpp-string">"Waiting for clients..."</span>);
        }

        <span class="cs-keyword">private</span><span class="cs-keyword">void</span> Append(<span class="cs-keyword">string</span> str)
        {
            textBox1.AppendText(<span class="cpp-string">"\r\n"</span> + str);
        }
    }
}
</pre><p>The interesting part is of course the <code>button1_Click</code> which creates the service and makes the service public to other clients. Now for other clients to connect to this service, the service needs a <em>contract</em>. The contract is not something that one would write themselves, and that's why I use the <i>scvutil.exe</i> tool. On my PC, the tool is located at <i>C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\svcutil.exe</i>. In order to make the contract <em>object</em> (that is actually the proxy that clients connect to), we need to generate <i>Proxy.cs</i> (the name of the file is not relevant at all, could be <i>foo.cs</i> as well). This will happen in step 3. 
</p><h3>3) Building the Proxy Object for Clients to Use</h3><p>The proxy object is built from the service description that is located on port 8001 (check the code). It reads the meta data from the service, and constructs the contract that is needed by clients when they want to communicate with the service.<br /></p><ol><li>First, start the service and hit the "Start service" button. This creates the service. All meta info is published for other clients or tools to read them. 
</li><li>Second, run <i>"C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\svcutil.exe" http://localhost:8001/MyService /out:Proxy.cs</i>. This creates two files. A <i>Proxy.cs</i> file and an XML config file. I discard the config file because I'll configure the service inside the program. You should consider putting the <i>"C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\svcutil.exe" http://localhost:8001/MyService /out:Proxy.cs</i> in a bat file to make it easier for yourself. I have included my bat file for you to see. It resides in the "<i>ProxyGenerator</i>" folder.<br /><br />Next, I create a simple client and add relevant code to it. </li></ol><h3>4) Creating a Client Application and Adding Relevant Code to it</h3><p>Now, we create a simple standard client program as a Windows Forms apllication. Once the files are generated (<i>Proxy.cs</i> and <i>output.config</i> &lt;- can be suppressed), I add the <i>Proxy.cs</i> to my client program which is called <b>WCFClient</b>. Now, the client knows the contract and is able to obey it in order to create a communication channel to the service. One could compile the <i>Proxy.cs</i> into a DLL and simply add the DLL to the client (a cleaner way to add something that is crucial as a communication contract or <em>interface</em>, I think). For demonstration, we leave the <i>Proxy.cs</i> as it is and add it to the client project. Next, and the last step: we need to add some simple client code in order to retrieve the service proxy object. My client code looks like this: </p><div class="precollapse" id="premain2" style="WIDTH: 100%"><img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" /><span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="2"> Collapse</span></div><pre lang="cs" id="pre2" style="MARGIN-TOP: 0px"><span class="cs-keyword">using</span> System;
<span class="cs-keyword">using</span> System.Collections.Generic;
<span class="cs-keyword">using</span> System.ComponentModel;
<span class="cs-keyword">using</span> System.Data;
<span class="cs-keyword">using</span> System.Drawing;
<span class="cs-keyword">using</span> System.Text;
<span class="cs-keyword">using</span> System.Windows.Forms;
<span class="cs-keyword">using</span> System.ServiceModel;

<span class="cs-keyword">namespace</span> WCFClient
{
    <span class="cs-keyword">public</span> partial <span class="cs-keyword">class</span> Form1 : Form
    {
        <span class="cs-keyword">string</span> endPointAddr = <span class="cpp-string">""</span>;

        <span class="cs-keyword">public</span> Form1()
        {
            InitializeComponent();
        }

        <span class="cs-keyword">private</span><span class="cs-keyword">void</span> button1_Click(<span class="cs-keyword">object</span> sender, EventArgs e)
        {
            <span class="cs-keyword">if</span> (textBox1.Text != <span class="cpp-string">""</span>)
            {
                endPointAddr = <span class="cpp-string">"net.tcp://"</span> + textBox2.Text + 
                               <span class="cpp-string">":8000/MyService"</span>;
                NetTcpBinding tcpBinding = <span class="cs-keyword">new</span> NetTcpBinding();
                tcpBinding.TransactionFlow = <span class="cs-keyword">false</span>;
                tcpBinding.Security.Transport.ProtectionLevel = 
                  System.Net.Security.ProtectionLevel.EncryptAndSign;
                tcpBinding.Security.Transport.ClientCredentialType = 
                  TcpClientCredentialType.Windows;
                tcpBinding.Security.Mode = SecurityMode.None;

                EndpointAddress endpointAddress = 
                   <span class="cs-keyword">new</span> EndpointAddress(endPointAddr);

                Append(<span class="cpp-string">"Attempt to connect to: "</span> + endPointAddr);

                IService1 proxy = 
                  ChannelFactory&lt;IService1&gt;.CreateChannel(
                  tcpBinding, endpointAddress);

                <span class="cs-keyword">using</span> (proxy <span class="cs-keyword">as</span> IDisposable)
                {
                    Append(<span class="cpp-string">"Message from server: "</span> + 
                      (proxy.HelloWorld(textBox1.Text) + 
                      <span class="cpp-string">" back to you :)"</span>));
                }   
            }
        }

        <span class="cs-keyword">private</span><span class="cs-keyword">void</span> Append(<span class="cs-keyword">string</span> str)
        {
            textBox3.AppendText(<span class="cpp-string">"\r\n"</span> + str);</pre><h2>Points of Interest</h2><p>Well, I really had a lot of fun working with the code and writing the article. The most impressive thing, in my opinion, is that the transport layer very easily can be modified to use HTTP instead of TCP. I did not point that out in the article but that's also something nice to have. Being able to switch the transport layer from TCP (Secure SSL) into HTTP with some simple code, that's amazing! I did have some problems, though. The <code>tcpBinding.Security.Mode = SecurityMode.None;</code> is very crucial on both sides. I'm not sure what it does, but it does not turn off the security completely as I have read in my references. There is still SSL encryption, but on a lower level. It is possible to add certificates to the connection, which also makes the connection more secure. 
</p><h2>History</h2><ul><li>December 13<sup>th</sup> 2006 - Project completed.</li></ul><!-- Article Ends --><script src="/script/togglePre.js" type="text/javascript"></script><h2>Bo Hansen</h2><div style="OVERFLOW: hidden"><table border="0"><tbody><tr valign="top"><td class="smallText" nowrap=""><br /></td><td class="smallText"><p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=1612320">here</a> to view Bo Hansen's online profile.</p></td></tr></tbody></table></div><img src ="http://www.cnitblog.com/MartinYao/aggbug/21455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 18:02 <a href="http://www.cnitblog.com/MartinYao/articles/21455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Communication options with WCF - Part 2</title><link>http://www.cnitblog.com/MartinYao/articles/21453.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 10:01:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21453.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21453.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21453.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21453.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21453.html</trackback:ping><description><![CDATA[
		<h2>Introduction</h2>
		<p nd="1">In <a href="http://www.codeproject.com/winfx/WCF_CommOptions_part1.asp">Part 1</a>, we defined what a service is, and also created a simple WCF service and client to demonstrate the process of creating and consuming services under WCF. In this article, we'll start exploring some of the more advanced communication options available under WCF. All the source code for the examples presented are available in the download for Part 1.</p>
		<h2>An All-In-One Service</h2>
		<p nd="2">Let's continue by implementing an example where the service and the client both exist in the same process! Why would anyone want to do this? Well, I’ve needed it, for one. Suppose you have a module that contains several services. The services are actually DB servers that abstract and isolate access to the database. Well, one of the services might actually need the functionality that is provided by another service (in the process). Since they are all in the same housing, you could instantiate the required service (class) locally. But then, you would be accessing the service differently than other clients, and you would also be bypassing the isolation and other facilities that the <code nd="3">ServiceHost</code> provides. In any case, this is just another way of doing things when the requirements are right. In a subsequent section, we’ll actually be doing the opposite, where we are creating a singleton and want only one instance of the service class. The following source shows the changes necessary to modify the LocalTimeService example.</p>
		<div class="precollapse" id="premain0" style="WIDTH: 100%">
				<img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" />
				<span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="4" preid="0"> Collapse</span>
		</div>
		<pre lang="cs" id="pre0" style="MARGIN-TOP: 0px" nd="5">namespace AllInOneTimeService
{
    class Client
    {
        public bool keepClocking = true;
        LocalTimeProxy proxy = null;
        public Client()
        {
            proxy = new LocalTimeProxy();
        }
        public void ClockingThread()
        {
            while (keepClocking)
            {
                Console.WriteLine(proxy.GetLocalTime());
                Thread.Sleep(1000);
            }
            proxy.Close();
        }
        static void Main(string[] args)
        {
            Uri baseAddress = new 
              Uri(ConfigurationManager.AppSettings[<span class="cpp-string" nd="6">"basePipeTimeService"</span>]);
            ServiceHost serviceHost = new 
              ServiceHost(typeof(LocalTimeService), baseAddress);
            serviceHost.Open();
            <span class="cs-comment" nd="7">//The service is open for business</span>
            Console.WriteLine(<span class="cpp-string" nd="8">"Service is running...."</span> + 
                              <span class="cpp-string" nd="9">"press any key to terminate."</span>);
            <span class="cs-comment" nd="10">//Start a client thread</span>
            Client client = new Client();
            Thread thread = new Thread(new 
                   ThreadStart(client.ClockingThread));
            thread.Start();

            Console.ReadKey();
            client.keepClocking = false;

            <span class="cs-comment" nd="11">//wait 2 seconds</span>
            Thread.Sleep(2000);
            <span class="cs-comment" nd="12">//Close up shop</span>
            serviceHost.Close();
        }
    }
}</pre>
		<p nd="13">After the service is started, we simply instantiate a client. Of course, this is just to demonstrate that the service can be accessed by internal <b>and</b> external sources. Normally, it would be one service accessing another service as a result of a client request. The only other change of note is that the config file needs to define both the service <b>and</b> the client endpoints. Go ahead and start this version of the service. Then, start several instances of the standalone clients. As you can see, the service can be accessed from different sources (internal and external), and they are all serviced by the same host.</p>
		<h2>Two Spigots for the Price of One</h2>
		<p nd="14">The LocalTimeService examples that we've been using only supports IPC (internal clients using named pipes). Let’s change that, and add support for TCP clients. The cool thing about this is that we won’t have to change much, it will mostly be only config file changes. The code below is the only change we need to make to the service code. We’re using the <i>AllInOne</i> version of the service so that you can see how we can support both types of transport at the same time. The internal client will use named pipes, while the external client will use TCP.</p>
		<pre lang="cs" nd="15">static void Main(string[] args)
{
    Uri baseAddress = new Uri(
      ConfigurationManager.AppSettings[<span class="cpp-string" nd="16">"baseTcpTimeService"</span>]);
    ServiceHost serviceHost = new ServiceHost(
      typeof(LocalTimeService), baseAddress);
    baseAddress = new Uri(
      ConfigurationManager.AppSettings[<span class="cpp-string" nd="17">"basePipeTimeService"</span>]);
    serviceHost.AddServiceEndpoint(typeof(ILocalTime), 
              new NetNamedPipeBinding(), baseAddress);
    serviceHost.Open();
    ...
}</pre>
		<p nd="18">First, we instantiate <code nd="19">ServiceHost</code>, and pass the name of the endpoint that we want it to support (TCP). Then, we programmatically add a second endpoint (named pipe). All the required information is in the config file, as shown below:</p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="20" preid="2"> Collapse</span>
		</div>
		<pre lang="xml" id="pre2" style="MARGIN-TOP: 0px" nd="21">&lt;configuration&gt;
  &lt;appSettings&gt;
    &lt;add key="baseTcpTimeService" 
         value="net.tcp://localhost:9000/LocalTimeService" /&gt;
    &lt;add key="basePipeTimeService" 
         value="net.pipe://localhost/LocalTimeService" /&gt;
  &lt;/appSettings&gt;
  &lt;system.serviceModel&gt;
    &lt;services&gt;
      &lt;service name="LocalTimeService"&gt;
        &lt;endpoint 
           address="" 
           binding="netTcpBinding" 
           contract="ILocalTime" 
         /&gt;
      &lt;/service&gt;
    &lt;/services&gt;
    &lt;client&gt;
      &lt;endpoint name ="LocalTimeService"
                address="net.pipe://localhost/LocalTimeService"
                binding="netNamedPipeBinding"
                contract="ILocalTime" /&gt;
    &lt;/client&gt;
  &lt;/system.serviceModel&gt;
&lt;/configuration&gt;</pre>
		<p nd="22">The only change that’s required on the standalone client is in the config file, as shown next:</p>
		<pre lang="xml" nd="23">&lt;configuration&gt;
  &lt;system.serviceModel&gt;
    &lt;client&gt;
      &lt;endpoint name ="LocalTimeService"
                address="net.tcp://localhost:9000/LocalTimeService"
                binding="netTcpBinding"
                contract="ILocalTime" /&gt;
    &lt;/client&gt;

  &lt;/system.serviceModel&gt;
&lt;/configuration&gt;</pre>
		<p nd="24">As we did before, go ahead and start the service, and then start several stand-alone clients. No difference, except that the external clients are using TCP to communicate with the service, while the internal clients use named pipes. Isn't that cool?! But wait, there’s more.</p>
		<h2>The Lazy Client</h2>
		<p nd="25">Let’s now consider a different service. This is a service that monitors the temperature in a reactor. It is very important that changes in the reactor temperature be detected as quickly as possible. So, each client needs to check with the service at very short intervals of time. And what would that be? Once a second, ten times a second, a hundred times a second? Clearly, if the number of clients is large, there would be a lot of wasted traffic if the temperature is not changing frequently. Alternatively, suppose the client decides to check the temperature every 100 milliseconds, there is still the possibility that the temperature may have changed a few times (up and down) in that span of time. So, if the client needed to know each change of temperature, then it would have missed a few.</p>
		<p nd="26">So a better (or ‘more gooder’, as a friend of mine prefers) solution would be to implement a Publish/Subscribe pattern. The service (sensor) would simply monitor the temperature, and if the change happens to be above a specified threshold, it would send out a message to any client that had requested to be notified. The clients would just have to subscribe to the service and then wait for any notifications. When the notifications come, each client would do whatever it needed to do according to its role: log, alarm, control, etc. Here’s the code for a temperature sensor service:</p>
		<div class="precollapse" id="premain4" style="WIDTH: 100%">
				<img id="preimg4" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="4" />
				<span id="precollapse4" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="27" preid="4"> Collapse</span>
		</div>
		<pre lang="cs" id="pre4" style="MARGIN-TOP: 0px" nd="29">
				<span class="cs-comment" nd="28">//The interface the service exposes</span>
[ServiceContract(Session = true, 
   CallbackContract = typeof(ITempChangedHandler))]
public interface ITempChangedPub
{
    [OperationContract(IsInitiating = true)]
    void Subscribe();
    [OperationContract(IsTerminating = true)]
    void Unsubscribe();
}
<span class="cs-comment" nd="30">//The interface that clients must implement...</span>
public interface ITempChangedHandler
{
    [OperationContract(IsOneWay = true)]
    void TempChanged(int newTemp);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class TempSensor : ITempChangedPub
{
    <span class="cs-comment" nd="31">//a list of who's interested</span>
    ArrayList subscribers = new ArrayList();
    TempSimulator sim;  <span class="cs-comment" nd="32">//our temperature simulator</span>
    public TempSensor()
    {
        <span class="cs-comment" nd="33">//Start simulating the temperature</span>
        sim = new TempSimulator(this);
        Thread simThread = new Thread(new 
               ThreadStart(sim.SimThread));
        simThread.Start();
    }
    public void Subscribe()
    {
        subscribers.Add(
          OperationContext.Current.GetCallbackChannel&lt;ITempChangedHandler&gt;());
    }
    public void Unsubscribe()
    {
        ITempChangedHandler caller = 
          OperationContext.Current.GetCallbackChannel&lt;ITempChangedHandler&gt;();
        foreach (ITempChangedHandler h in subscribers)
            if (h == caller)
            {
                subscribers.Remove(h);
                break;
            }
    }
    public void PublishTempChanged(int temp)
    {
        foreach (ITempChangedHandler h in subscribers)
            h.TempChanged(temp);
    }
    public void StopSim()
    {
        sim.runSim = false;
        Thread.Sleep(1000);
    }
}</pre>
		<p nd="34">A few things to note on the above code. First, you’ll notice that the service class has been decorated with a <code nd="35">ServiceBehavior</code> attribute. There are several options for this attribute, but it essentially specifies how we want to control the ‘instancing behavior’ of the service. In this situation, we specify ‘Single’ since we want only one instance of the service. It has to stick around to be able to receive the subscription messages from the clients. The <code nd="36">ServiceBehavior</code> attribute has two additional parameters specified for it. First, the <code lang="cs" nd="37">Session=true</code> indicates that the service will create and maintain a session for each client. The lifetime of the session is defined by the <code nd="38">IsInitiating</code>/<code nd="39">IsTerminating</code> properties of the <code nd="40">OperationContract</code> attribute. So the session can be <i>kinda</i> controlled by the client. Let’s take another look at that.</p>
		<p nd="41">Consider the interface defined below that might be used to control AGVs. Automated Guided Vehicles (AGVs) are essentially self guided forklifts. They have a laser system on board with which they can tell where they are within a physical environment. And then you can control them pretty much like you would control a remote control car.</p>
		<pre lang="cs" nd="42">[ServiceContract(Session=true)]
public interface IAGVControl
{
    [OperationContract(IsInitiating=true)]
    int AcquireVehicle();
    [OperationContract(IsTerminating=true)]
    void ReleaseVehicle();
    [OperationContract]
    void MoveTo(int location);
    [OperationContract]
    void Load();
    [OperationContract]
    void Unload();
}</pre>
		<p nd="43">A client would first make a call to <code nd="44">AcquireVehicle</code> to establish a session (as well as get a physical AGV to manipulate). Now, the client can perform any number of operations with the vehicle. Possession of the vehicle is essentially controlled through session management! Of course, we know that with great power comes great responsibility. Once the client calls <code nd="45">ReleaseVehicle</code>, there better not be any subsequent requests made.</p>
		<p nd="46">One last point on the previous TempSensor code. You’ll notice that the <code nd="47">OperationContract</code> attribute for the callback <code nd="48">TempChanged</code> has the <code nd="49">IsOneway</code> property set to <code lang="cs"><span class="cs-keyword" nd="50">true</span></code>. This property defines the requirement (or lack thereof) for a reply message. Even if the return value is <code lang="cs"><span class="cs-keyword" nd="51">void</span></code>, there will still be a reply message generated if the <code nd="52">IsOneway</code> property is set to <code lang="cs"><span class="cs-keyword" nd="53">false</span></code> (the default). Requiring a reply message provides a mechanism to return exceptions thrown by the service back to the client. Also note that if you set <code nd="54">IsOneway</code> to <code lang="cs"><span class="cs-keyword" nd="55">true</span></code> and you <b>do</b> have a return value, you’ll get an exception.</p>
		<p nd="56">Now, here’s the rest of the code for the temperature service:</p>
		<pre lang="cs" nd="57">static void Main(string[] args)
{
    <span class="cs-comment" nd="58">//Now start hosting the service</span>
    TempSensor sensor = new TempSensor();
    Uri baseAddress = new Uri(
      ConfigurationManager.AppSettings[<span class="cpp-string" nd="59">"basePipeTempService"</span>]);
    ServiceHost serviceHost = new ServiceHost(sensor, baseAddress);
    serviceHost.Open();
    Console.WriteLine(<span class="cpp-string" nd="60">"Service is running...."</span> + 
                 <span class="cpp-string" nd="61">"press any key to terminate."</span>);
    Console.ReadKey();
    
    sensor.StopSim();

    serviceHost.Close();
}</pre>
		<p nd="62">The only thing to note on the above is that we create the service class object and <i>then</i> pass a reference to it to <code nd="63">ServiceHost</code>.</p>
		<p nd="64">So, the temperature service provides an interface that lets clients <i>subscribe</i> and <i>unsubscribe</i> to temperature changes. In this case, the service will send a message to each client that has subscribed, whenever the temperature changes more than 5 degrees. There is nothing special about the subscribe/unsubscribe interface, you could call it whatever you want (in fact, you’ll need a different name if there is more than one thing that clients can subscribe to), and you can pass in parameters. For example, you might allow each client to specify at what granularity or temperature level that it wants to be notified. Of course, the service has to do a little more work, in this case, to keep track of which client wants what.</p>
		<p nd="65">You’ll also notice that there is a second interface defined. That is the interface that the clients have to implement in order to receive notifications from the service. When the proxy gets created for the client, a ‘callback’ interface will also be included in order for the clients to be able to know what <i>they</i> have to implement. Here’s the proxy class for the service.</p>
		<div class="precollapse" id="premain7" style="WIDTH: 100%">
				<img id="preimg7" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="7" />
				<span id="precollapse7" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="66" preid="7"> Collapse</span>
		</div>
		<pre lang="cs" id="pre7" style="MARGIN-TOP: 0px" nd="67">[ServiceContract(CallbackContract=typeof(
                 ITempChangedPubCallback), Session=true)]
public interface ITempChangedPub
{
    [OperationContract]
    void Subscribe();
    
    [OperationContract]
    void Unsubscribe();
}

public interface ITempChangedPubCallback
{
    [OperationContract]
    void TempChanged(int newTemp);
}

public interface ITempChangedPubChannel : ITempChangedPub, 
                       System.ServiceModel.IClientChannel
{
}

public partial class TempChangedPubProxy : 
       System.ServiceModel.DuplexClientBase&lt;ITempChangedPub&gt;, 
       ITempChangedPub
{
    public TempChangedPubProxy(
           System.ServiceModel.InstanceContext callbackInstance) : 
           base(callbackInstance)
    {
    }
    
    public void Subscribe()
    {
        base.InnerProxy.Subscribe();
    }
    
    public void Unsubscribe()
    {
        base.InnerProxy.Unsubscribe();
    }
}</pre>
		<p nd="68">The temperature service client, then, is just slightly more complicated than the client for the <code nd="69">LocalTimeService</code>. The <code nd="70">TempChanged</code> client needs to do two things. First, it has to instantiate a class that implements the <code nd="71">ITempChangedPubCallback</code> interface so that the service can send messages back to the client. And the other thing the client needs to do is provide some kind of hosting for the callback, similar to what the <code nd="72">ServiceHost</code> provides for the service. This is how the message can be detected and routed to the right place. The class that provides that functionality is the <code nd="73">InstanceContext</code> class (if you look at the class definition, you'll notice it has a <code nd="74">host</code> member). When we create the <code nd="75">InstanceContext</code>, we pass it the handler class that will service the temperature change messages (has implemented <code nd="76">ItempChangedPubCallback</code>).</p>
		<pre lang="cs" nd="77">class TempChangedHandler : ITempChangedPubCallback
{
    public void TempChanged(int temp)
    {
        Console.WriteLine(temp.ToString());
    }
}
static void Main(string[] args)
{
    InstanceContext siteTempChangedHandler = null;
    TempChangedPubProxy proxyTempChanged;

    siteTempChangedHandler = new InstanceContext(new TempChangedHandler());
    proxyTempChanged = new TempChangedPubProxy(siteTempChangedHandler);
    proxyTempChanged.Subscribe();

    Console.WriteLine(<span class="cpp-string" nd="78">"Client is running....press any key to terminate."</span>);
    Console.ReadKey();
    
    proxyTempChanged.Unsubscribe();
}</pre>
		<p nd="79">That’s the extent of the code, except for the endpoint definition in the config file. Here, we need to indicate both the ‘client’ endpoint (the service we’re talking to) as well as an endpoint definition for the client's callback.</p>
		<div class="precollapse" id="premain9" style="WIDTH: 100%">
				<img id="preimg9" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="9" />
				<span id="precollapse9" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="80" preid="9"> Collapse</span>
		</div>
		<pre lang="xml" id="pre9" style="MARGIN-TOP: 0px" nd="81">&lt;configuration&gt;
  &lt;appSettings&gt;
    &lt;add key="basePipeTempChangedHandler" 
       value="net.pipe://localhost/TempSensorHandler" /&gt;
  &lt;/appSettings&gt;
  &lt;system.serviceModel&gt;
    &lt;services&gt;
      &lt;service 
      name="TempSensorHandler"&gt;
        &lt;!-- use base address provided by host --&gt;
        &lt;endpoint name="pipeEndpoint" address=""
                  binding="netNamedPipeBinding"
                  contract="ITempChangedPubCallback" /&gt;
      &lt;/service&gt;
    &lt;/services&gt;
    &lt;client&gt;
      &lt;endpoint name ="TempChangedPub"
                address="net.pipe://localhost/TempSensor"
                binding="netNamedPipeBinding"
                contract="ITempChangedPub" /&gt;
    &lt;/client&gt;
  &lt;/system.serviceModel&gt;
&lt;/configuration&gt;</pre>
		<p nd="82">Start the service, and then start an instance of the client to test out the code. I’m curious to see if the temperatures does manage to stay around 100 over time.</p>
		<h2>Asynchronicity</h2>
		<p nd="83">Sometimes, the functionality to be provided by a service is a long process, and the client cannot afford to wait for the response. Under those circumstances, there are two different approaches to providing asynchronous access to the service. The first one is based on the .NET Asynchronous pattern where the disconnection is provided by the client side infrastructure. The second method uses a Duplex Communication pattern where the client implements a callback interface. Let’s take a look at each approach.</p>
		<p nd="84">Consider the AGV application described previously. AGVs are very slow vehicles relative to the processing time of the system. They travel at best about 1 foot/second. So, to travel 10 feet, it would take 10 seconds. And there’s speedup, slowdown, and turning delays that also have an effect on the total time. So when a client sends a <i>MoveTo</i> request, it will need to wait a while before it gets back a response that the operation completed.</p>
		<p nd="85">There is nothing special that needs to be done on the service side for asynchronous clients. All of the functionality is provided on the client side. So we’ll start with a service that implements the <code nd="86">IAGVControl</code> interface described previously. Here's the code for a class we'll call <i>AGVController</i>.</p>
		<pre lang="cs" nd="87">[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class AGVControl : IAGVControl
{
    public int AcquireVehicle()
    {
        int vehicleID = 0;
        return vehicleID;
    }
    public void ReleaseVehicle()
    {
        <span class="cs-comment" nd="88">//place vehicle back in the pool</span>
    }
    public void MoveTo(int location)
    {
        <span class="cs-comment" nd="89">//This takes a long time...</span>
        Thread.Sleep(5000);
    }
    public void Load()
    {
        <span class="cs-comment" nd="90">//load it</span>
    }
    public void Unload()
    {
        <span class="cs-comment" nd="91">//unload it</span>
    }
}</pre>
		<p nd="92">As you can see, there is (literally) nothing here. Except that the <code nd="93">MoveTo</code> operation takes 5 seconds to complete. So, any method calling <code nd="94">MoveTo</code> won’t return right away. The instancing behavior for the class is specified as <code nd="95">PerSession</code>. That means, <code nd="96">ServiceHost</code> will create an instance of <code nd="97">AGVControl</code> for each client session. And the session lifetime was defined with the interface using the <code nd="98">IsInitiating</code>/<code nd="99">IsTerminating</code> attributes. Compile <code nd="100">AGVControl</code> into a service DLL so that we can generate a proxy using <i>svcutil</i>. We’ll describe the proxy generation when we build the asynchronous client.</p>
		<p nd="101">As usual, we need to build a host for the service. There is nothing different from the previous examples, so here it is.</p>
		<pre lang="cs" nd="102">static void Main(string[] args)
{
    Uri baseAddress = 
        new Uri(ConfigurationManager.AppSettings[<span class="cpp-string" nd="103">"basePipeAGVCtlr"</span>]);
    ServiceHost serviceHost = 
        new ServiceHost(typeof(AGVControl), baseAddress);
    serviceHost.Open();
    Console.WriteLine(<span class="cpp-string" nd="104">"Service is running....press any key to terminate."</span>);
    Console.ReadKey();
    serviceHost.Close();
}</pre>
		<pre lang="xml" nd="105">&lt;configuration&gt;
  &lt;appSettings&gt;
    &lt;add key="basePipeAGVCtlr" value="net.pipe://localhost/AGVControl" /&gt;
  &lt;/appSettings&gt;
  &lt;system.serviceModel&gt;
    &lt;services&gt;
      &lt;service name="AGVController.AGVControl"&gt;
        &lt;endpoint 
           address="" 
           binding="netNamedPipeBinding" 
           contract="AGVController.IAGVControl" 
         /&gt;
      &lt;/service&gt;
    &lt;/services&gt;
  &lt;/system.serviceModel&gt;
&lt;/configuration&gt;</pre>
		<p nd="106">Compile and start the service to make sure everything is OK. Now, let’s turn our attention towards the client where all the magic takes place. Once again, the first thing we need is a proxy in order for the client to be able to do anything.</p>
		<p nd="107">One of the options for <i>svcutil</i> is to generate asynchronous methods to match the Asynchronous pattern. Essentially, this means generating two method signatures for each service method, one to <i>begin</i> the asynchronous operation and one to <i>end</i>. The other item that’s required for the Asynchronous pattern is a callback method that will be called when the asynchronous operation has completed. That’s when we get the results from the service operation.</p>
		<p nd="108">So, execute <i>svcutil</i> against the AGVControl service DLL that we created above. This will generate the ‘xsd’ and ‘wsdl’ files, as we have seen previously. Then, re-run <i>svcutil</i>, but this time, specify the <i>*.xsd</i> and <i>*.wsdl</i> files that were just created, <b>and</b> also include the ‘/a’ option. Svcutil will generate a proxy, with asynchronous signatures for each method that the service exposes.</p>
		<p nd="109">Since <i>svcutil</i> is really just a little helper utility, I decided to do some editing on the resulting file. I know that the only method that takes a long time is the <code nd="110">MoveTo</code> method (I know that because I coded the delay). So, I only want to implement <i>that one</i> as an asynchronous operation, all others are to remain as synchronous calls. Here’s the edited version of the proxy:</p>
		<div class="precollapse" id="premain13" style="WIDTH: 100%">
				<img id="preimg13" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="13" />
				<span id="precollapse13" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="111" preid="13"> Collapse</span>
		</div>
		<pre lang="cs" id="pre13" style="MARGIN-TOP: 0px" nd="112">[ServiceContract]
public interface IAGVControl
{
    [OperationContract]
    int AcquireVehicle();
    [OperationContract]
    void Load();

    [OperationContract(AsyncPattern = true)]
    System.IAsyncResult BeginMoveTo(int location, 
           System.AsyncCallback callback, object asyncState);
     void EndMoveTo(System.IAsyncResult result);
 
    [OperationContract]
    void ReleaseVehicle();
    [OperationContract]
    void Unload();
}

public interface IAGVControlChannel : 
       IAGVControl, System.ServiceModel.IClientChannel
{
}

public partial class AGVControlProxy : 
       System.ServiceModel.ClientBase&lt;IAGVControl&gt;, 
       IAGVControl
{
    public AGVControlProxy()
    {
    }
    public int AcquireVehicle()
    {
        return base.InnerProxy.AcquireVehicle();
    }
    public void Load()
    {
        base.InnerProxy.Load();
    }
    public System.IAsyncResult BeginMoveTo(int location, 
           System.AsyncCallback callback, object asyncState)
    {
        return base.InnerProxy.BeginMoveTo(location, 
                               callback, asyncState);
    }
    public void EndMoveTo(System.IAsyncResult result)
    {
        base.InnerProxy.EndMoveTo(result);
    }
    public void ReleaseVehicle()
    {
        base.InnerProxy.ReleaseVehicle();
    }
    public void Unload()
    {
        base.InnerProxy.Unload();
    }
}</pre>
		<p nd="113">You’ll notice that the <code nd="114">MoveTo</code> operation has been replaced with two methods, <code nd="115">BeginMoveTo</code> and <code nd="116">EndMoveTo</code>. There are also a couple of extra parameters that were added to the <code nd="117">BeginMoveTo</code> method (in addition to the location parameter that was defined for <code nd="118">MoveTo</code>). Those are required in order to support the Asynchronous pattern. The first parameter is a callback delegate where we will specify what method needs to be called when the operation completes. The second parameter is a state parameter, and it’s usual to pass the proxy object so that the <i>end</i> operation can be called.</p>
		<p nd="119">OkeeDokee, we are ready to build a client that will exercise the AGVControl service. This time, we’ll build a Windows Forms client, to make the UI more conducive to what we want. The figure below shows the sample application that is included in the download. We are using the shell application, simply to demonstrate the asynchronous operation, so there's not much code included.</p>
		<p>
				<img height="301" alt="agvclient_screen.jpg" src="http://www.codeproject.com/WCF/WCFCommOptions_2/agvclient_screen.jpg" width="301" />
		</p>
		<p nd="120">All that we want to show here is that the <code nd="121">MoveTo</code> operation is indeed disconnected from the actual processing that is taking place at the service. To demonstrate that, the client code creates a separate thread that will update the status bar while the service is doing its thing and we are waiting for a reply (complete source is in the download for Part 1).</p>
		<div class="precollapse" id="premain14" style="WIDTH: 100%">
				<img id="preimg14" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="14" />
				<span id="precollapse14" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="122" preid="14"> Collapse</span>
		</div>
		<pre lang="cs" id="pre14" style="MARGIN-TOP: 0px" nd="123">public partial class Form1 : Form
{
    AGVControlProxy proxy = null;
    bool gotVehicle = false;
    bool gotResponse = false;
    ...
    private void btnMoveTo_Click(object sender, EventArgs e)
    {
        if (txtPosition.Text.Length &gt; 0)
        {
            int position = System.Convert.ToInt32(txtPosition.Text);
            proxy.BeginMoveTo(position, MoveCallback, proxy);
            <span class="cs-comment" nd="124">//Start a thread to display activity</span>
            gotResponse = false;
            Thread thread = new Thread(new ThreadStart(WaitingThread));
            thread.Start();
        }
    }
    private void MoveCallback(IAsyncResult ar)
    {
        ((AGVControlProxy)ar.AsyncState).EndMoveTo(ar);
        gotResponse = true;
    }
    private void WaitingThread()
    {
        int waitCount = 1;
        while (gotResponse == false)
        {
            toolStripStatusLabel1.Text = 
                <span class="cpp-string" nd="125">"Waiting..."</span> + waitCount.ToString();
            waitCount++;
            Thread.Sleep(100);
        }
        toolStripStatusLabel1.Text = <span class="cpp-string" nd="126">"Got response"</span>;
    }
    ...
}</pre>
		<p nd="127">Compile the client, and make sure you’ve got the service app running. Depress the Acquire button, and then the MoveTo button. You’ll see the status bar being updated until a response is received from the service. The callback method is what controls the termination of the thread so you also know when that occurred.</p>
		<p nd="128">That's it for now. In Part 3 (the last one!), we'll complete the asynchronous examples by implementing the same functionality as above but utilizing a Duplex communication pattern. We will also look at the fourth transport type, message-queueing, and where that may be used.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Al Alberto</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=62340">here</a> to view Al Alberto's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21453.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 18:01 <a href="http://www.cnitblog.com/MartinYao/articles/21453.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Communication options with WCF - Part 3</title><link>http://www.cnitblog.com/MartinYao/articles/21454.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 10:01:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21454.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21454.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21454.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21454.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21454.html</trackback:ping><description><![CDATA[
		<h2>Introduction to Part 3</h2>
		<p nd="1">In <a href="http://www.codeproject.com/winfx/WCF_CommOptions_part1.asp">Part 1</a> we defined what a service was and also created a simple WCF service and client to demonstrate the process of creating and consuming services under WCF. <a href="http://www.codeproject.com/winfx/WCF_CommOptions_part2.asp">Part 2</a> continued with some examples of the various communication patterns that can be easily implemented with WCF. In this article we'll continue by completing the examination of asynchronous communication and then examine the fourth transport supported under WCF, message queue. Note that all the source code for the examples presented are available in the download for Part 1.</p>
		<h2>July CTP Update</h2>
		<p nd="2">I recently had the <i>opportunity</i> to install the July CTP. I compiled the sample source to see if there were any surprises. It seems that svcutil has had a few changes. And there were some naming changes that will generate some syntax errors if you compile the sample source as is. You should be able to easily identify and fix them, IntelliSense works so much better these days.</p>
		<h2>Asynchronicity Part du</h2>
		<p nd="3">So, in the last article we developed an example of asynchronous communication based on the .Net Asynch pattern. That functionality was provided entirely on the client and mostly implemented by the .Net infrastructure. Now we'll provide the same functionality (asynchronous communication) but this time we’ll use a Duplex Communication pattern. In this case both the client and the service share in the work. The mechanics are essentially the same as we saw for the Publish/Subscribe except that here there is a 1:1 correlation between the request and the response. Whereas for the Publish/Subscribe pattern, one request (subscription) resulted in many responses (publications). And for Publish/Subscribe there can be any number of clients subscribing to one publication. But in both cases the request is disconnected from the response. Just to be clear, the request is a message and the response is a <b>separate</b> message. Each one though follows a request/reply pattern based on the <i>IsOneWay</i> property setting. Does that make it less clear? Well, I better leave it at that.</p>
		<p nd="4">For Duplex Communication the client makes a request of the service. The service will then, at some future time send a message back to the client with the results. The service also specifies what the client needs to ‘implement’ by providing a callback interface definition. And in processing the response, the  client behaves almost like a service. Let’s begin by defining a new version of the AGVController (call it <i>AGVControllerD</i>) that supports duplex communication so we can examine each of those points. Here is the revised service code.</p>
		<pre lang="cs" nd="5">[ServiceContract(Session = true, CallbackContract = typeof(IMoveToCallback))]
public interface IAGVControl
{
    ...
}
<span class="cs-comment" nd="6">//What the client needs to implement</span>
public interface IMoveToCallback
{
    [OperationContract(IsOneWay = true)]
    void MoveDone(int location);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class AGVControl : IAGVControl
{
    private IMoveToCallback callback = null;
    public AGVControl()
    {
        callback = OperationContext.Current.GetCallbackChannel<imovetocallback>();
    }
    ...
    public void MoveTo(int location)
    {
        <span class="cs-comment" nd="7">//This takes a long time...</span>
        Thread.Sleep(5000);
        <span class="cs-comment" nd="8">//Send a response</span>
        callback.MoveDone(location);
    }
    ...
}
</imovetocallback></pre>
		<p nd="9">First thing you’ll notice is that the <i>ServiceContract</i> now specifies a callback interface. The service needs this in order to know what to call. Next is the actual definition of the callback interface that the client <b>has</b> to implement. Since we already know <i>what</i> to call we just need to know <i>who</i> to call. And that is the purpose of the statement in the constructor. Each instance of the service object (we've specified an instancing of<i> per session </i>so <i>ServiceHost</i> will create an instance of the service class for each client session) remembers whom it is communicating with. This is important because the client assumes that the service is maintaining some state (a vehicle) in between method calls. Then when the appropriate time comes the service can make a call back to the client.</p>
		<p nd="10">One thing to note is that the callback interface is not limited to a single method. We have the same flexibility here as we had with the asynchronous client when we edited the proxy class. There we just implemented a <i>Begin/End</i> pair for one method. In this example we could define any or all of the service methods to also have a callback methods. To compare the two asynchronous examples in the same light, we are only defining a callback for the <i>MoveTo</i> operation. But we could certainly have defined a callback method for each of the service methods.</p>
		<p nd="11">Compile the service dll and then run the svcutil against it to generate the proxy class. This time we don’t specify the ‘/a’ option. The host is the same as before. All that you’ll have to do is change references to <i>AGVController</i> with <i>AGVControllerD </i>(likewise the namespace in the config file). </p>
		<p nd="12">Now let’s build the Duplex client. The main difference here is that we need to provide a class that implements the callback interface so that the service can talk back to us. The svcutil automatically includes the definition of the callback interface as part of the proxy. Just as we saw in the Publish/Subscribe example we need some kind of <i>hosting</i> on the client side in order for us to be able to receive the messages from the service. And that functionality is provided by the <i>InstanceContext</i> class just as before. Here is the code specific to the <i>duplex</i> client, everything else is the same as was for the <i>asynchronous</i> client.</p>
		<pre lang="cs" nd="13">public partial class Form1 : Form
{
    AGVControlProxy proxy = null;
    InstanceContext siteAGV = null;
    bool gotVehicle = false;
    public bool gotResponse = false;
    ...
    private void Form1_Load(object sender, EventArgs e)
    {
        siteAGV = new InstanceContext(new MoveCallbackHandler(this));
        proxy = new AGVControlProxy(siteAGV);
    }
    ...
}
public class MoveCallbackHandler : IAGVControlCallback
{
    Form1 parent;
    public MoveCallbackHandler(Form1 parent)
    {
        this.parent = parent;
    }
    public void MoveDone(int location)
    {
        parent.gotResponse = true;
    }
}
</pre>
		<p nd="14">If you run this version of the client (make sure the service is running) you should see the same results for both implementations. So if the service provides for asynchronous communication great, but even if it doesn't you can still implement it on the client with just a slight amount of code and revising the proxy accordingly. One more thing, if the requirements are right you could provide both forms of communication on the service and let the client choose.</p>
		<h2>Queuing it</h2>
		<p nd="15">The fourth communication transport mechanism supported by WCF is the message queue (to be fair, some of the literature describes peer-to-peer communication as another transport type). Message queues are not something new to WCF. It has been a part of Windows but was not available on all versions, which I thought was a bad thing. I think the message queue is an excellent communication mechanism for the right applications. When would you use a message queue? When you need it! A typical situation is for applications that may not have access to the service at all times. They may at times be <i>disconnected</i>.</p>
		<p nd="16">Another application might be a producer/consumer pattern where each may have different processing speeds. That is, suppose there are a number of data producers and each produces data in irregular cycles. It is possible for all of them to happen to send their data at the same time, which could overwhelm the consumer. So by using a message queue we are essentially spreading out the processing of the data over time. A typical example of this might be a voting application. The vote data might come in spurts because you can’t control when people will cast their vote. In other words the producers can do whatever/whenever they want, the consumer will never be overwhelmed (it will just take longer).</p>
		<p nd="17">Another important use of a message queue is to tie two disparate systems together. Two applications might be separated not only in time and space but also in platform and language and they can still talk to each other using a message queue. You might be thinking “that’s what a service provides”. But remember that on the other side of the queue there doesn’t have to be a service. Needless to say, the usage of a queue should match the application requirements pretty closely.</p>
		<p nd="18">To finish up the WCF journey let’s take a look at an implementation of a message queue. The application is an operator console utility that displays the state of various modules that might make up a system. The figure below represents a material handling system that has some robots, AGVs, conveyors, etc. Each of the modules in the system sends their state information to a central monitor application so that the operator can see at any time what each module is doing. Or if something failed s/he can see what immediately preceded the failure. The client modules themselves might be running at different locations and could have been implemented in different languages.</p>
		<p nd="19">Each module is actually sending its state information to a queue and the monitor service processes the messages that are in the queue. As you’ll see, the client code does not provide any indication that the message is actually going to a queue. At the risk of overstating it...to me that is the beauty of WCF. Additionally, since the use of a message queue is simply defined as the endpoint, at some future time the system could be changed so that it did not use a queue but made direct calls to a service and the client code would not change at all. All that would need to change would be the configuration files to specify a different endpoint.</p>
		<p>
				<img height="162" alt="mathandling.gif" src="http://www.codeproject.com/WCF/WCF_CommOptions_part3/mathandling.gif" width="385" />
		</p>
		<p nd="20">So a message queue behaves like Email, somebody sends you a message and it’s held somewhere until you go get it. Previously, if you implemented a message queue you had two choices in how you processed the messages. You could set up an event handler and the system would call your delegate any time there was a message in the queue or you could poll the message queue if you were looking for a specific message type. Under WCF using a message queue is completely transparent. Because outside of setting up the actual queue, there is no syntactical difference from the other transport types. On the client side you just make a method call as always. And on the service side you define an interface and a class that implements the interface just as you would for any of the other transport types. The only difference between this transport and the others is that a message queue has to exist in order to use it. OK, lets look at some of the code.</p>
		<p nd="21">The <i>QState Monitor</i> is an MDI app that creates a window for each module that sends it a message. Any subsequent messages that are received from the same module will be displayed in the same window. The message is just two strings, one is the name of the module and the second string is the state information to be displayed. The download contains all of the code so we’ll just look at the WCF side of things. First let’s define the service.</p>
		<pre lang="cs" nd="22">[ServiceContract]
public interface IQSMonitor
{
    [OperationContract(IsOneWay = true)]
    void ModState(string strMod, string strState);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class QSMonitor : IQSMonitor
{
    QStateWnd parent = null;
    public QSMonitor(QStateWnd parent)
    {
        this.parent = parent;
    }
    public void ModState(string strMod, string strState)
    {
        parent.ProcessMessage(strMod, strState);
    }
}
</pre>
		<p nd="23">As you can see there is nothing in the code that indicates that a message queue is being used. The actual message <i>queue</i> can be created programmatically or manually using the Computer Management Console (in Administrative Tools). If you create the queue manually then you can change the transport without affecting the code at all. Here’s the code for starting up the service. </p>
		<pre lang="cs" nd="24">...
ServiceHost host = null;
QSMonitor handler;

public QStateWnd()
{
<span class="cs-comment" nd="25">//          To create the queue programmatically uncomment these lines.</span><span class="cs-comment" nd="26">//          string queueName = ConfigurationManager.AppSettings["queueName"];</span><span class="cs-comment" nd="27">//          if (!MessageQueue.Exists(queueName))</span><span class="cs-comment" nd="28">//              MessageQueue.Create(queueName, false);</span>

    handler = new QSMonitor(this);

    <span class="cs-comment" nd="29">// Get base address for Msmq</span>
    string baseAddress = ConfigurationManager.AppSettings[<span class="cpp-string" nd="30">"baseAddress"</span>];
    host = new ServiceHost(handler, new Uri(baseAddress));
    <span class="cs-comment" nd="31">// Open the ServiceHostBase to create listeners and start listening for </span><span class="cs-comment" nd="32">// messages.</span>
    host.Open();
    ...
}
</pre>
		<p nd="33">There is nothing different here than the code required for the other transports that we used previously. The <i>handler</i> is the service class. We pass the <i>handler</i> a reference to the main window (<i>QStateWnd</i>) so that it can tell the main window when messages are received. The functionality to be performed for each message is actually defined in the <i>QStateWnd</i> class which is to display the message. Finally we just have to create a <i>ServiceHost</i> to host the service just as before.</p>
		<p nd="34">The rest of the code is concerned with the mechanics of the application and is pretty straightforward. When the service class receives a message it just passes it to the main window, MDI frame. MDI frame classes maintain a list of the child windows that they own. We use that facility to keep track of what modules have already sent messages. Essentially we just iterate through the list of child windows and check their title bar text. When an MDI child window is created we assign the module name to the window title. This way we can match up the module that sent the message to a specific window.</p>
		<p nd="35">That’s it, we create a new child window if the module name does not exist on any current window and if there already is an existing window we just add the state information to the window.</p>
		<p>
				<img height="575" alt=" qstate.GIF" src="http://www.codeproject.com/WCF/WCF_CommOptions_part3/qstate.GIF" width="551" />
		</p>
		<p nd="36">Finally here’s what the config file looks like when using a message queue.</p>
		<div class="precollapse" id="premain4" style="WIDTH: 100%">
				<img id="preimg4" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="4" />
				<span id="precollapse4" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="37" preid="4"> Collapse</span>
		</div>
		<pre lang="xml" id="pre4" style="MARGIN-TOP: 0px" nd="38">&lt;configuration&gt;
  &lt;appSettings&gt;
    &lt;!-- use appSetting to configure MSMQ queue name --&gt;
    &lt;add key="queueName" value=".\private$\QSMonitor" /&gt;
    &lt;add key ="baseAddress" value="net.msmq://localhost/private/QSMonitor"/&gt;
  &lt;/appSettings&gt;

  &lt;system.serviceModel&gt;
    &lt;services&gt;
      &lt;service 
          name="QSMonitor"&gt;
        &lt;!-- Define NetMsmqEndpoint --&gt;
        &lt;endpoint address="net.msmq://localhost/private/QSMonitor"
                   binding="netMsmqBinding"
                   bindingConfiguration="Binding1" 
                  contract="IQSMonitor" /&gt;
      &lt;/service&gt;
    &lt;/services&gt;

    &lt;bindings&gt;
      &lt;netMsmqBinding&gt;
        &lt;binding name="Binding1" exactlyOnce="false"&gt;
          &lt;security mode="None" &gt;
          &lt;/security&gt;
        &lt;/binding&gt;
      &lt;/netMsmqBinding&gt;
    &lt;/bindings&gt;
  &lt;/system.serviceModel&gt;
&lt;/configuration&gt;
</pre>
		<p nd="39">A couple of comments on the config file above because it’s pretty powerful stuff. I have not mentioned the binding classes much because there is just so much stuff there and the default classes provided by WCF have been sufficient. But you can create your own custom binding as well as customize the default binding classes to suit your requirement. Providing that capability programmatically is a very flexible feature but not surprising. What <b>is</b> really neat is that you have the same capabilities when using the configuration file route. In the above file we specified that we wanted to use the default <i>netMsmqBinding</i> binding. You’ll notice that there is an additional <i>bindings</i> section also in the file and that there is an additional property in the endpoint definition labeled-<i>bindingConfiguration</i>. What is happening here is that we are saying ‘use the default <i>netMsmqBinding</i> binding but with the following changes’. So if some of the default options of a particular binding are not suitable you can modify it <b>from the configuration file</b>! Why is this so amazing to me? It could be that I'm amazed by simple things or it could be that this gives us just a little more flexibility in our solution. If we can change something without having to re-compile, that's convenient. If it's already deployed, it's a life saver. And finally, there's a lot of functionality behind these options that we did not have to write a single line of code.</p>
		<p nd="40">So why <b>did</b><i></i>I need to revise the default behavior? Well it wasn’t working. I tried a couple of options and then did some searching on the Net. It turns out that security is turned on by default for this binding. Security was not a requirement for me at that point (I’m sure I’ll have to cross that bridge soon enough). In any case all I had to do was to override the default settings to turn security off. That’s how we learn things most of times isn’t it? Through mistakes that lead to discoveries.</p>
		<p nd="41">Anyway back to the current example. On the client side all that is needed is a simple proxy and the appropriate entries in the config file. Since we don’t have a service dll and since our service does not have a MEX endpoint, our only option is to roll our own proxy by hand. By the way, you can even create your proxies <b>dynamically</b> by using the <i>ChannelFactory</i> class. Generally there’s more than one way to do something in WCF. How’s that for flexibility?</p>
		<p nd="42">A quick side note. If you do edit the proxies make sure that your parameter names match up on both sides. Parameters are identified by name, so if there is just the smallest typo, you’ll spend some time looking for why messages are not being processed. Even though they are being sent correctly <b>and </b>there are no exceptions. A free debugging option when using a message queue is the message queue itself! You can examine your messages in the message queue, if the service is not running. For the other transports you need some other utilities to be able to see the messages as they are crossing the wire. </p>
		<p nd="43">Here’s the proxy and config file that the clients will need. There is a sample client in the download source.</p>
		<pre lang="cs" nd="44">[ServiceContract]
public interface IQSMonitor
{
[OperationContract(IsOneWay = true)]
void ModState(string strMod, string strState);
}
public interface IQSMonitorChannel : IQSMonitor, 
                                     System.ServiceModel.IClientChannel
{
}
public partial class QSMonitorProxy : System.ServiceModel.ClientBase<iqsmonitor>, 
                                      IQSMonitor
{
    public QSMonitorProxy()
    {
    }
    public void ModState(string strMod, string strState)
    {
        base.InnerProxy.ModState(strMod, strState);
    }
}
</iqsmonitor></pre>
		<pre lang="xml" nd="45">&lt;configuration&gt;
  &lt;system.serviceModel&gt;
    &lt;client&gt;
      &lt;endpoint name=""
                address="net.msmq://localhost/private/QSMonitor" 
                  binding="netMsmqBinding" 
                  bindingConfiguration="Binding1" 
                contract="IQSMonitor" /&gt;
    &lt;/client&gt;
    &lt;bindings&gt;
      &lt;netMsmqBinding&gt;
        &lt;binding name="Binding1" exactlyOnce="false"&gt;
          &lt;security mode="None"&gt;
          &lt;/security&gt;
        &lt;/binding&gt;
      &lt;/netMsmqBinding&gt;
    &lt;/bindings&gt;
  &lt;/SYSTEM.SERVICEMODEL&gt;
&lt;/configuration&gt;
</pre>
		<p nd="46">If you feel like experimenting, make the following change to the config file for both the service and the client.</p>
		<pre lang="xml" nd="47">    &lt;add key ="baseAddress" value="net.pipe://localhost/QSMonitor"/&gt;
    ...
    &lt;endpoint address="net.pipe://localhost/QSMonitor"
               binding="netNamedPipeBinding"
              contract="IQSMonitor" /&gt;
    ...
</pre>
		<p nd="48">Re-start the service and the client. You should see no difference in the results but what is happening under the cover changed quite a bit.</p>
		<h2>Heigh-Ho Silver Away!</h2>
		<p nd="49">There you have it, just enough knowledge on WCF to make you slightly dangerous. There's lots more to explore, there's the security side of communication, all the binding options, peer-to-peer and broadcast messages, and maybe even defining custom transports. You never know when that special requirement comes up that just does not fit the mold. So until next time Kemo Sabe...</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Al Alberto</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=62340">here</a> to view Al Alberto's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21454.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 18:01 <a href="http://www.cnitblog.com/MartinYao/articles/21454.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Communication options with WCF - Part 1</title><link>http://www.cnitblog.com/MartinYao/articles/21452.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 10:00:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21452.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21452.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21452.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21452.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21452.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/WCF/WCF_CommOptions_part1/WCF_CommOptions_src.zip">Download source - 90.2 Kb</a>
				</li>
		</ul>
		<h2>Introduction</h2>
		<p nd="1">I’m impressed! And that’s not an easy thing to do. I’ve been playing with the CTP (February 2006) release for a while now, and believe it or not, I think we may actually have something here. I have not had the time to explore the other ‘Foundation’ members (of WinFX), but from what I’ve seen of the Windows Communication Foundation (WCF), it really is a very nice package. Yes, I know that each time there is a new technology released, there is always some <i>neat</i> features. But in the past, I always thought that we were paying more (in complexity) than what we were getting (in functionality), can you spell COM? This time, it <i>feels</i> like we are getting the whole thing for free. It’s possible that it may be just my naïve perspective, but hey it’s all about me anyway.</p>
		<p nd="2">I like to preface my articles with a disclaimer. The content in this article is simply my impressions and interpretation, and it should be read in that context. Even though the prose may at times seem like I know what I’m talking about, any similarity to actual facts may simply be coincidence. Enjoy the journey.</p>
		<h2>Why can’t we just talk?</h2>
		<p nd="3">The minute we stepped out of the DOS box, we realized that we were not alone any more, and we needed to learn to get along and communicate with the other inhabitants of the virtual world. DDE, OLE, COM, DCOM, and Remoting have been some of the attempts at providing mechanisms for two applications to be able to talk to each other. Remember how OLE and COM were described when first introduced? As the ‘foundation for all future products’. With hindsight, we can see that they were really just baby steps. Each one solved only a small part of the whole problem. So if they were baby steps, then WCF is certainly a giant leap. WCF provides a complete solution to the communication problem. And it does it with elegance <b>and</b> simplicity. Can you tell that I’m just a <i>little</i> enthusiastic?</p>
		<p nd="4">Whether your requirement is to communicate with another module on the same machine, or another module implemented in a different language, or you need to communicate with a module that’s on a machine on the other side of the world, or you want to communicate with a module running on a different platform, or even communicate with a module that’s not even running!, yup, you can do it under WCF.</p>
		<p nd="5">In my opinion, the beauty of WCF is that it is an ‘all-inclusive’ solution. It is the first one to provide a complete end-to-end solution covering the scope and depth of the problem. It is also the simplest from a programmer's point of view; you are always just making a method call. Yeah, I am sure that there is quite a bit of magic going on under the covers to support that simplicity of use.</p>
		<p nd="6">Now, I have not explored every nook and cranny, or option, or possibility, but from what I’ve seen, it’s an excellent solution. At least for me, and as I said before…</p>
		<h2>What is a service?</h2>
		<p nd="7">Let’s see how much trouble I can get myself into here. I think that, in its most elemental form, a service is simply some functionality (code) running in some <b>external</b> process that is made available to other processes <b><i>in a standard way</i></b>. That’s pretty much the crux of it, except that <b><i>in a standard way</i></b> also encompasses platform and language neutrality.</p>
		<p nd="8">So, by the above definition, a service really has two parts. First is the code that must be running somewhere in order to provide some functionality to clients. And second, there must be some <i>generic mechanism</i> that can be used by any process, regardless of the platform, language, or locality, that makes the service accessible. That <i>generic mechanism</i> has turned out to be XML and SOAP. Of course, there are some additional facilities required in order for a client to be able to <i>know</i> (or <i>discover</i>) what functionality the service makes available. But I think of those as supporting technologies.</p>
		<p nd="9">There is also some glue that is required in order to tie the two parts of a service together. That glue is the code that will support the communication medium (transport) that is being used by the service and the client to talk to each other. Being lazy…I mean smart, we’ve come up with some generic glue also. This way, each service implementation does not have to re-invent the wheel. For Web Services, the generic glue is a <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/WCF/WCF_CommOptions_part1.asp#" target="_blank" itxtdid="3059973">Web Server</a>. So, a Web Server provides a <i>hosting environment</i> for services that use HTTP as their transport mechanism. I would also like to suggest that Web Services are a special implementation of a <i>service </i>as defined above.</p>
		<p nd="10">Here are the things that we will be examining in the rest of this article. How do you define a service? How do you implement a service? How do you host a service? How do you access and use a service? Once we have the basics nailed down, we’ll look at some of the more complex communication options that WCF facilitates.</p>
		<h2>So what is WCF?</h2>
		<p nd="11">Here is what WCF is for me:</p>
		<blockquote>
				<p nd="12">WCF is an inter-application communication platform. It provides a common API that <b>hides</b> the underlying communication medium. It <b>can</b> be platform neutral, and it provides for <b>locality indifference</b>. Essentially, under WCF, I, as a programmer, do not need to know or care <b><i>where</i></b> the other end is or <b><i>how</i></b> the actual communication is taking place. To me, that is beautiful!</p>
		</blockquote>
		<p nd="13">WCF is <i>services</i> based technology. It has, as its roots, Web Services, and thus XML and SOAP are its core technologies. WCF took the concept of Web Services and super-charged it. Much of the look and feel of WCF behaves like traditional Web Services. In fact, I like to think of WCF services as Web Services on steroids. You define WCF services much like Web Services. You can interrogate a known service for its methods with the same protocols that are available for Web Services. And you have very similar infrastructure requirements as are for Web Services. The main difference is that WCF has expanded the transports that are available to include TCP/IP, IPC (named pipes), and message queue-ing, in addition to HTTP. I think the focus of Web Services is to solve the <i>interoperability</i> problem, and the focus of WCF is to solve the much broader <i>communication</i> problem. And it has done this while still maintaining a uniform API as well providing more efficient mechanisms.</p>
		<p nd="14">The most important feature from a developer perspective is that you don’t have to be concerned with what or how you are communicating. The code is the same, no matter what the final transport mechanism or locality of service might be.</p>
		<h2>Service Contracts-exposing functionality</h2>
		<p nd="15">Our WCF journey starts with how services define the functionality that they expose. Much of the infrastructure required to implement services under WCF is specified using <i>declarative programming</i>. That means, using attributes to specify functionality. The following shows how to declare an interface that will be exposed as a service:</p>
		<pre lang="cs" nd="16">[ServiceContract]
public interface ILocalTime
{
    [OperationContract]
    string GetLocalTime();
}

public class LocalTimeService : ILocalTime
{
    ...
}</pre>
		<p nd="17">The <code nd="18">ServiceContract</code> attribute specifies that the interface defines the functionality of a service. <code nd="19">OperationContract</code> is used to decorate each method that is to be exposed as part of the service. That is all that is required to create a WCF service. Just slightly more is required to actually deploy the service, which we’ll cover later on.</p>
		<p nd="20">By the way, you don’t have to use interfaces when implementing a service, just like you don’t have to use an interface to define a class. You <b>do</b> have to specify what you want exposed through a service, explicitly. You can define anything else you want or need as part of the interface, but only methods, and only methods that get decorated with <code nd="21">[OperationContract]</code>, will be exposed by the service.</p>
		<h2>Data Contracts-exposing data types</h2>
		<p nd="22">WCF also allows you to expose custom data types so that you are not restricted to simple data types of the CLR. These are simple structs with no methods associated with it. This can be a little confusing sometimes, because the same syntax is used for both services as well as for CLR definitions. Here’s an example of a <code nd="23">DataContract</code> that we will use.</p>
		<pre lang="cs" nd="24">[DataContract]
public class SensorTemp
{
    [DataMember]
    public int probeID;
    [DataMember]
    public int temp;
}</pre>
		<p nd="26">
				<code nd="25">DataContract</code> specifies the data type that you are exposing and, <code nd="27">DataMember</code> specifies the members that are part of the data type. As is the case with <code nd="28">ServiceContract</code>, you have to explicitly declare which members are to be exposed to external clients, using <code nd="29">DataMember</code>. What that means is that you can include anything else that you may want (or need) as part of the class definition, but only the members decorated with <code nd="30">DataMember</code> will be visible to clients.</p>
		<h2>Coding options in WCF</h2>
		<p nd="31">As we saw above, one of the options available to specify functionality under WCF is to use attributes. Attributes are translated by the compiler to generate much of the infrastructure required by WCF in order for us to create and use services.</p>
		<p nd="32">The second way you can specify many of the options is through configuration files. This allows you to make changes without having to re-compile. Many of the WCF classes will automatically use default values from the config file. Here’s an example of an endpoint specified using config data (<i>endpoints</i> will be described shortly). First, the config file, then the code statement referencing the config file data:</p>
		<pre lang="xml" nd="33">&lt;endpoint name ="LocalTimeService"
        address="net.pipe://localhost/LocalTimeService"
        binding="netNamedPipeBinding"
        contract="ILocalTime" /&gt;</pre>
		<pre lang="cs" nd="34">LocalTimeProxy proxy = new LocalTimeProxy(<span class="cpp-string" nd="35">"LocalTimeService"</span>);</pre>
		<p nd="36">Finally, the third way of coding functionality is, of course, programmatically. Many of the things that you can do via attributes or config files can also be done programmatically. Here is the previous endpoint, defined programmatically:</p>
		<pre lang="cs" nd="37">Uri baseAddress = new Uri(ConfigurationManager.AppSettings[<span class="cpp-string" nd="38">"basePipeTimeService"</span>]);
baseAddress = new Uri(ConfigurationManager.AppSettings[<span class="cpp-string" nd="39">"basePipeTimeService"</span>]);
serviceHost.AddServiceEndpoint(typeof(ILocalTime), 
            new NetNamedPipeBinding(), baseAddress);</pre>
		<h2>Endpoints</h2>
		<p nd="40">Endpoints are the ‘identity’ of a service. They define all the information that we need in order to establish and communicate successfully with a service. Endpoints are made up of three pieces of information: <b>A</b>ddress, <b>B</b>inding, and <b>C</b>ontract. The <i>address</i> is obviously the location of the service, such as ‘net.pipe://localhost/LocalTimeService’. The <i>binding</i> specifies security options, encoding options, and transport options, which means a lot of options! Luckily, there is a collection of pre-defined bindings provided with WCF that we can use to make our life simpler. And finally, the <i>contract</i> is the actual interface that the service implements.</p>
		<h2>Implementing a WCF Service</h2>
		<p nd="41">So, a service is nothing more than a regular class that gets decorated with some special attributes. The attributes are then translated by the compiler to generate the special infrastructure code required to expose the class as a service to the world. In the following code, we first define an interface that has one method that returns the local time of where the service has been deployed. The <code nd="42">LocalTimeService</code> class then implements the interface, and thus exposes the functionality to the world, or at least to whomever is interested.</p>
		<pre lang="cs" nd="43">[ServiceContract]
public interface ILocalTime
{
    [OperationContract]
    string GetLocalTime();
}

[ServiceBehavior(InstanceContextMode = 
                 InstanceContextMode.PerCall)]
public class LocalTimeService : ILocalTime
{
    public string GetLocalTime()
    {
        return DateTime.Now.ToShortTimeString();
    }
}</pre>
		<p nd="44">That’s all that’s needed to create a WCF service. If you compile the above code into a DLL (a library), you will have created a service. Of course, there is a little more needed in order to have something that’s useable. We need two other pieces in order to complete the service. We need something that will be able to load the service DLL when a client requests the functionality of the service. And we need something that will be able to listen on a communication port, and look through everything that is being received to see if it matches what we are responsible for, our service contract.</p>
		<h2>Deploying a WCF Service</h2>
		<p nd="45">There are a number of ways to deploy our service. First, if we implement our service to support HTTP, then we could deploy our service just like a regular Web Service, using IIS. If we want to support some of the other transports, then we could deploy the service using Windows Activation Service (WAS), which is an enhancement available in IIS 7.0. If either of these is not suitable or we want more control over the service, then the other solution is to build our own hosting environment by using <code nd="46">ServiceHost</code>. <code nd="47">ServiceHost</code> is a class available in WCF to host services, almost like a mini IIS, just for our service. We can implement <code nd="48">ServiceHost</code> in any housing available under Windows, in a console app, a Windows executable, or a Windows service (formerly NT service).</p>
		<p nd="50">
				<code nd="49">ServiceHost</code> will listen on whatever channel we specify for our service, using whatever protocol we specify, and call our service whenever a client request for our specific service comes in. That’s a lot of bang for just a couple of lines of code. All that we need to do is tell <code nd="51">ServiceHost</code> the endpoint that it is responsible for and the class that it should instantiate when a matching message is received. Here’s the code that’s required to host the <code nd="52">LocalTimeService</code> in a console app:</p>
		<pre lang="cs" nd="53">class TimeService
{
    static void Main(string[] args)
    {
        Uri baseAddress = new Uri(
          ConfigurationManager.AppSettings[<span class="cpp-string" nd="54">"basePipeTimeService"</span>]);
        ServiceHost serviceHost = new ServiceHost(typeof(LocalTimeService),
                                                         baseAddress);
        serviceHost.Open();
        Console.WriteLine(<span class="cpp-string" nd="55">"Service is running....press any key to terminate."</span>);
        Console.ReadKey();
        serviceHost.Close();
    }
}</pre>
		<p nd="56">You can now compile the service. However, if you try to run it, you’ll get an error message indicating that you haven’t provided <code nd="57">ServiceHost</code> with an endpoint. As we saw above, you can specify endpoints either programmatically, or by using the configuration file. The nice thing about using configuration is that you can change it at any time and you don’t have to recompile. As we’ll see later, you can specify multiple endpoints for a service, depending on the clients that you want to support. And if at some point later, you decide to not support a specific transport, you just have to edit the configuration file.</p>
		<p nd="58">Here’s the config file that we’ll need for the <code nd="59">LocalTimeService</code>:</p>
		<pre lang="xml" nd="60">&lt;configuration&gt;
  &lt;appSettings&gt;
    &lt;add key="basePipeTimeService" 
         value="net.pipe://localhost/LocalTimeService" /&gt;
  &lt;/appSettings&gt;
  &lt;system.serviceModel&gt;
    &lt;services&gt;
      &lt;service name="LocalTimeService"&gt;
        &lt;endpoint 
           address="" 
           binding="netNamedPipeBinding" 
           contract="ILocalTime" 
         /&gt;
      &lt;/service&gt;
    &lt;/services&gt;
  &lt;/system.serviceModel&gt;
&lt;/configuration&gt;</pre>
		<p nd="61">Let’s examine the entries in the config file. You should note that there could be as many entries as needed. For example, there could be several endpoints that the service supports (different transports). There is also only one service being specified in this example, but there could be several services provided in the same housing. You can see that the endpoint has three properties: <i>address</i>, <i>binding</i>, and <i>contract</i>. The binding indicated is referencing the standard <code nd="62">netNamedPipeBinding</code> provided in WCF. There are various default binding classes provided for each transport. You can see the options for each in the docs.</p>
		<p nd="63">I will say here that you too will encounter the “<i>zero application (non-infrastructure) endpoints</i>” exception at some point. There won’t be too many clues as to what exactly is not matching up, so you’ll have to scrutinize the text. Make sure that you have the correct namespaces specified.</p>
		<p nd="64">Now you can execute the application, and the service will be available to any client that knows how to communicate with it.</p>
		<h2>Proxies</h2>
		<p nd="65">Just by saying we want to go to New York and we are going to go by car, does not get us there. We need a car to actually get us there. Having a completely defined endpoint is not enough. We need something (code) that will actually take the endpoint as a parameter and allow us to do what we want to do, call a method. And that something is a proxy.</p>
		<p nd="66">As was the case in the past with COM, proxies take care of all the low level plumbing (serializing and packaging our parameters) so that we just need to make the call. We don’t care how they are forced through the ‘spigot’ or how they are pulled out on the other side.</p>
		<p nd="67">And as we’ve also had in the past, there is a utility that will create the proxies for us. However, this is one area of WCF that needs some improvement. And I’m guessing this functionality will become incorporated into Visual Studio in future releases. At least, I would hope so. To create a proxy, you need to use the command line utility, <b>svcutil</b>, which has a number of switches that are not all or well documented. But hey, I’m not complaining, it’s a small inconvenience for a whole lot of major improvements. And it’s still only Beta.</p>
		<p nd="68">So, you run <b>svcutil</b> against your service DLL and <b>bam</b>! You got your proxy class. There are other options, like if the service has a MEX endpoint, you can direct it to the service, and it will extract the service information dynamically from the service. This is essentially the same functionality provided through Studio when creating a Web Service, and we use the ‘Add Web Reference’ dialog. What I really want is for Visual Studio to automatically generate the proxies since it has all the information in the source files to begin with! But as I said, I’m not complaining. ;)</p>
		<p nd="69">Currently then, creating the proxy is a two step process. First, you run <b>svcutil</b> against your service DLL, which will create the schema (XSD) and WSDL files. Then, you invoke <b>svcutil</b><i>again</i>, but this time, you run it against the output it just created (<i>*.xsd</i>, <i>*.wsdl</i>).</p>
		<p nd="70">The <b>svcutil</b> will generate ‘<i>output.cs</i>’ as the default file name, unless you specify otherwise, I normally just rename it. There are also options to just generate <code nd="71">DataContracts</code> or just <code nd="72">ServiceContracts</code>, and also an option to generate a client config file. Here’s the proxy file for the <code nd="73">LocalTimeService</code>, with some portions edited for readability. There’s not much there, since all the magic occurs in <code nd="74">ClientBase</code>.</p>
		<pre lang="cs" nd="75">[ServiceContract]
public interface ILocalTime
{
    [OperationContract]
    string GetLocalTime();
}

public interface ILocalTimeChannel : ILocalTime, 
                 System.ServiceModel.IClientChannel
{
}

public partial class LocalTimeProxy : 
       System.ServiceModel.ClientBase&lt;ILocalTime&gt;, ILocalTime
{
    
    public LocalTimeProxy()
    {
    }
    public string GetLocalTime()
    {
        return base.InnerProxy.GetLocalTime();
    }
}</pre>
		<h2>Consuming a WCF Service</h2>
		<p nd="76">So now, the next logical step is to build a client that knows how to consume the service that is being provided. The client code only needs two things: the proxy that allows it to communicate with the service, and the endpoint to the service. Here’s one version of a client that consumes the <code nd="77">LocalTimeService</code>:</p>
		<div class="precollapse" id="premain9" style="WIDTH: 100%">
				<img id="preimg9" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="9" />
				<span id="precollapse9" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="78" preid="9"> Collapse</span>
		</div>
		<pre lang="cs" id="pre9" style="MARGIN-TOP: 0px" nd="79">class Client
{
    public bool keepClocking = true;
    LocalTimeProxy proxy = null;
    public Client()
    {
        proxy = new LocalTimeProxy();
    }
    public void ClockingThread()
    {
        while (keepClocking)
        {
            Console.WriteLine(proxy.GetLocalTime());
            Thread.Sleep(1000);
        }
        proxy.Close();
    }
    static void Main(string[] args)
    {
        Client client = new Client();

        <span class="cs-comment" nd="80">//Create a separate thread to request the time once a second</span>
        Thread thread = new Thread(new ThreadStart(client.ClockingThread));
        thread.Start();

        Console.WriteLine(<span class="cpp-string" nd="81">"Service is running....press"</span> + 
                          <span class="cpp-string" nd="82">" any key to terminate."</span>);
        Console.ReadKey();

        client.keepClocking = false;
        <span class="cs-comment" nd="83">//wait 2 seconds</span>
        Thread.Sleep(2000);
    }
}</pre>
		<p nd="84">Not much to this client. All that it's doing is making a call to the service method <code nd="85">GetLocalTime()</code>, once a second. As you can see, the client code has no indication as to what or where the other end of the method call is. Nor what mechanism is actually being used to make the connection. It is just a simple class method call! As we look at other examples, you'll keep seeing the simplicity of coding that is provided under WCF. And here is the config file that specifies the endpoint to the service which is required by the client.</p>
		<pre lang="xml" nd="86">&lt;configuration&gt;
  &lt;system.serviceModel&gt;
    &lt;client&gt;
      &lt;endpoint name ="LocalTimeService"
                address="net.pipe://localhost/LocalTimeService"
                binding="netNamedPipeBinding"
                contract="ILocalTime" /&gt;
    &lt;/client&gt;

  &lt;/system.serviceModel&gt;
&lt;/configuration&gt;</pre>
		<p nd="87">Compile and run the client. Start several instances. Just make sure that the service is started before you start the clients, otherwise nobody will be listening.</p>
		<p nd="88">That’s it for the basics in getting services listening and consuming services. In Part 2, we'll build some examples that will demonstrate WCF's support for the various communication patterns. The download includes all of the source code for the sample applications described in the articles.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Al Alberto</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=62340">here</a> to view Al Alberto's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21452.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 18:00 <a href="http://www.cnitblog.com/MartinYao/articles/21452.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hosting WCF services in a Windows Forms Application</title><link>http://www.cnitblog.com/MartinYao/articles/21451.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 09:58:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21451.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21451.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21451.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21451.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21451.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/WCF/wcfservice/wcfservice_demo.zip">Download demo project - 19.6 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/WCF/wcfservice/wcfservice_code.zip">Download source - 31.2 Kb</a>
				</li>
		</ul>
		<p>
				<img height="306" alt="Sample Image" src="http://www.codeproject.com/WCF/wcfservice/wcfservice2.jpg" width="600" />
		</p>
		<h2>Introduction</h2>
		<p nd="1">This article discusses about some features of the service model of WCF which is part of .NET 3.0. It demonstrates how to host multiple instances of the same service and contract in a Windows Forms application. Those services are then consumed by a client form belonging to the same application and by the same client form but created from another application. It demonstrates how to create singleton services and consume them without using any configuration file. 
</p>
		<h2>Background</h2>
		<p nd="2">As I was abroad and I couldn't carry with me my model train control station, I decided to write a simple simulator so I could continue to work on the bigger software I'm designing and that drives the control station. 
</p>
		<p nd="3">First of all, I had to create a simple simulation of the locomotives that run on the track and that the control station can pilot. As I recently attended a WCF training, I thought that it would be a good practice to write this part using the new WCF service model. Years ago, I would have written some COM servers to simulate the locomotives, but technology has evolved, and the new service model developed for .NET 3.0 is far more powerful than was COM. </p>
		<p nd="4">In fact, what I want to simulate is the locomotive DCC decoder. This little piece of hardware is used to control a locomotive on a track referenced by its address. The control station sends commands to change the speed, the direction, and switch the lights. It also can read the status of the device by its address. It can easily be simulated by a singleton service. There is no problem of scalability as the locomotives are going to run on a single machine and their number won't be big, neither the connections to a given locomotive. First, I wanted to use a <code nd="5">Sharable</code> instance for the service, but this instance mode has disappeared in the latest release of WCF…</p>
		<p>
		</p>
		<h2>Using the code: Service contract</h2>
		<p nd="6">Here is how the interface to simulate a simple locomotive decoder looks like: </p>
		<div class="precollapse" id="premain0" style="WIDTH: 100%">
				<img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" />
				<span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="7" preid="0"> Collapse</span>
		</div>
		<pre lang="cs" id="pre0" style="MARGIN-TOP: 0px" nd="8">[ServiceContract]
public interface IDCCLocomotiveContract
{    
    <span class="cs-comment" nd="9">/// Gets the running direction of that locomotive</span><span class="cs-comment" nd="10">/// &lt;/summary&gt;</span><span class="cs-comment" nd="11">/// &lt;returns&gt;&lt;/returns&gt;</span>
    [OperationContract(IsOneWay=false)]
    Direction GetDirection();
    <span class="cs-comment" nd="12">/// &lt;summary&gt;</span><span class="cs-comment" nd="13">/// Changes the direction of that locomotive</span><span class="cs-comment" nd="14">/// &lt;/summary&gt;</span><span class="cs-comment" nd="15">/// &lt;param name="direction"&gt;New direction&lt;/param&gt;</span>
    [OperationContract]
    void ChangeDirection(Direction direction);
    <span class="cs-comment" nd="16">/// &lt;summary&gt;</span><span class="cs-comment" nd="17">/// Sets the new speed value</span><span class="cs-comment" nd="18">/// &lt;/summary&gt;</span><span class="cs-comment" nd="19">/// &lt;param name="speed"&gt;Speed value (0 - 28)&lt;/param&gt;</span>
    [OperationContract]
    void SetSpeed(byte speed);

    <span class="cs-comment" nd="20">/// &lt;summary&gt;</span><span class="cs-comment" nd="21">/// Gets the current locomotive speed</span><span class="cs-comment" nd="22">/// &lt;/summary&gt;</span><span class="cs-comment" nd="23">/// &lt;returns&gt;Speed value&lt;/returns&gt;</span>
    [OperationContract(IsOneWay=false)]
    byte GetSpeed();

    <span class="cs-comment" nd="24">/// &lt;summary&gt;</span><span class="cs-comment" nd="25">/// Switch the main light</span><span class="cs-comment" nd="26">/// &lt;/summary&gt;</span><span class="cs-comment" nd="27">/// &lt;param name="state"&gt;ON if true, OFF if false&lt;/param&gt;</span>
    [OperationContract]
    void SwitchLight(bool state);

    <span class="cs-comment" nd="28">/// &lt;summary&gt;</span><span class="cs-comment" nd="29">/// Gets the main light status</span><span class="cs-comment" nd="30">/// &lt;/summary&gt;</span><span class="cs-comment" nd="31">/// &lt;returns&gt;true if ON, false otherwise&lt;/returns&gt;</span>
    [OperationContract(IsOneWay=false)]
    bool GetLightState();
}</pre>
		<p>
		</p>
		<h2>Service implementation</h2>
		<p nd="32">WCF allows managing the instance behavior just by using an attribute parameter for the class that implements the service. There are three different instance modes. When using the <code nd="33">PerCall</code> mode, every time you call the service, you get a fresh instance. That means that all data are lost between calls. The <code nd="34">PerSession</code> mode maintains a session between each call until the proxy is released. The <code nd="35">PerSession</code> is the default mode so you don't have to specify it. Finally, the <code nd="36">Single</code> mode keeps the same instance of the service until the server itself is shutdown. Take note that in earlier versions of WCF (May CTP and before), the <code nd="37">PerCall</code> was the default mode and that there was a <code nd="38">Sharable</code> mode that doesn't exist anymore. 
</p>
		<p nd="39">Here is how the implementation of this simple service looks like:</p>
		<div class="precollapse" id="premain1" style="WIDTH: 100%">
				<img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" />
				<span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="40" preid="1"> Collapse</span>
		</div>
		<pre lang="cs" id="pre1" style="MARGIN-TOP: 0px" nd="44">
				<span class="cs-comment" nd="41">/// &lt;summary&gt;</span>
				<span class="cs-comment" nd="42">/// Implements the IDCCLocomotiveContract</span>
				<span class="cs-comment" nd="43">/// &lt;/summary&gt;</span>
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
class DCCLocomotiveService : IDCCLocomotiveContract, IDisposable
{    
    const byte        
        MaxSpeed = 28,        
        MinSpeed = 0;
    protected byte m_speed = 0;
    protected bool m_light = false;
    protected Direction m_direction = Direction.Forward;
         
    public Direction GetDirection()
    {
         return m_direction;
    }

    public void ChangeDirection(Direction direction)
    {
         m_direction = direction;
    }

    public void SetSpeed(byte speed)
    {
         if (speed &gt;= MinSpeed &amp;&amp; speed &lt;= MaxSpeed)
             m_speed = speed;
    }

    public byte GetSpeed()
    {
         return m_speed;
    }

    public void SwitchLight(bool state)
    {
         m_light = state;
    }

    public bool GetLightState()
    {
         return m_light;
    }
}</pre>
		<h2>Demo applications</h2>
		<p nd="45">I created two simple applications. The "locomotive factory" behaves like when you put the locomotive on the track, and the "Locomotives monitor" allows watching the parameters of the locomotives. The locomotive factory is a window application that starts one server per locomotive. Each server gets an address related to the address of the locomotive. It has one endpoint for the locomotive contract. I chose this implementation because I wanted to be able to stop the server like when you remove a locomotive from the track. For testing purposes, I made it possible to the factory to control the locomotives from a control dialog box. First, I wanted to use one service and several endpoints for the locomotives, but it was not possible because all the endpoints must be created before the service host is started. 
</p>
		<p nd="46">I use an XML file that contains the list of the available locomotives. Basically, I use the address and the name. A checkbox list is created from that file. When you check an element, it starts the locomotive server and creates the endpoint for it. When you uncheck the element, it stops the server. </p>
		<p nd="47">The main user interface of the "Locomotive factory" is shown at the beginning of the article.<br /></p>
		<p nd="48">A button allows sending commands to the given locomotive from a dialog box. This is where I found a strange behavior. In my first draft, I was creating the <code nd="49">ServiceHost</code> instance in the same thread as the application. When I was calling a method on the <code nd="50">IDCCLocomotiveContract</code> instance I got from a <code nd="51">ChannelFactory</code> in the dialog box, it was failing with a timeout. However, the same call from the same <code nd="52">ChannelFactory</code> from another application was working.</p>
		<h2>Hosting the Service</h2>
		<p nd="53">I designed a simple class that creates the <code nd="54">ServiceHost</code> instance in a different thread than the one of the application. It seems that a Windows Forms application introduces restrictions due to the window messaging, and I suppose that there is some interference between this messaging and the one used by WCF. This class creates a <code nd="55">Thread</code> that starts the <code nd="56">ServiceHost</code> for my contracts and waits until the application stops it. It uses a simple pattern to stop it, with a boolean that triggers the end of the thread method. A thread must not be stopped using the <code nd="57">Abort</code> method, because in our case, it must close the <code nd="58">ServiceHost</code>. A call to <code nd="59">Abort</code> would let the <code nd="60">ServiceHost</code> open. </p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="61" preid="2"> Collapse</span>
		</div>
		<pre lang="cs" id="pre2" style="MARGIN-TOP: 0px" nd="62">class ThreadedServiceHost&lt;TService, TContract&gt;
{    
    const int SleepTime = 100;    
    private ServiceHost m_serviceHost = null;    
    private Thread m_thread;    
    private string         
        m_serviceAddress,        
        m_endpointAddress;    
        private bool m_running;    
        private Binding m_binding;

    public ThreadedServiceHost(        
        string serviceAddress, 
        string endpointAddress, Binding binding)
    {
         m_binding = binding;
         m_serviceAddress = serviceAddress;
         m_endpointAddress = endpointAddress;

         m_thread = new Thread(new ThreadStart(ThreadMethod));
         m_thread.Start();
    }

    void ThreadMethod()
    {
        try
        {
            m_running = true;
            <span class="cs-comment" nd="63">// Start the host</span>
            m_serviceHost = new ServiceHost(
                 typeof(TService), 
                 new Uri(m_serviceAddress));
            m_serviceHost.AddServiceEndpoint(
                typeof(TContract), 
                m_binding, 
                m_endpointAddress);
                m_serviceHost.Open();

            while (m_running)
            {
                <span class="cs-comment" nd="64">// Wait until thread is stopped</span>
                Thread.Sleep(SleepTime);
            }

            <span class="cs-comment" nd="65">// Stop the host</span>
            m_serviceHost.Close();
        }
        catch (Exception)
        {
            if (m_serviceHost != null)
            {
                m_serviceHost.Close();
            }
        }
    }

    <span class="cs-comment" nd="66">/// &lt;summary&gt;</span><span class="cs-comment" nd="67">/// Request the end of the thread method.</span><span class="cs-comment" nd="68">/// &lt;/summary&gt;</span>
    public void Stop()
    {
        lock (this)
        {
            m_running = false;
        }
    }
}</pre>
		<p>
		</p>
		<h2>Proxy for the Service</h2>
		<p nd="69">An interesting issue of my application is that I don't know by advance the number of locomotives and their addresses, so I don't use any <i>App.Config</i> file in my construction. I don't use any proxy as well but the <code nd="70">ChannelFactory</code> that allows creating a proxy on the fly. I use a <code nd="71">NetTcpBinding</code> which is OK for a local service and can eventually be used in Windows distributed environment. The <code nd="72">ChannelFactory</code> takes an interface derived from the contract of your service and the <code nd="73">IChannel</code> interface. It then dynamically creates a proxy to your service that you can use as if you where using the interface of your contract. </p>
		<p nd="74">Below is the code that I use to create the proxy with my service: </p>
		<pre lang="cs" nd="76">
				<span class="cs-comment" nd="75">// Creates the corresponding endpoint</span>
EndpointAddress endPoint = new EndpointAddress(    
    new Uri(string.Format(Constants.LocoServerBaseAddress, 
    address) + address));

<span class="cs-comment" nd="77">// Creates the proper binding</span>
System.ServiceModel.Channels.Binding binding = new NetTcpBinding();

<span class="cs-comment" nd="78">// Creates channel factory with the binding and endpoint</span>
m_dccLocoFactory = new ChannelFactory(binding, endPoint);
m_dccLocoFactory.Open();

<span class="cs-comment" nd="79">// Creates the channel and opens it to work with the service</span>
m_locoChannel = m_dccLocoFactory.CreateChannel();
m_locoChannel.Open();</pre>
		<p nd="80">The interface to create this proxy using the <code nd="81">ChannelFactory</code> is as follows:</p>
		<pre lang="cs" nd="85">
				<span class="cs-comment" nd="82">///&lt;summary&gt;</span>
				<span class="cs-comment" nd="83">/// Interface used to create the Proxy for IDCCLocomotiveContract</span>
				<span class="cs-comment" nd="84">///&lt;/summary&gt;</span>
interface IDCCLocomotiveChannel : IDCCLocomotiveContract, IClientChannel
{
}</pre>
		<p nd="86">The <code nd="87">ChannelFactory</code> is used in a dialog box that can be configured either to send commands to the locomotive service or to watch the different parameters. </p>
		<p nd="88">Here is a copy of the two versions of that dialog box which are in fact implemented by the same code:s</p>
		<p>
				<img height="210" src="http://www.codeproject.com/WCF/wcfservice/wcfservice1.jpg" width="234" />
				<img height="210" src="http://www.codeproject.com/WCF/wcfservice/wcfservice3.jpg" width="234" />
		</p>
		<p nd="89">Locomotive control is a child window form of the Locomotive Factory while the Locomotive Monitor is a child window form of the Locomotives Monitor, a simple application that connects to a locomotive service, given its address.</p>
		<p>
				<img height="104" src="http://www.codeproject.com/WCF/wcfservice/wcfservice4.jpg" width="284" />
		</p>
		<p nd="90">Like for a real locomotive decoder, the locomotive service doesn't provide any notification. A locomotive decoder is a passive device that cannot send information to the control station by its own. In order to reflect changes in the different clients, each client must manage its own pooling.</p>
		<h2>Points of Interest</h2>
		<p nd="91">You can dig into the complete code of this simple application, and I hope it will give you some ideas of what you could do with the new service model of WCF. Of course, this is just a glimpse at this very powerful framework for Service Oriented Application (SOA). WCF is a very complete framework to develop SOA based applications in the Microsoft world. However, as it is based on open standards like WS-*, applications can be designed to interoperate with other applications that comply to those standards regardless of the technology that implements it. 
</p>
		<p nd="92">Even if it is a simple demo of what can be done with WCF, this should help those who are discovering this new service model. Doing it, I discovered as few interesting points such as hosting the services in a Forms application which requires to use a specific thread for the service. I also found out that if you need to start and stop individual instances of the same service, you cannot use multiple endpoints, but must use a host for each of your instance. 
</p>
		<p nd="93">Another interesting point is the creation of dynamic hosts and proxies as their addresses depend on a list and is unknown by advance.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Olivier ROUIT</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=3528826">here</a> to view Olivier ROUIT's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21451.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 17:58 <a href="http://www.cnitblog.com/MartinYao/articles/21451.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ITrackingHandler Interface  </title><link>http://www.cnitblog.com/MartinYao/articles/21444.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 03:51:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21444.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21444.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21444.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21444.html</trackback:ping><description><![CDATA[
		<div class="title">ITrackingHandler Interface  </div>
		<!--Content type: DocStudio. Transform: devdiv2mtps.xslt.-->
		<div id="mainSection">
				<div id="mainBody">Indicates that the implementing object must be notified of marshaling, unmarshaling, and disconnection of objects and proxies by the remoting infrastructure. 
<p><b>Namespace:</b> System.Runtime.Remoting.Services<br /><b>Assembly:</b> mscorlib (in mscorlib.dll) 
</p><div class="MTPS_CollapsibleRegion"><script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl036d6292a,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl03img,";
]]&gt;</script><a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl036d6292a','ctl00_LibFrame_ctl03img');" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler.aspx#"><img class="CollapseRegionImg" id="ctl00_LibFrame_ctl03img" alt="Syntax" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />Syntax</a><br /><div class="section" id="ctl00_LibFrame_ctl036d6292a" style="DISPLAY: block"><div id="ctl00_LibFrame_ctl03"><a name="syntaxToggle"><div class="code" id="ctl00_LibFrame_ctl04_VisualBasicDeclaration"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">Visual Basic (Declaration)</div></div><pre class="code" id="ctl00_LibFrame_ctl04VisualBasicDeclaration" space="preserve">&lt;ComVisibleAttribute(<span style="COLOR: blue">True</span>)&gt; _
<span style="COLOR: blue">Public</span><span style="COLOR: blue">Interface</span> ITrackingHandler
</pre></div><div class="code" id="ctl00_LibFrame_ctl05_VisualBasicUsage"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">Visual Basic (Usage)</div></div><pre class="code" id="ctl00_LibFrame_ctl05VisualBasicUsage" space="preserve"><span style="COLOR: blue">Dim</span> instance <span style="COLOR: blue">As</span> ITrackingHandler
</pre></div><div class="code" id="ctl00_LibFrame_ctl06_CSharp"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">C#</div></div><pre class="code" id="ctl00_LibFrame_ctl06CSharp" space="preserve">[ComVisibleAttribute(<span style="COLOR: blue">true</span>)] 
<span style="COLOR: blue">public</span> interface ITrackingHandler
</pre></div><div class="code" id="ctl00_LibFrame_ctl07_ManagedCPlusPlus"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">C++</div></div><pre class="code" id="ctl00_LibFrame_ctl07ManagedCPlusPlus" space="preserve">[ComVisibleAttribute(<span style="COLOR: blue">true</span>)] 
<span style="COLOR: blue">public</span> interface <span style="COLOR: blue">class</span> ITrackingHandler
</pre></div><div class="code" id="ctl00_LibFrame_ctl08_JSharp"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">J#</div></div><pre class="code" id="ctl00_LibFrame_ctl08JSharp" space="preserve">/** @attribute ComVisibleAttribute(<span style="COLOR: blue">true</span>) */ 
<span style="COLOR: blue">public</span> interface ITrackingHandler
</pre></div><div class="code" id="ctl00_LibFrame_ctl09_JScript"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">JScript</div></div><pre class="code" id="ctl00_LibFrame_ctl09JScript" space="preserve">ComVisibleAttribute(<span style="COLOR: blue">true</span>) 
<span style="COLOR: blue">public</span> interface ITrackingHandler
</pre></div><div class="code" id="ctl00_LibFrame_ctl10_XAML"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">XAML</div></div><pre class="code" id="ctl00_LibFrame_ctl10XAML" space="preserve">Not applicable.
</pre></div></a></div></div></div><div class="MTPS_CollapsibleRegion"><script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl11311e708,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl11img,";
]]&gt;</script><a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl11311e708','ctl00_LibFrame_ctl11img');" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler.aspx#"><img class="CollapseRegionImg" id="ctl00_LibFrame_ctl11img" alt="Remarks" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />Remarks</a><br /><div class="section" id="ctl00_LibFrame_ctl11311e708" style="DISPLAY: block"><div id="ctl00_LibFrame_ctl11"><a name="remarksToggle"><p>Every object that is registered with <a onclick="javascript:Track('ctl00_LibFrame_ctl11|ctl00_LibFrame_ctl12',this);" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.trackingservices.aspx">TrackingServices</a> is called by remoting when an object or proxy in the current <a onclick="javascript:Track('ctl00_LibFrame_ctl11|ctl00_LibFrame_ctl13',this);" href="http://msdn2.microsoft.com/en-us/library/system.appdomain.aspx">AppDomain</a> is marshaled, unmarshaled, or disconnected.</p><p>Note that only objects can be disconnected. An exception is thrown when disconnect is called on a proxy.</p><div class="alert"><table width="100%"><tbody><tr><th align="left"><img class="note" alt="Note" src="http://msdn2.microsoft.com/en-us/library/6ky6zkza.note(en-us,VS.85).gif" />Note: </th></tr><tr><td><p>This interface makes a link demand. A SecurityException is thrown if the immediate caller makes the call through a reference to the interface and does not have infrastructure permission. See <a onclick="javascript:Track('ctl00_LibFrame_ctl11|ctl00_LibFrame_ctl14',this);" href="http://msdn2.microsoft.com/en-us/library/hzsc022c.aspx">[&lt;topic://cpconLinkDemands&gt;]</a> for more information.</p></td></tr></tbody></table></div><span></span></a></div></div></div><div class="MTPS_CollapsibleRegion"><script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl16b8d49e0,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl16img,";
]]&gt;</script><a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl16b8d49e0','ctl00_LibFrame_ctl16img');" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler.aspx#"><img class="CollapseRegionImg" id="ctl00_LibFrame_ctl16img" alt="Example" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />Example</a><br /><div class="section" id="ctl00_LibFrame_ctl16b8d49e0" style="DISPLAY: block"><div id="ctl00_LibFrame_ctl16"><a name="codeExampleToggle"><p>The following code example shows how to implement the methods of the <b>ITrackingHandler</b> interface to receive notifications from the remoting infrastructure. </p><p>The following code example shows how to implement this interface.</p><div class="code" id="ctl00_LibFrame_ctl17_CSharp"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">C#</div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl17CSharp');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl17CSharp" space="preserve"><span style="COLOR: blue">using</span> System;
<span style="COLOR: blue">using</span> System.Runtime.Remoting;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Channels;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Services;


<span style="COLOR: green">// Intercept marshal, unmarshal, and disconnect events for an object.</span><span style="COLOR: blue">public</span><span style="COLOR: blue">class</span> TrackingHandler : ITrackingHandler
{
    <span style="COLOR: green">// Called when the tracked object is marshaled.</span>

    [System.Security.Permissions.SecurityPermissionAttribute(
     System.Security.Permissions.SecurityAction.LinkDemand,
     Flags=System.Security.Permissions.SecurityPermissionFlag.Infrastructure)]
    <span style="COLOR: blue">public</span><span style="COLOR: blue">void</span> MarshaledObject(Object obj, ObjRef objRef)
    {
        <span style="COLOR: green">// Notify the user of the marshal event.</span>
        Console.WriteLine("Tracking: An instance of {0} was marshaled.", 
            obj.ToString());

        <span style="COLOR: green">// Print the channel information.</span><span style="COLOR: blue">if</span> (objRef.ChannelInfo != <span style="COLOR: blue">null</span>)
        {
            <span style="COLOR: green">// Iterate over ChannelData.</span><span style="COLOR: blue">foreach</span>(object data <span style="COLOR: blue">in</span> objRef.ChannelInfo.ChannelData)
            {
                <span style="COLOR: blue">if</span> (data is ChannelDataStore)
                {
                    <span style="COLOR: green">// Print the URIs from the ChannelDataStore objects.</span><span style="COLOR: blue">string</span>[] uris = ((ChannelDataStore)data).ChannelUris;
                    <span style="COLOR: blue">foreach</span>(<span style="COLOR: blue">string</span> uri <span style="COLOR: blue">in</span> uris)
                        Console.WriteLine("ChannelUri: " + uri);
                }
            }
        }

        <span style="COLOR: green">// Print the envoy information.</span><span style="COLOR: blue">if</span> (objRef.EnvoyInfo != <span style="COLOR: blue">null</span>)
            Console.WriteLine("EnvoyInfo: " + objRef.EnvoyInfo.ToString());

        <span style="COLOR: green">// Print the type information.</span><span style="COLOR: blue">if</span> (objRef.TypeInfo != <span style="COLOR: blue">null</span>)
        {
            Console.WriteLine("TypeInfo: " + objRef.TypeInfo.ToString());
            Console.WriteLine("TypeName: " + objRef.TypeInfo.TypeName);
        }

        <span style="COLOR: green">// Print the URI.</span><span style="COLOR: blue">if</span> (objRef.URI != <span style="COLOR: blue">null</span>)
            Console.WriteLine("URI: " + objRef.URI.ToString());
    }

    <span style="COLOR: green">// Called when the tracked object is unmarshaled.</span>
    [System.Security.Permissions.SecurityPermissionAttribute(
     System.Security.Permissions.SecurityAction.LinkDemand,
     Flags=System.Security.Permissions.SecurityPermissionFlag.Infrastructure)]
    <span style="COLOR: blue">public</span><span style="COLOR: blue">void</span> UnmarshaledObject(Object obj, ObjRef objRef)
    {
        Console.WriteLine("Tracking: An instance of {0} was unmarshaled.", 
            obj.ToString());
    }

    <span style="COLOR: green">// Called when the tracked object is disconnected.</span>
    [System.Security.Permissions.SecurityPermissionAttribute(
     System.Security.Permissions.SecurityAction.LinkDemand,
     Flags=System.Security.Permissions.SecurityPermissionFlag.Infrastructure)]
    <span style="COLOR: blue">public</span><span style="COLOR: blue">void</span> DisconnectedObject(Object obj)
    {
        Console.WriteLine("Tracking: An instance of {0} was disconnected.", 
            obj.ToString());
    }
}
</pre></div><p>The following code example shows how to implement this interface on a server.</p><div class="code" id="ctl00_LibFrame_ctl18_CSharp"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">C#</div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl18CSharp');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl18CSharp" space="preserve"><span style="COLOR: blue">using</span> System;
<span style="COLOR: blue">using</span> System.Runtime.Remoting;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Channels;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Channels.Tcp;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Services;
<span style="COLOR: blue">using</span> System.Security.Permissions;

<span style="COLOR: blue">public</span><span style="COLOR: blue">class</span> Server
{
[SecurityPermission(SecurityAction.Demand)]
    <span style="COLOR: blue">public</span><span style="COLOR: blue">static</span><span style="COLOR: blue">void</span> Main(<span style="COLOR: blue">string</span>[] args)
    {
        <span style="COLOR: green">// Register the TCP channel.</span>
        TcpChannel channel = <span style="COLOR: blue">new</span> TcpChannel(1234);
        ChannelServices.RegisterChannel(channel);

        <span style="COLOR: green">// Register a tracking handler.</span>
        ITrackingHandler handler1 = <span style="COLOR: blue">new</span> TrackingHandler();
        TrackingServices.RegisterTrackingHandler(handler1);

        <span style="COLOR: green">// Register a second handler.</span>
        ITrackingHandler handler2 = <span style="COLOR: blue">new</span> TrackingHandler();
        TrackingServices.RegisterTrackingHandler(handler2);

        <span style="COLOR: green">// Get the number of currently registered handlers.</span>
        Console.WriteLine("Registered tracking handlers: " + 
            TrackingServices.RegisteredHandlers.Length);

        <span style="COLOR: green">// Remove the tracking handler from the registered handlers.</span>
        TrackingServices.UnregisterTrackingHandler(handler2);
        Console.WriteLine("Registered tracking handlers: " + 
            TrackingServices.RegisteredHandlers.Length);

        <span style="COLOR: green">// Create and marshal an object for remote invocation.</span>
        RemoteService service = <span style="COLOR: blue">new</span> RemoteService();
        ObjRef obj = RemotingServices.Marshal(service, "TcpService");

        <span style="COLOR: green">// Wait for the user prompt.</span>
        Console.WriteLine("\r\nPress ENTER to unmarshal the object.");
        Console.ReadLine();

        <span style="COLOR: green">// Unmarshal the object.</span>
        RemotingServices.Unmarshal(obj);

        <span style="COLOR: green">// Wait for the user prompt.</span>
        Console.WriteLine("Press ENTER to disconnect the object.");
        Console.ReadLine();

        <span style="COLOR: green">// Disconnect the object.</span>
        RemotingServices.Disconnect(service);
    }
}
</pre></div><p>The following code example shows how to implement this interface on a client for the server in the preceding code example.</p><div class="code" id="ctl00_LibFrame_ctl19_CSharp"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">C#</div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl19CSharp');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl19CSharp" space="preserve"><span style="COLOR: blue">using</span> System;
<span style="COLOR: blue">using</span> System.Diagnostics;
<span style="COLOR: blue">using</span> System.Reflection;
<span style="COLOR: blue">using</span> System.Runtime.Remoting;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Channels;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Channels.Tcp;

<span style="COLOR: blue">public</span><span style="COLOR: blue">class</span> Client
{
    <span style="COLOR: blue">public</span><span style="COLOR: blue">static</span><span style="COLOR: blue">void</span> Main(<span style="COLOR: blue">string</span>[] args)
    {
        <span style="COLOR: green">// Register the TCP channel.</span>
        ChannelServices.RegisterChannel(<span style="COLOR: blue">new</span> TcpChannel());

        <span style="COLOR: green">// Register the client for the remote object.</span>
        WellKnownClientTypeEntry remoteType = <span style="COLOR: blue">new</span> WellKnownClientTypeEntry(
            typeof(RemoteService),"tcp:<span style="COLOR: green">//localhost:1234/TcpService");</span>
        RemotingConfiguration.RegisterWellKnownClientType(remoteType);

        <span style="COLOR: green">// Create an instance of the remote object.</span>
        RemoteService service = <span style="COLOR: blue">new</span> RemoteService(); 

        <span style="COLOR: green">// Invoke a method on the remote object.</span>
        service.Hello("world");
        Console.WriteLine("Hello invoked on server.");
    }
}
</pre></div><p>The following code example shows the remote object that is used by the server and the client.</p><div class="code" id="ctl00_LibFrame_ctl20_CSharp"><div class="CodeSnippetTitleBar"><div class="CodeDisplayLanguage">C#</div><div class="CopyCodeButton"><a class="copyCode" href="javascript:CopyCode('ctl00_LibFrame_ctl20CSharp');"><img height="9" src="http://msdn2.microsoft.com/msdn/Controls/CodeSnippet/en-us/copy_off.gif" align="middle" border="0" /> Copy Code</a></div></div><pre class="code" id="ctl00_LibFrame_ctl20CSharp" space="preserve"><span style="COLOR: blue">using</span> System;
<span style="COLOR: blue">using</span> System.Diagnostics;
<span style="COLOR: blue">using</span> System.Runtime.Remoting;
<span style="COLOR: blue">using</span> System.Runtime.Remoting.Channels;

<span style="COLOR: green">// Remote object.</span><span style="COLOR: blue">public</span><span style="COLOR: blue">class</span> RemoteService : MarshalByRefObject
{
    <span style="COLOR: blue">private</span> DateTime startTime;

    <span style="COLOR: blue">public</span> RemoteService()
    {
        <span style="COLOR: green">// Notify the user that the constructor was invoked.</span>
        Console.WriteLine("Constructor invoked.");
        startTime = DateTime.Now;
    }

    ~RemoteService()
    {
        <span style="COLOR: green">// Notify the user that the destructor was invoked.</span>
        TimeSpan elapsedTime = 
            <span style="COLOR: blue">new</span> TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
        Console.WriteLine("Destructor invoked after " + 
            elapsedTime.ToString() + " seconds.");
    }

    <span style="COLOR: blue">public</span><span style="COLOR: blue">void</span> Hello(<span style="COLOR: blue">string</span> name)
    {
        <span style="COLOR: green">// Print a simple message.</span>
        Console.WriteLine("Hello, " + name);
    }
}
</pre></div></a></div></div></div><div class="MTPS_CollapsibleRegion"><script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl21a747ced,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl21img,";
]]&gt;</script><a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl21a747ced','ctl00_LibFrame_ctl21img');" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler.aspx#"><img class="CollapseRegionImg" id="ctl00_LibFrame_ctl21img" alt=".NET Framework Security" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />.NET Framework Security</a><br /><div class="section" id="ctl00_LibFrame_ctl21a747ced" style="DISPLAY: block"><div id="ctl00_LibFrame_ctl21"><a name="permissionsToggle"><ul><li><a onclick="javascript:Track('ctl00_LibFrame_ctl21|ctl00_LibFrame_ctl22',this);" href="http://msdn2.microsoft.com/en-us/library/system.security.permissions.securitypermission.aspx">SecurityPermission</a>  For operating with infrastructure code. Demand value: <a onclick="javascript:Track('ctl00_LibFrame_ctl21|ctl00_LibFrame_ctl23',this);" href="http://msdn2.microsoft.com/en-us/library/system.security.permissions.securityaction.aspx">SecurityAction.LinkDemand</a>; Permission Value: <a onclick="javascript:Track('ctl00_LibFrame_ctl21|ctl00_LibFrame_ctl24',this);" href="http://msdn2.microsoft.com/en-us/library/system.security.permissions.securitypermissionflag.aspx">SecurityPermissionFlag.Infrastructure</a></li></ul></a></div></div></div><div class="MTPS_CollapsibleRegion"><script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl25fd9fcd1,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl25img,";
]]&gt;</script><a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl25fd9fcd1','ctl00_LibFrame_ctl25img');" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler.aspx#"><img class="CollapseRegionImg" id="ctl00_LibFrame_ctl25img" alt="Platforms" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />Platforms</a><br /><div class="section" id="ctl00_LibFrame_ctl25fd9fcd1" style="DISPLAY: block"><div id="ctl00_LibFrame_ctl25"><a name="platformsToggle"><p>Windows 98, Windows Server 2000 SP4, Windows Millennium Edition, Windows Server 2003, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP SP2, Windows XP Starter Edition</p><p></p><p><span>The Microsoft .NET Framework 3.0 is supported on Windows Vista, Microsoft Windows XP SP2, and Windows Server 2003 SP1. </span></p></a></div></div></div><div class="MTPS_CollapsibleRegion"><script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl26555370e,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl26img,";
]]&gt;</script><a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl26555370e','ctl00_LibFrame_ctl26img');" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler.aspx#"><img class="CollapseRegionImg" id="ctl00_LibFrame_ctl26img" alt="Version Information" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />Version Information</a><br /><div class="section" id="ctl00_LibFrame_ctl26555370e" style="DISPLAY: block"><div id="ctl00_LibFrame_ctl26"><a name="frameworksToggle"><h4 class="subHeading">.NET Framework</h4>Supported in: 3.0, 2.0, 1.1, 1.0<br /></a></div></div></div><div class="MTPS_CollapsibleRegion"><script type="text/Javascript"><![CDATA[
var ExpCollDivStr = ExpCollDivStr;
ExpCollDivStr = ExpCollDivStr + "ctl00_LibFrame_ctl27f2d1c08,";
var ExpCollImgStr = ExpCollImgStr;
 ExpCollImgStr = ExpCollImgStr + "ctl00_LibFrame_ctl27img,";
]]&gt;</script><a class="CollapseRegionLink" onclick="ShowHideCollapsibleArea('ctl00_LibFrame_ctl27f2d1c08','ctl00_LibFrame_ctl27img');" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler.aspx#"><img class="CollapseRegionImg" id="ctl00_LibFrame_ctl27img" alt="See Also" src="http://msdn2.microsoft.com/msdn/Controls/CollapsibleArea/en-us/minus.gif" align="middle" />See Also</a><br /><div class="section" id="ctl00_LibFrame_ctl27f2d1c08" style="DISPLAY: block"><div id="ctl00_LibFrame_ctl27"><a name="seeAlsoToggle"><div><h4 class="subHeading">Reference</h4><a onclick="javascript:Track('ctl00_LibFrame_ctl27|ctl00_LibFrame_ctl28',this);" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.itrackinghandler_members.aspx">ITrackingHandler Members</a><br /><a onclick="javascript:Track('ctl00_LibFrame_ctl27|ctl00_LibFrame_ctl29',this);" href="http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.services.aspx">System.Runtime.Remoting.Services Namespace</a><br /></div></a></div></div></div></div>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 11:51 <a href="http://www.cnitblog.com/MartinYao/articles/21444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Installing a Service Programmatically</title><link>http://www.cnitblog.com/MartinYao/articles/21443.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 03:16:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21443.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21443.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21443.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21443.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21443.html</trackback:ping><description><![CDATA[
		<div>
				<font face="Verdana" size="2" nd="1">With the arrival of .Net, creation of a windows service has become a piece of cake by just selecting Windows Service as project type in the New Project Dialog Box. For installing a service, .Net provides a utility called installutil.exe which installs the service and registers it with Service Control Manager(SCM) but at times there is a need to install the service programmatically for example as a part of your custom installer program where you don't want your user to manually install the service using installutil.</font>
				<p>
						<font face="Verdana" size="2" nd="2">The following piece of code shows how to install/uninstall a given service and register it with SCM calling appropriate APIs using P/Invoke in C#</font>
				</p>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">SERVICEINSTALLER.CS</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">using System;<br />using System.Runtime.InteropServices;</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">namespace SvcInstaller<br />{<br /> /// &lt;summary&gt;<br /> /// Summary description for ServiceInstaller.<br /> /// &lt;/summary&gt;<br /> class ServiceInstaller<br /> {<br />  #region Private Variables<br />  <br />  private string _servicePath;<br />  private string _serviceName;<br />  private string _serviceDisplayName;</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">  #endregion Private Variables</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="3">  #region DLLImport<br />  <br />  [DllImport("advapi32.dll")]<br />  public static extern IntPtr OpenSCManager(string lpMachineName,string lpSCDB, int scParameter);<br />  [DllImport("Advapi32.dll")]<br />  public static extern IntPtr CreateService(IntPtr SC_HANDLE,string lpSvcName,string lpDisplayName, <br /> int dwDesiredAccess,int dwServiceType,int dwStartType,int dwErrorControl,string lpPathName, <br />string lpLoadOrderGroup,int lpdwTagId,string lpDependencies,string lpServiceStartName,string lpPassword);<br />  [DllImport("advapi32.dll")]<br />  public static extern void CloseServiceHandle(IntPtr SCHANDLE);<br />  [DllImport("advapi32.dll")]<br />  public static extern int StartService(IntPtr SVHANDLE,int dwNumServiceArgs,string lpServiceArgVectors);<br />  <br />  [DllImport("advapi32.dll",SetLastError=true)]<br />  public static extern IntPtr OpenService(IntPtr SCHANDLE,string lpSvcName,int dwNumServiceArgs);<br />  [DllImport("advapi32.dll")]<br />  public static extern int DeleteService(IntPtr SVHANDLE);</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">  [DllImport("kernel32.dll")]<br />  public static extern int GetLastError();<br />  <br />  #endregion DLLImport</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="4">  <br />  /// &lt;summary&gt;<br />  /// The main entry point for the application.<br />  /// &lt;/summary&gt;<br />  #region Main method + testing code <br />   [STAThread]<br />   static void Main(string[] args)<br />   {<br />     <br />     // TODO: Add code to start application here<br />     <br />    <br />    #region Testing<br />    //  Testing --------------<br />     string svcPath;<br />     string svcName;<br />     string svcDispName;<br />     <br />     //path to the service that you want to install<br />     svcPath = @"C:\build\service\Debug\Service.exe";<br />     svcDispName="Service Display Name";<br />     svcName= "Service Name";<br />  <br />     ServiceInstaller c = new ServiceInstaller();<br />     c.InstallService(svcPath, svcName, svcDispName);<br />  <br />     Console.Read();<br />       Testing --------------<br />    #endregion Testing<br />   }<br />  #endregion Main method + testing code - Commented</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="5">
						<br />  <br />  /// &lt;summary&gt;<br />  /// This method installs and runs the service in the service conrol manager.<br />  /// &lt;/summary&gt;<br />  /// &lt;param name="svcPath"&gt;The complete path of the service.&lt;/param&gt;<br />  /// &lt;param name="svcName"&gt;Name of the service.&lt;/param&gt;<br />  /// &lt;param name="svcDispName"&gt;Display name of the service.&lt;/param&gt;<br />  /// &lt;returns&gt;True if the process went thro successfully. False if there was any error.&lt;/returns&gt;<br />  public bool InstallService(string svcPath, string svcName, string svcDispName)<br />  {<br />   #region Constants declaration.<br />   int SC_MANAGER_CREATE_SERVICE = 0x0002;<br />   int SERVICE_WIN32_OWN_PROCESS = 0x00000010;<br />   //int SERVICE_DEMAND_START = 0x00000003;<br />   int SERVICE_ERROR_NORMAL = 0x00000001;</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">   int STANDARD_RIGHTS_REQUIRED = 0xF0000;<br />   int SERVICE_QUERY_CONFIG       =    0x0001;<br />   int SERVICE_CHANGE_CONFIG       =   0x0002;<br />   int SERVICE_QUERY_STATUS           =  0x0004;<br />   int SERVICE_ENUMERATE_DEPENDENTS   = 0x0008;<br />   int SERVICE_START                  =0x0010;<br />   int SERVICE_STOP                   =0x0020;<br />   int SERVICE_PAUSE_CONTINUE         =0x0040;<br />   int SERVICE_INTERROGATE            =0x0080;<br />   int SERVICE_USER_DEFINED_CONTROL   =0x0100;</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="6">   int SERVICE_ALL_ACCESS             =  (STANDARD_RIGHTS_REQUIRED     | <br />        SERVICE_QUERY_CONFIG         |<br />        SERVICE_CHANGE_CONFIG        |<br />        SERVICE_QUERY_STATUS         | <br />        SERVICE_ENUMERATE_DEPENDENTS | <br />        SERVICE_START                | <br />        SERVICE_STOP                 | <br />        SERVICE_PAUSE_CONTINUE       | <br />        SERVICE_INTERROGATE          | <br />        SERVICE_USER_DEFINED_CONTROL);<br />   int SERVICE_AUTO_START = 0x00000002;<br />   #endregion Constants declaration.</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="7">   try<br />   {<br />    IntPtr  sc_handle = OpenSCManager(null,null,SC_MANAGER_CREATE_SERVICE);</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="8">    if (sc_handle.ToInt32() != 0)<br />    {<br />     IntPtr sv_handle = CreateService(sc_handle,svcName,svcDispName,SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,svcPath,null,0,null,null,null);</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">     if(sv_handle.ToInt32() ==0)<br />     {</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="9">      CloseServiceHandle(sc_handle);<br />      return false;<br />     }<br />     else<br />     {<br />      //now trying to start the service<br />      int i = StartService(sv_handle,0,null);<br />      // If the value i is zero, then there was an error starting the service.<br />      // note: error may arise if the service is already running or some other problem.<br />      if(i==0)<br />      {<br />       //Console.WriteLine("Couldnt start service");<br />       return false;<br />      }<br />      //Console.WriteLine("Success");<br />      CloseServiceHandle(sc_handle);<br />      return true;<br />     }<br />    }<br />    else<br />     //Console.WriteLine("SCM not opened successfully");<br />     return false;</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2">   }<br />   catch(Exception e)<br />   {<br />    throw e;<br />   }<br />  }</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="10">  <br />  /// &lt;summary&gt;<br />  /// This method uninstalls the service from the service conrol manager.<br />  /// &lt;/summary&gt;<br />  /// &lt;param name="svcName"&gt;Name of the service to uninstall.&lt;/param&gt;<br />  public bool UnInstallService(string svcName)<br />  {<br />   int GENERIC_WRITE = 0x40000000;<br />   IntPtr sc_hndl = OpenSCManager(null,null,GENERIC_WRITE);</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="11">   if(sc_hndl.ToInt32() !=0)<br />   {<br />    int DELETE = 0x10000;<br />    IntPtr svc_hndl = OpenService(sc_hndl,svcName,DELETE);<br />    //Console.WriteLine(svc_hndl.ToInt32());<br />    if(svc_hndl.ToInt32() !=0)<br />    { <br />     int i = DeleteService(svc_hndl);<br />     if (i != 0)<br />     {<br />      CloseServiceHandle(sc_hndl);<br />      return true;<br />     }<br />     else<br />     {<br />      CloseServiceHandle(sc_hndl);<br />      return false;<br />     }<br />    }<br />    else<br />     return false;<br />   }<br />   else<br />    return false;<br />  }<br /> }<br />}</font>
		</div>
		<div>
				<font face="Verdana" size="2">
				</font> </div>
		<div>
				<font face="Verdana" size="2" nd="12">
						<br />The above code shows that installing a service programmatically is as simple if not simpler than using installutil and gives you far more flexibility.<br /></font>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21443.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 11:16 <a href="http://www.cnitblog.com/MartinYao/articles/21443.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>.NET Remoting – Basic Maneuvers</title><link>http://www.cnitblog.com/MartinYao/articles/21442.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 03:07:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21442.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21442.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21442.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21442.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21442.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/CRemoteObjServer.zip">Download server source - 13.9 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/CRemoteObjClient.zip">Download client source - 8.16 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/CRemoteObjInterface.zip">Download interface source - 4.45 Kb</a>
				</li>
				<li>
						<a href="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/CRemoteObj.zip">Download object source - 4.53 Kb</a>
				</li>
		</ul>
		<h2>Contents</h2>
		<ol>
				<li nd="1">Introduction 
</li>
				<li nd="2">Objectives. 
</li>
				<li nd="3">Prerequisites. 
</li>
				<li nd="4">Package. 
</li>
				<li nd="5">To Run The Sample. 
</li>
				<li nd="6">Configuration Files.<i></i></li>
				<li nd="7">Assembly References from client assembly.<i></i></li>
				<li nd="8">Code blocks in client source file <i>CRemoteObjClient.cs</i></li>
				<li nd="9">Code blocks in client source file <i>CRemoteObjServer.cs</i></li>
				<li nd="10">Walkthrough. 
</li>
				<li nd="11">References. </li>
		</ol>
		<h2>Introduction</h2>
		<p>
				<img height="147" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image001.png" width="380" border="0" />
		</p>
		<p nd="12">.NET Remoting is a framework for developing distributed applications. It is the successor to DEC/RPC/DCOM. Simply put, Remoting allows an application (Remoting Client) to instantiate a type (Remotable class) on a remote server (Remoting server) across network. Communication between client and server object (Instance of Remotable class) hosted by a Remoting Server is channeled through a "proxy" – representation of server object on client side.</p>
		<p nd="13">Remoting Server can be a simple console application, a Windows Service or hosted on IIS. It's responsible for hosting server objects and publishes them to the outside world. A Remoting client is any application that consumes the published server object.</p>
		<p nd="14">There're many tutorials on this broad subject. Unfortunately, most are lacking, in one way or another, in coverage of basic maneuvers a developer needs to know. The purpose of this article is to supplement MSDN and to provide a complete coverage of basic remoting tasks in one short article. </p>
		<h2>Objectives of the tutorial</h2>
		<p nd="15">The tutorial will cover the following topics.</p>
		<ul>
				<li nd="16">Basic Remoting , Programmatic Configuration (channel/type), Config files, LeaseTime, <code><span class="cpp-keyword">new</span></code>, <code nd="17">Activator.GetObject</code> (server-activated object, or SAO) and <code nd="18">Activator.CreateInstance</code> (client activated object, or CAO) 
</li>
				<li nd="19">How to implement interface for remote class, and interact with remote object through interface. 
</li>
				<li nd="20">Passing custom/user-defined objects between remote object and client. 
</li>
				<li nd="21">Asynchronous method calls on remote object. 
</li>
				<li nd="22">Events and remoting: How client can subscribe to events generated by remote object. </li>
		</ul>
		<p nd="23">This tutorial does not cover the following topics.</p>
		<ul>
				<li nd="24">Hosting server from IIS [Ref - 2,7] 
</li>
				<li nd="25">Delayed loading of channels 
</li>
				<li nd="26">Tracking services 
</li>
				<li nd="28">
						<code nd="27">ClientSponsor</code> and sponsoring mechanism [Ref 5] 
</li>
				<li nd="29">Custom formatter, sinks, channels – that's where things actually get complicated. [Ref 1] 
</li>
				<li nd="30">SoapSuds </li>
		</ul>
		<h2>Prerequisites</h2>
		<ul>
				<li nd="31">.NET Delegates and events. 
</li>
				<li nd="32">Asynchronous programming. <code nd="33">IAsyncResult, BeginInvoke, EndInvoke, WaitHandle</code>... etc. </li>
		</ul>
		<h2>Package</h2>
		<ul>
				<li>
						<i>dnetremoting.zip</i>
				</li>
		</ul>
		<h3>Overall Architecture</h3>
		<ul>
				<li nd="34">ReadMe: <i>dnetremoting.doc</i> (That's this document.) 
</li>
				<li nd="35">Project folders, classes and overall structure: </li>
		</ul>
		<p nd="36">[<b>Editor Note </b>- hyphens/spaces have been used in the table contents to prevent scrolling]</p>
		<table width="600" border="1">
				<tbody>
						<tr>
								<td>
										<p>
												<b>Module</b>
										</p>
								</td>
								<td>
										<p>
												<b>Folder/Directory</b>
										</p>
								</td>
								<td>
										<p>
												<b>Primary class</b>
										</p>
								</td>
								<td>
										<p>
												<b>namespace</b>
										</p>
								</td>
								<td>
										<p>
												<b>Remarks</b>
										</p>
								</td>
						</tr>
						<tr>
								<td>
										<p nd="37">Interface </p>
										<p nd="38">Dll</p>
								</td>
								<td>
										<p>
												<i>/CRemote-ObjInterface</i>
										</p>
										<p nd="39">source: <i>IRemoteObj.cs</i></p>
								</td>
								<td>
										<p>
												<code nd="40">CRemote-ObjInterface</code>
										</p>
								</td>
								<td>
										<p>
												<code nd="41">nsCRemote-ObjInterface</code>
										</p>
								</td>
								<td>
										<p nd="42">Interface (<code nd="43">IRemoteObj</code>) for <code nd="44">CRemoteObj</code>. Three interface methods:</p>
										<ul>
												<li>
														<code nd="45">Authenticate-Loser</code>
												</li>
												<li>
														<code nd="46">SetupUser</code>
												</li>
												<li>
														<code nd="47">UpdateProfile</code>
												</li>
										</ul>
										<code nd="48">CProfile</code>
										<p nd="49">To illustrates how to pass custom object between remote object (<code nd="50">CRemoteObj</code>) and client. It's marked <code nd="51">Serializable</code> and is input/return parameter of <code nd="52">CremoteObj. UpdateProfile</code>.</p>
										<code nd="53">CStatusEventSink</code>
										<p nd="54">This is the event sink class to be instantiated in client's process. It's derived from <code nd="55">MarshalByRefObject</code>. The class has a public method <code nd="56">StatusHandler</code>. This method handles the events raised by <code nd="57">CRemoteObj. AuthenticateLoser</code></p>
										<code nd="58">StatusEventArgs</code>
										<p nd="60">
												<code nd="59">Serializable</code> event argument. Refer to <code nd="61">CremoteObj</code>. <code nd="62">evStatus</code>. Delegate to <code nd="63">evStatus</code> event can be found in <code nd="64">CRemoteObj</code> class. Signature as follows:</p>
										<p>
												<code nd="65">
														<span class="cpp-keyword">public</span> delegate <span class="cpp-keyword">void</span> StatusEvent( object sender, StatusEventArgs e);</code>
										</p>
								</td>
						</tr>
						<tr>
								<td>
										<p nd="66">Remote Object dll</p>
								</td>
								<td>
										<p>
												<i>/CRemoteObj</i>
										</p>
										<p nd="67">source: <i>CRemoteObj.cs</i></p>
								</td>
								<td>
										<p>
												<code nd="68">CRemoteObj</code>
										</p>
								</td>
								<td>
										<p>
												<code nd="69">nsCRemoteObj</code>
										</p>
								</td>
								<td>
										<p nd="70">That's your remote object. This class implement <code nd="71">IRemoteObj</code> interface and exposes:</p>
										<p nd="72">Methods</p>
										<ul>
												<li nd="74">
														<code nd="73">Authenticate-Loser</code> – Demonstrates how to client can response to events generated by remote object. 
</li>
												<li nd="76">
														<code nd="75">SetupUser</code> – Demonstrates how to execute asynchronous calls and method parameters IO. 
</li>
												<li nd="78">
														<code nd="77">UpdateProfile</code> – Demonstrates how to pass instances of user-defined class in and out of remote object. </li>
										</ul>
										<p nd="79">Properties</p>
										<ul>
												<li nd="81">
														<code nd="80">objID</code> - Remote object's lifetime depends on the object's activation mode: </li>
										</ul>
										<ol>
												<li nd="82">Server activated - <code nd="83">SingleCall</code></li>
												<li nd="84">Server activated - <code nd="85">Singleton</code></li>
												<li nd="86">Client activated </li>
										</ol>
										<p nd="87">With Server- activated-<code nd="88">SingleCall</code> objects, a new instance of remote object is created every time client invoke a method through the proxy. For "Server-activated-Singleton" and "Client-activated" objects, lifetime of the remote object depends on server configurations (config file <code nd="89">&lt;lifetime&gt;</code> element or programmatically via <code nd="90">LifeTimeServices</code>), as well as when client invoke a method on the remote object. Default <code nd="91">LeaseTime</code><i><b></b></i>is 300 sec.</p>
										<p nd="92">The object ID is to demonstrate remote object lifetime by marking each object with a randomly generated ID. Pay attention to server console when invoking methods on remote object.</p>
								</td>
						</tr>
						<tr>
								<td>
										<p nd="93">Server</p>
										<p nd="94">exe</p>
								</td>
								<td>
										<p>
												<i>/CRemote-ObjServer</i>
										</p>
										<p nd="95">source: <i>CRemote-ObjServer.cs</i></p>
										<p nd="96">Executable:</p>
										<p>
												<i>\web_vdir\bin\ CRemoteObj-Server.exe</i>
										</p>
										<p nd="97">(C# console app)</p>
								</td>
								<td>
										<p>
												<code nd="98">CRemote-ObjServer</code>
										</p>
								</td>
								<td>
										<p>
												<code nd="99">nsCRemote-ObjServer</code>
										</p>
								</td>
								<td>
										<p nd="101">
												<code nd="100">CRemoteObjServer</code> hosts/publishes the remote object <code nd="102">CRemoteObj</code>.</p>
										<p nd="103">Config files</p>
										<ul>
												<li nd="105">
														<code nd="104">cremoteobjserv (client). config</code>
														<i>
														</i>– for client activation. 
</li>
												<li nd="107">
														<code nd="106">cremoteobjserv (wellknown) .config </code>– for server activation; <code nd="108">WellKnown ObjectMode = SingleCall</code>. You need to modify the config file if you wish to publish the remote object using Singleton mode. </li>
										</ul>
										<p nd="109">OPTION 2 loads config file as follows:</p>
										<p>
												<code nd="110">Remoting Configuration. Configure( <span class="cpp-string" nd="111">"cremoteobjserv. config"</span>);</code>
										</p>
										<p nd="112">Please be reminded that security setting in config files will be ignored unless file name comply to the following convention:</p>
										<p>
												<i>AssemblyName. exe.config</i>
										</p>
										<p nd="113">In this case:</p>
										<p>
												<i>CRemoteObjServer. exe.config</i>
										</p>
										<p>
												<b>
												</b>
										</p>
										<p nd="114">For this tutorial, rename config file name to: <i>cremoteobjserv.config </i>and place it in the same folder (<i>\web_vdir\bin</i>) as the exe.</p>
								</td>
						</tr>
						<tr>
								<td>
										<p nd="115">Client</p>
										<p nd="116">exe</p>
								</td>
								<td>
										<p>
												<i>/CRemote-ObjClient</i>
										</p>
										<p nd="117">source:</p>
										<p>
												<i>CRemoteObj-Client.cs</i>
										</p>
										<p nd="118">Executable:</p>
										<p>
												<i>\ bin\Debug \CRemoteObj-Client.exe</i>
										</p>
										<p nd="119">(C# console app)</p>
								</td>
								<td>
										<p>
												<code nd="120">CRemote-ObjClient</code>
										</p>
								</td>
								<td>
										<p>
												<code nd="121">nsCRemote-ObjClient</code>
										</p>
								</td>
								<td>
										<p>
												<b>NOTE: </b>
										</p>
										<p nd="122">If it wasn't due to the fact that we used "new" keyword to instantiate remote object in &lt;BLOCK 2-A&gt;:<br /><br /><code nd="123">CRemoteObj obj = <span class="cpp-keyword">new</span> CRemoteObj();</code></p>
										<p nd="124">We could have eliminated reference to <code nd="125">CRemoteObj</code> assembly – reference to interface <code nd="126">CRemoteObjInterface</code> would suffice.</p>
										<p nd="127">Config files (folder: /config file):</p>
										<ul>
												<li nd="128">
														<i>cremoteobjclient (client).config </i>– for client activation. 
</li>
												<li nd="129">
														<i>cremoteobjclient (wellknown). config </i>– for server activation; </li>
										</ul>
										<p nd="130">In &lt;BLOCK 2-A&gt;, we called Configure to load configuration file:</p>
										<p>
												<code nd="131">RemotingConfiguration. Configure( <span class="cpp-string" nd="132">"cremoteobjclient. config"</span>);</code>
										</p>
										<p nd="133">Please be reminded that security setting in config files will be ignored unless file name comply to the following convention:</p>
										<p>
												<i>AssemblyName .exe.config</i>
										</p>
										<p nd="134">In this case:</p>
										<p>
												<i>CRemoteObjClient .exe.config</i>
										</p>
										<p>
												<b>
												</b>
										</p>
										<p nd="135">For this tutorial, rename config file name to: <i>cremoteobjclient.config </i>and place it in the same folder (<i>\web_vdir\bin</i>) as the exe.</p>
								</td>
						</tr>
				</tbody>
		</table>
		<b>
				<i>
						<br />
				</i>
		</b>
		<h2>To run the sample</h2>
		<ol>
				<li nd="136">Run server executable. 
</li>
				<li nd="137">Run client executable. </li>
		</ol>
		<p nd="138">Server and client are both C# console apps. Pay attention to console output.</p>
		<p>
				<img height="446" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image003.jpg" width="550" border="0" />
		</p>
		<h2>Configuration files</h2>
		<p nd="139">Before Remoting client and Remoting Server can begin to communicate, two pieces of information must first be properly configured, whether you're on server side or client side:</p>
		<h3>Communication channel</h3>
		<ul>
				<li nd="140">Communication channel: port number, protocol, uri/url. &lt;channel&gt; tags in configuration files. (Both server and client side) </li>
		</ul>
		<p nd="141">
				<b>NOTE</b>: You don't need to specify port number for client side.</p>
		<h3>Type (Remotable class) to be published or consumed:</h3>
		<ul>
				<li nd="142">Resource to be published and activation mode (config file for remoting server) &lt;service&gt; tag in configuration files (Remoting server side). 
</li>
				<li nd="143">Resource to be consumed (config file for remoting client) &lt;client&gt; tag in configurations files (Remoting client side). </li>
		</ul>
		<p nd="144">Two configuration options:</p>
		<ul>
				<li nd="145">Using XML configuration files: 
<ul><li><code nd="146">RemotingConfiguration.Configure(<span class="cpp-string" nd="147">"cremoteobjserv.config"</span>);</code></li></ul></li>
				<li nd="148">Configure setting programmatically: <pre lang="cs" nd="150"><span class="cs-comment" nd="149">//Configure channel:</span>
ChannelServices.RegisterChannel(httpchannel);
<span class="cs-comment" nd="151">//Register TYPE for server-activated objects (SAO):</span>
RemotingConfiguration.RegisterWellKnownServiceType(
  <span class="cs-keyword" nd="152">typeof</span>(nsCRemoteObj.CRemoteObj), <span class="cs-comment" nd="153">//Type</span><span class="cpp-string" nd="154">"CRemoteObjURI"</span>, <span class="cs-comment" nd="155">//"object URI" (NOT URL to remoting server!)</span>
  WellKnownObjectMode.SingleCall <span class="cs-comment" nd="156">//Activation mode: SingleCall or Singleton</span>
);
<span class="cs-comment" nd="157">//Register TYPE for client-activated objects (CAO):</span>
RemotingConfiguration.RegisterActivatedType(
  Typeof(nsCRemoteObj.CRemoteObj) );</pre></li>
		</ul>
		<p nd="158">
				<b>Note</b>: There're two types of server object: SAO (server activated object) and CAO (client activated object). SAO can be further divided into two types: <code nd="159">singlecall</code> and <code nd="160">singleton</code>. Here's a brief description.</p>
		<ul>
				<li nd="161">Server-Activated Object (SAO) = Well-known Object. 
</li>
				<li nd="162">Client Activated Object (CAO). </li>
		</ul>
		<h3>SAO</h3>
		<p nd="164">
				<code nd="163">SingleCall</code> - A new instance of the remotable class is created every time a client invokes a method through proxy. </p>
		<p nd="165">
				<b>Lifetime</b>: Instance remains in memory for duration of method call. </p>
		<p nd="166">This is stateless. </p>
		<p nd="168">
				<code nd="167">Singleton</code> - The Singleton Instance is created on first method call. </p>
		<p nd="169">
				<b>Lifetime</b>: This instance stays in memory until "Lease" expires.</p>
		<p nd="170">This is Stateful. Only one instance of Singleton-SAO is instantiated on remoting server. Multiple Remoting Clients are serviced by same Singleton-SAO. Multiple remoting clients can communicate through one instance of Singleton SAO.</p>
		<h3>CAO</h3>
		<ul>
				<li nd="171">Instantiated on remote server as soon as the remoting client requests that an instance be created through <code nd="172">Activator.CreateInstance</code> or <code><span class="cpp-keyword">new</span></code> keyword. This is in contrast to SAO where server objects are created upon first method call. 
</li>
				<li nd="173">
						<b>Lifetime</b>: Just as Singleton-SAO, object stays in memory until "Lease" expires. 
</li>
				<li nd="174">Stateful. Each CAO instance services only the remoting client responsible for its creation. Therefore, if you have multiple Remoting Client, each gets their own instance of CAO. </li>
		</ul>
		<p nd="175">Single-call SAO is the most scalable option among the three and is most suitable in a load-balanced environment. Singleton-SAO and CAO offers more flexibility that:</p>
		<ul>
				<li nd="176">Remoting clients have complete control over a server object's lifetime. 
</li>
				<li nd="177">Server objects are stateful. CAO maintains states across multiple requests/method call. Singleton-SAO maintains states across multiple Remoting Client. </li>
		</ul>
		<h3>Sample configuration files</h3>
		<h4>Server side</h4>
		<p>
				<img height="349" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image005.png" width="481" border="0" />
		</p>
		<p nd="178">Server activated. File name: <i>cremoteobjserv(wellknown).config</i></p>
		<h4>Client side</h4>
		<p>
				<img height="342" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image007.png" width="576" border="0" />
		</p>
		<p nd="179">Server-activated. File name: <i>cremoteobjclient(wellknown).config</i></p>
		<p nd="180">In addition to type and channel information, configuration file may contain setting such as object lifetime, security... Example: lifetime of Singleton SAO and CAO remote object can be configured in <code nd="181">&lt;lifetime&gt; </code>tag in a config file:</p>
		<p>
				<img height="170" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image009.jpg" width="600" border="0" />
		</p>
		<p nd="182">Remote object lives as long as <code nd="183">CurrentLeaseTime</code>&gt;0. When the object first got instantiated, <code nd="184">CurrentLeaseTime = leaseTime</code>. From this point on, <code nd="185">CurrentLeaseTime</code> starts decrementing until the next method call, or that a "sponsor" extend the "lease". On next method call (if lease has not yet expired), <code nd="186">CurrentLeaseTime</code> is reset to <code nd="187">renewOnCallTime</code>. On the other hand, if <code nd="188">CurrentLeaseTime</code> decrement to zero before next method call arrives, the object is marked for garbage collection. If this happens, the next method call will be serviced by a new instance of the remote class. In short, "server-activated Singleton" and "client activated" remote object lives for as long as <code nd="189">CurrentLeaseTime</code>&gt;0. Lifetime setting can also be configured programmatically:</p>
		<pre lang="cs" nd="191">
				<span class="cs-keyword" nd="190">using</span> System.Runtime.Remoting.LifetimeServices;
LifetimeServices.LeaseTime = TimeSpan.FromMinutes(<span class="cs-literal" nd="192">3</span>);
LifetimeServices.RenewOnCallTime = TimeSpan.FromMinutes(<span class="cs-literal" nd="193">3</span>);</pre>
		<p nd="194">For more information about leasing mechanism and sponsoring mechanism, refer to MSDN under the topic "Lifetime Leases" [Ref 5] and "Remoting Example: Lifetimes" [Ref 8]. </p>
		<h2>Assembly References from "client" assembly</h2>
		<ul>
				<li nd="195">Reference to interface assembly (i.e.. "Project" menu&gt;"Add Reference"&gt;select interface <i>CRemoteObjInterface.dll</i>) </li>
		</ul>Now, since we use "<code><span class="cpp-keyword">new</span></code>" keyword in BLOCK 2-A, we added reference to remote object assembly (<i>CRemoteObj.dll</i>) in addition to reference to interface assembly (<i>CRemoteObjInterface.dll</i>). In general however, we can retrieve proxy via <code>Activator.GetObject</code> or <code>Activator.CreateInstance</code>, thereby eliminating need to reference remote object assembly (i.e.. Client references ONLY interface assembly). 
<h2>Code blocks in client source file CRemoteObjClient.cs</h2><p>Basically, client source is separated into two mutually exclusive blocks – marked by the following tags in client's source code <i>CRemoteObjClient.cs</i>:</p><blockquote><h4>&lt;BLOCK X-Y&gt; </h4><pre lang="cs">... code block ...</pre><h4>&lt;BLOCK X-Y END&gt; </h4></blockquote><p>You should comment out BLOCK 1 when you want to run code in BLOCK 2, and vice versa. </p><ul><li>BLOCK 1: remote object is published as server-activated object. 
</li><li>BLOCK 2: remote object is published as client-activated object. </li></ul><p>So, depending on setting in <code>CRemoteObjServer</code>, you may wish to comment out one of the blocks before you compile and execute. In addition, &lt;BLOCK 2-A&gt; and &lt;BLOCK 2-B&gt; are mutually exclusive. </p><ul><li>BLOCK 2-A configures channel/type information by loading the config file, then instantiate remote object with "new" keyword. 
</li><li>BLOCK 2-B configures channel/type information programmatically. Proxy is retrieved with call to <code>Activator.CreateInstance(..)</code></li></ul><p>So, again, comment out one of the two before compile and execute.</p><pre lang="cs"><span class="cs-keyword">namespace</span> nsCRemoteObjClient

{ <span class="cs-comment">//nsCRemoteObjClient</span><span class="cs-keyword">class</span> CRemoteObjClient

{ <span class="cs-comment">//CRemoteObjClient</span></pre><ol type="a"><li>Delegate to <code>CRemoteObj.SetupUser()</code></li><li><b>Signature</b>: <code><span class="cpp-keyword">public</span> string SetupUser(string sname, string spasswd, out string sTime)</code></li><li>Refer to &lt;BLOCK 1-C&gt; </li></ol><pre lang="cs"><span class="cs-keyword">public</span><span class="cs-keyword">delegate</span><span class="cs-keyword">string</span> SetupUserDelegate(...);
[STAThread]
<span class="cs-keyword">static</span><span class="cs-keyword">void</span> Main(<span class="cs-keyword">string</span>[] args)
{ <span class="cs-comment">//Main()</span><span class="cs-comment">//Scenario 1: "server-activated" remote object. </span></pre><blockquote><h4>&lt;BLOCK 1&gt;</h4><pre lang="cs"><span class="cs-keyword">try</span>

{ <span class="cs-comment">//try-A</span></pre><blockquote><h4>&lt;BLOCK 1-A&gt; </h4><h5>Retrieve interface/proxy:</h5><ul><li>Illustrates how to retrieve proxy via: <code>Activator.GetObject(...)</code></li><li>Cast proxy into <code>IRemoteObj</code>: further separation between provider and supplier. </li></ul><h4>&lt;BLOCK 1-A END&gt;</h4><h4>&lt;BLOCK 1-B&gt;</h4><ul><li>LeaseTime 
</li><li>Invoke method on remote object through interface. </li></ul><h4>&lt;BLOCK 1-B END&gt;</h4><h4>&lt;BLOCK 1-C&gt; Asynchronous programming/remoting</h4><ul><li>Illustrates asynchronous calls on remote objects and how to pass parameters into/out of methods exposed by remote object using <code>BeginInvoke</code>, <code>EndInvoke</code>. 
</li><li>Illustrates how to pass user defined object to/from remote object. </li></ul><h4>&lt;BLOCK 1-C END&gt;</h4></blockquote><pre lang="cs">} <span class="cs-comment">//try-A</span><span class="cs-keyword">catch</span>(Exception err)
{
}</pre><h4>&lt;BLOCK 1 END&gt;</h4></blockquote><ul><li>Scenario 2: "client-activated" remote object. </li></ul><blockquote><h4>&lt;BLOCK 2&gt; </h4><pre lang="cs"><span class="cs-keyword">try</span>

{ <span class="cs-comment">//try-B</span></pre><blockquote><h4>&lt;BLOCK 2-A&gt;</h4><ul><li>Illustrates how to configure channel information using config file. 
</li><li>Illustrates how to instantiate remote object with "<code><span class="cpp-keyword">new</span></code>" keyword. </li></ul><h4>&lt;BLOCK 2-A END&gt;</h4><h4>&lt;BLOCK 2-B&gt;</h4><p>Illustrates how to retrieve proxy to remote object via </p><pre lang="cs">Activator.CreateInstance(...)</pre><h4>&lt;BLOCK 2-B END&gt;</h4><h4>&lt;BLOCK 2-C&gt;</h4><ul><li>Illustrate how to invoke method through proxy. 
</li><li>Illustrates how to subscribe to events generated by remote object. </li></ul><h4>&lt;BLOCK 2-C END&gt;</h4></blockquote><pre lang="cs">} <span class="cs-comment">//try-B</span><span class="cs-keyword">catch</span>(Exception err)
{
}</pre><h4>&lt;BLOCK 2 END&gt;</h4></blockquote><pre lang="cs">    } <span class="cs-comment">//Main()</span>
  } <span class="cs-comment">//CRemoteObjClient</span>
} <span class="cs-comment">//nsCRemoteObjClient</span></pre><h2>Code blocks in server source file</h2><ul><li>Block 1: Option 1: programmatic registration of channel and type information thru: </li></ul><pre lang="cs">ChannelServices.RegisterChannel
RemotingConfiguration.RegisterWellKnownServiceType</pre><ul><li>Block 2: Option 2: Configure by loading xml config file: </li></ul><pre lang="cs">RemotingConfiguration.Configure(<span class="cpp-string">"cremoteobjserv.config"</span>);</pre><p>Again, you comment out OPTION 1 if you're using OPTION 2, and vice versa.</p><h2>Walkthrough</h2><ul><li>Remoting Basic, configuration (channel/type): programmatic, config files. LeaseTime, "<code><span class="cpp-keyword">new</span></code>", <code>Activator.GetObject</code> (for server-activated) and<code> Activator.CreateInstance</code> (for client activated) 
</li><li>Config files: See previous section. </li></ul><h4>LeaseTime</h4><ul><li>Change config file, and run application in different activation mode: server-activated SingleCall, server-activated Singleton, client-activated. 
</li><li>Run the client. Comment out &lt;BLOCK 2&gt; and &lt;BLOCK 1-C&gt;. Pay attention that <code>obj.AuthenticateLoser</code> is invoked in &lt;BLOCK 1-B&gt; 
</li><li>Monitor server console and <code>objID</code>. Default LeaseTime is 300-sec for Singleton, if time elapsed between method calls is greater than 300sec, a new instance of remote object gets created each call when remote object runs under the following mode: 
<ul><li>server-activated Singleton. 
</li><li>Client-activated </li></ul></li></ul><p>Lease configuration can be found in server config file:</p><pre lang="xml">&lt;lifetime leaseTime="100MS" sponsorshipTimeout="50MS" 
    renewOnCallTime="100MS" leaseManagerPollTime="10MS" /&gt;</pre><p>In &lt;BLOCK 1-B&gt;, time between consecutive method calls is 101 ms. That's a little over 100 sec. So, every method call should be serviced by a new instance – therefore a different <code>objID</code>. Adjust lease configuration and observe. For server-activated <code>SingleCall</code>, a new instance service every method call.</p><p><b>NOTE</b>: <code>objID</code> is a randomly generated object ID assigned to an instance of remote object <code>CRemoteObj</code> – take a look at constructor.</p><p>"<code><span class="cpp-keyword">new</span></code>" keyword: Instead of <code>Activator.GetObject</code> or<code> Activator.CreateInstance</code>, we can use "<code><span class="cpp-keyword">new</span></code>" to instantiate the remote object. However, if you choose to configure channel and type information via config file, you may instantiate remote object using "new" keyword. However, "<code><span class="cpp-keyword">new</span></code>" implies that you'll be instantiating <code>CRemoteObj</code> – as opposed to interface <code>IRemoteObj</code>. This further implies that your client assembly must reference <code>CRemoteObj</code> assembly.</p><p><code>Activator.GetObject</code>: Take a look at <i>CRemoteObjClient.cs</i> &lt;BLOCK 1-A&gt;</p><pre lang="cs">IRemoteObj obj = (IRemoteObj) Activator.GetObject(
<span class="cs-keyword">typeof</span>(IRemoteObj),
<span class="cpp-string">"http://localhost:8085/CRemoteObjURI"</span>
);</pre><p><code>Activator.CreateInstance</code>: Take a look at <i>CRemoteObjClient.cs </i>&lt;BLOCK 2-B&gt;</p><pre lang="cs"><span class="cs-comment">//Note that it references CRemoteObjServer – not the remote object.</span><span class="cs-keyword">object</span>[] attrs = { <span class="cs-keyword">new</span> UrlAttribute(
  <span class="cpp-string">"tcp://localhost:8086/CRemoteObjServer"</span>) };
ObjectHandle handle = (ObjectHandle) Activator.CreateInstance(
  <span class="cpp-string">"CRemoteObj"</span>, 
  <span class="cpp-string">"nsCRemoteObj.CRemoteObj"</span>, 
  attrs);
CRemoteObj obj = (CRemoteObj) handle.Unwrap();</pre><h3>How to implement interface for remote class, and interact with remote object through interface.</h3><p><b>Interface</b>: <code>IRemoteObj </code>(<i>CRemoteObjInterface.cs</i>)</p><p>If we instantiate remote object via <code>Activator's</code> static methods, it'd be unnecessary for client assembly to reference remote object's assembly. This provides further separation between the remote object and the client. If client side settings are loaded from config file instead, remote object has to be instantiated using "new" keyword. This implies client has to reference remote object's assembly. </p><p>Take a look at the source code. Pay attention to the architecture and assembly references.</p><h3>Passing custom/user-defined objects between remote object and client.</h3><p><b>User defined class</b>: <code>CProfile</code> (defined in <i>CRemoteObjInterface.cs</i>)</p><pre lang="cs"><span class="cs-comment">//ATTENTION: Must be marked Serializable to pass </span><span class="cs-comment">//user-defined object in/out of remote object.</span>
[Serializable] 
<span class="cs-keyword">public</span><span class="cs-keyword">class</span> CProfile
{
  <span class="cs-keyword">public</span><span class="cs-keyword">string</span> fname;
  <span class="cs-keyword">public</span><span class="cs-keyword">string</span> lname;
  <span class="cs-keyword">public</span><span class="cs-keyword">string</span> user;
  <span class="cs-keyword">public</span><span class="cs-keyword">string</span> passwd;
  <span class="cs-comment">//... other stuff...</span>
};</pre><p><b>Method</b>: <code>CRemoteObj.UpdateProfile</code> (defined in <i>CRemoteObj.cs</i>)</p><p>The method takes a <code>CProfile</code> object as argument, and return a <code>CProfile</code> object.</p><pre lang="cs"><span class="cs-keyword">public</span> CProfile UpdateProfile(CProfile p)
{
<span class="cs-comment">//...other stuff...</span><span class="cs-keyword">return</span> pout;
}</pre><p>The method is also exposed by interface <code>IRemoteObj</code>, although it's not necessary. Call to method is trivial. Now, let's take a look at &lt;BLOCK 1-C&gt; in client's source file <i>CRemoteObjClient.cs</i>.</p><pre lang="cs">CProfile p = <span class="cs-keyword">new</span> CProfile(<span class="cpp-string">"John"</span>, <span class="cpp-string">"Kennedy"</span>, <span class="cpp-string">"JFK"</span>, <span class="cpp-string">"ace"</span>);
CProfile p2 = obj.UpdateProfile(p);
Console.WriteLine(<span class="cpp-string">"UpdateProfile completed. return p2 passwd: {0}"</span>, 
    p2.passwd);</pre><h3>Asynchronous method calls on remote object. </h3><p>Take a look at &lt;BLOCK 1-C&gt; in client's source file <i>CRemoteObjClient.cs</i>.</p><pre lang="cs"><span class="cs-comment">//ATTENTION:</span><span class="cs-comment">//Delegate for CRemoteObj.SetupUser:</span><span class="cs-keyword">public</span><span class="cs-keyword">delegate</span><span class="cs-keyword">string</span> SetupUserDelegate(
    <span class="cs-keyword">string</span> string1, <span class="cs-keyword">string</span> string2, <span class="cs-keyword">out</span><span class="cs-keyword">string</span> string3);

<span class="cs-comment">//...other stuff...</span>

Main(...)
{</pre><blockquote><h4>&lt;BLOCK 1-C&gt;</h4><p>Wrap <code>SetupUser</code> in a delegate before calling <code>BeginInvoke</code>:</p><ul><li><code>SetupUserDelegate d = <span class="cpp-keyword">new</span> SetupUserDelegate(obj.SetupUser);</code></li></ul><p>Invoke <code>CRemoteObj.SetupUser</code> asynchronously:</p><pre lang="cs"><span class="cs-keyword">string</span> sTime;
IAsyncResult ar = d.BeginInvoke(
  <span class="cpp-string">"Dexter"</span>,
  <span class="cpp-string">"AceInOpen"</span>,
  <span class="cs-keyword">out</span> sTime,
  <span class="cs-keyword">null</span>,
  <span class="cs-keyword">null</span>
);
<span class="cs-comment">//Blocks the current thread until obj.AuthenticateLoser completes.</span>
ar.AsyncWaitHandle.WaitOne(); 
<span class="cs-keyword">if</span>(ar.IsCompleted)
{
  <span class="cs-comment">//Retrieving output: </span><span class="cs-comment">//(a) "output variable" sTime</span><span class="cs-comment">//(b) "return variable" value.</span><span class="cs-keyword">string</span> ret = d.EndInvoke(<span class="cs-keyword">out</span> sTime, ar);
  Console.WriteLine(<span class="cpp-string">"obj.SetupUser return. sTime:{0}{1}"</span> +
  <span class="cpp-string">"return value: {2}"</span>, sTime, Environment.NewLine, ret);
}

<span class="cs-comment">//...other stuff...</span></pre><h4>&lt;BLOCK 1-C END&gt;</h4></blockquote><pre lang="cs"><span class="cs-comment">//...other stuff...</span>
}</pre><h3>Events and remoting: How client can subscribe to events generated by remote object.</h3><p><img height="310" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image011.png" width="504" border="0" /></p><p>The event handler is declared in the interface module <code>CRemoteObjInterface</code>. </p><p><img height="483" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image013.png" width="593" border="0" /></p><p><b>Routing mechanism</b>: Here's the sequence of things that takes place when client invoke the <code>AuthenticateLoser</code>:</p><p><img height="137" src="http://www.codeproject.com/csharp/DotNetRemotingBasicTutor/image015.png" width="487" border="0" /></p><p>That's it. I hope this article provide a good coverage of most basic maneuvers that would be expected of a component developer. Go through the source code and play around with different configurations. You should be well on your way to building distributed applications and harness power offered by .NET Remoting.</p><h2>References</h2><ul><li>REF-1 .NET Remoting Customization Made Easy by <a href="http://www.codeproject.com/script/profile/whos_who.asp?id=336638">Motti Shaked</a><ul><li><a href="http://www.codeproject.com/csharp/customsinks.asp">http://www.codeproject.com/csharp/customsinks.asp</a></li></ul></li><li>REF-2 MSDN online: Remoting Example: Hosting in Internet Information Services (IIS) 
<ul><li><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconremotingexamplehostinginiis.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconremotingexamplehostinginiis.asp</a></li></ul></li><li>REF-3 MSDN online: Remote Object Configuration 
<ul><li><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconremoteobjectschannels.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconremoteobjectschannels.asp</a></li></ul></li><li>REF-4 MSDN online: Asynchronous Programming Overview 
<ul><li><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp</a></li></ul></li><li>REF-5 Other MSDN references: 
<ul><li>"Asynchronous Delegates Programming Sample" 
</li><li>"Asynchronous Remoting" 
</li><li>"Asynchronous Programming Overview" 
</li><li>"Lifetime Leases" </li></ul></li><li>REF-6 SoapSuds vs. Interfaces in .NET Remoting by Ingo Rammer 
<ul><li><a href="http://www.ingorammer.com/RemotingFAQ/SoapSudsOrInterfaces.html">http://www.ingorammer.com/RemotingFAQ/SoapSudsOrInterfaces.html</a></li></ul></li><li>REF-7 Hosting Remote object in IIS – A Remoting sample by Sandeep Alur 
<ul><li><a href="http://www.developersdex.com/gurus/articles/573.asp?Page=3">http://www.developersdex.com/gurus/articles/573.asp?Page=3</a></li></ul></li><li>REF-8 MSDN online: Remoting Example: Lifetimes 
<ul><li><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconremotingexamplelifetimes.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconremotingexamplelifetimes.asp</a></li></ul></li></ul><!-- Article Ends --><script src="/script/togglePre.js" type="text/javascript"></script><h2>raymond.fung</h2><div style="OVERFLOW: hidden"><table border="0"><tbody><tr valign="top"><td class="smallText" nowrap=""><br /></td><td class="smallText"><p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=540015">here</a> to view raymond.fung's online profile.</p></td></tr></tbody></table></div><img src ="http://www.cnitblog.com/MartinYao/aggbug/21442.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 11:07 <a href="http://www.cnitblog.com/MartinYao/articles/21442.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>.NET Remoting: design decisions and best practicies</title><link>http://www.cnitblog.com/MartinYao/articles/21438.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 02:51:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21438.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21438.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21438.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21438.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21438.html</trackback:ping><description><![CDATA[
		<p>
				<img height="300" src="http://www.codeproject.com/csharp/RemotingDesignDecisions/Untitled-6.jpg" width="400" />
		</p>
		<h2>Introduction</h2>
		<p nd="1">A good architecture and design are necessary for the development of a successful application. The success of an application lies in its robustness and maintainability. These qualities are governed by the design and architectural choices that you make at the start of the project. Using the correct architecture is a key to ensuring the performance and scalability of an application. However, most of the time, you must maintain a balance between performance and scalability, probably favoring one more than the other, depending on the requirements.</p>
		<p nd="2">In this article, you will learn about the factors that will help you to determine whether to trade performance for scalability, or vice-versa. The overhead that is incurred when you provide the ability to scale a distributed application frequently leads to a decrease in performance. When you scale an existing application, you either scale up or scale out. Scaling up, which is most frequently used in client/server scenarios, is the process of adding hardware, such as more or faster RAM, disks, and processors; scaling out is the process of adding more servers. You can use a combination of the two mechanisms. However, if your applications are not designed for processing on multiple computers, you must scale up. Therefore, the issue you face when you design your distributed applications is whether to enable scaling or to focus on the performance of the application. The decision between performance and scalability is not a simple choice between one or the other: a correctly designed distributed application, designed to scale out, can perform better than traditional client/server applications. Generally, however, the more scalable an application is, the more the performance suffers. The hardware that you deploy has a big effect on the design and on the tradeoffs that you must consider.</p>
		<p nd="3">Your design choices affect the scalability and performance of your application. To make the right choices, you must have a clear understanding of the application requirements and the features of .NET Remoting. Taking your requirements into consideration, you must make decisions about the following factors: activation model, channel and formatter, implementing the common assembly, synchronous or asynchronous calls, events, and the default object lifetime.</p>
		<h2>Activation model</h2>
		<p>
		</p>
		<table id="Table3" cellspacing="1" cellpadding="1" border="0">
				<tbody>
						<tr>
								<td>
										<p align="center" nd="4">Stateful one object singleton</p>
								</td>
								<td>
										<p align="center" nd="5">Stateful CAO</p>
								</td>
								<td>
										<p align="center" nd="6">Stateless single-call</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>
												<img height="200" src="http://www.codeproject.com/csharp/RemotingDesignDecisions/Untitled-1.gif" width="171" />
										</p>
								</td>
								<td>
										<p>
												<img height="200" src="http://www.codeproject.com/csharp/RemotingDesignDecisions/Untitled-2.gif" width="195" />
										</p>
								</td>
								<td>
										<p>
												<img height="200" src="http://www.codeproject.com/csharp/RemotingDesignDecisions/Untitled-5.gif" width="200" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p nd="7">The manner in which a remote object is activated has a large effect on the affinity, state management, load balancing, performance, and scalability of your Remoting application. Based on these factors, and whether and how they apply to your architecture, you can implement either server-activated objects (SAO) or client-activated objects (CAO).</p>
		<h4>Server-activated objects</h4>
		<p nd="8">You use SAOs when you have more than one server providing services to clients. When you use SAOs, any one of the available servers that receives the request will be able to service the request. You can use either Single-Call SAOs or Singleton SAOs. You use Single-Call SAOs when you do not have to maintain the state of the objects. On the other hand, if you must perform state management, you use Singleton SAOs.</p>
		<h4>Client-activated objects</h4>
		<p nd="9">CAOs work well when you are using a single-server approach. When you are using a single-server approach, you do not provide load balancing or failover clustering, and you use components that maintain state for their specific client. With CAOs, you can obtain total control over object creation and the lifetime of the remote object.</p>
		<p>
				<img height="200" src="http://www.codeproject.com/csharp/RemotingDesignDecisions/Untitled-8.gif" width="350" />
		</p>
		<h2>Channel and formatter</h2>
		<p nd="10">Choosing the appropriate channel and formatter will affect the speed of message transfer and interoperability. The environment in which you want to deploy your Remoting application determines the channel and formatter that should be used.</p>
		<h3>Channel</h3>
		<p nd="11">In .NET Remoting, there are two channel types to choose from: Transmission Control Protocol (TCP) and Hypertext Transfer Protocol (HTTP).</p>
		<ul>
				<li nd="12">The TCP channel (<code nd="13">TCPChannel</code> class) provides maximum performance for a Remoting solution. The TCP channel does not provide built-in authentication and authorization checks. Therefore, use the TCP channel if your Remoting solution is deployed in a trusted environment without any firewalls. 
</li>
				<li nd="14">Use the HTTP channel (<code nd="15">HTTPChannel</code> class) when you need interoperability or when you need to allow communication through a firewall. </li>
		</ul>
		<h3>Formatter</h3>
		<p nd="16">In .NET Remoting, you have the option of using either the SOAP formatter or the binary formatter. Like the binary formatter, both TCP and HTTP channels can work with SOAP. Choose SOAP when you need interoperability and in applications that cross platform boundaries. The SOAP formatter is not optimal in performance because it is based on XML. This adds a performance overhead because of XML's self-descriptive tags and metatags.</p>
		<p nd="17">Both the TCP and HTTP channels support the binary formatter. Choose the binary formatter when performance is the priority. The binary formatter reduces the size of data transmitted and is therefore faster.</p>
		<table id="Table1" height="135" cellspacing="1" cellpadding="1" width="600" border="1">
				<tbody>
						<tr>
								<td width="50%">
										<b>HTTP channel</b>
								</td>
								<td>
										<b>TCP channel</b>
								</td>
						</tr>
						<tr>
								<td nd="18">Used to communicate over the Internet</td>
								<td nd="19">Used to communicate within an intranet</td>
						</tr>
						<tr>
								<td nd="20">Able to access the existing security systems of the HTTP server</td>
								<td nd="21">Must develop a security system based on the .NET Framework base classes</td>
						</tr>
						<tr>
								<td nd="22">Is less efficient in communication</td>
								<td nd="23">Is more efficient in communication</td>
						</tr>
				</tbody>
		</table>
		<p nd="24"> </p>
		<table id="Table2" height="135" cellspacing="1" cellpadding="1" width="600" border="1">
				<tbody>
						<tr>
								<td width="50%">
										<b>SOAP Formatter</b>
								</td>
								<td>
										<b>Binary Formatter</b>
								</td>
						</tr>
						<tr>
								<td nd="25">Information is sent by using SOAP</td>
								<td nd="26">Information is sent by using binary messages</td>
						</tr>
						<tr>
								<td nd="27">Is ideal for communication between applications that use incompatible architectures</td>
								<td nd="28">Can be read only by .NET Framework applications</td>
						</tr>
						<tr>
								<td nd="29">Is text-based</td>
								<td nd="30">Is binary and compact</td>
						</tr>
				</tbody>
		</table>
		<h2>Implementing the common assembly</h2>
		<p nd="31">Using generic and reusable assemblies enables you to quicken the application development process and, to a degree, allows a successful architecture to be re-used. You can implement a common assembly either by deploying it locally as a private assembly or by embedding the assembly in the Global Assembly Cache (GAC). Choose to implement the common assembly in a GAC when you must provide compatibility between the various versions of your libraries. If both the server and the client use the common assembly, then you can implement it by using interfaces, abstract classes, stub base classes, or Soapsuds-generated proxies.</p>
		<p nd="32">Interfaces are suitable for hiding the implementation from the definition and can be used to avoid deploying the common assembly for the client. Abstract classes have certain characteristics in common with interfaces. However, abstract classes cannot be instantiated, though they can and should contain implementation code. The difference between interfaces and abstract classes, however, is that abstract classes can be passed as parameters to methods in different application domains. Stub base classes are similar to interfaces and abstract classes in that stub base classes are used as the framework of the remote type. However, you can create an instance of a class from a stub base class. The Soapsuds-generated proxies are most frequently used with non-Microsoft Web services that you want to wrap in a proxy. As the name suggests, Soapsuds-generated proxies can be used only with SOAP calls.</p>
		<h3>Options for a Common Assembly:</h3>
		<ul>
				<li nd="33">
						<b>Interfaces</b>: An interface defines a set of properties, methods, and events. Unlike classes, interfaces do not provide implementation. You define an interface as a separate entity from the classes that implement it. When a class implements an interface, it must implement all the methods that are declared in that interface. An interface must define the methods that set and get data. However, the interface will not implement these methods. You use an interface to define a protocol of behavior that any class can implement. Use interfaces to: 
<ul><li nd="34">Capture similarities among unrelated classes without artificially forcing a class relationship. 
</li><li nd="35">Declare the methods that one or more classes are expected to implement. 
</li><li nd="36">Reveal an object’s programming interface without revealing its class. </li></ul></li>
				<li nd="37">
						<b>Abstract base classes</b>: An abstract class has one or more abstract methods. Abstract classes act as expressions of general concepts from which you can derive more classes that are specific. An abstract class serves as a base class from which other classes can inherit properties and methods. You cannot create an object of an abstract class type; however, you can pass references to abstract class types. You use abstract base classes to provide default behavior or to define families. 
</li>
				<li nd="38">
						<b>Stub base classes</b>: You cannot directly instantiate an abstract class or an interface. If you want to create your remote object on the client directly by using the <code lang="cs"><span class="cs-keyword" nd="39">new</span></code> operator, you can define the remote type in the common assembly as a non-abstract class. A stub class is intended as the framework of your remote type; using a stub class is much like using an interface or abstract class. You should still implement the remote type as a class, which is derived from the stub, in the server. Because the stub is not abstract, it is possible to accidentally create a local instance of the stub base class instead of a remote object. 
</li>
				<li nd="40">
						<b>SOAPSuds-generated proxies</b>: SOAPSuds is a command-line tool that generates an assembly containing a proxy class representing the remote object. The SOAPSuds tool can generate code in addition to an assembly. You can also use SOAPSuds to generate a proxy from a remote object hosted on another computer, provided the object is hosted using an HTTP channel. To use a SOAPSuds-generated proxy, your channel must use the SOAP formatter. 
</li>
				<li nd="41">
						<b>Factory design pattern</b>: Most implementations of the Factory pattern use two abstract classes, <code nd="42">Factory</code> and <code nd="43">Product</code>, to provide the invariant interfaces. Although you could use pure interfaces to implement the factory pattern, the use of abstract classes enables you to place common instance behavior in the abstract class. In addition, abstract classes offer versioning benefits over pure interfaces. </li>
		</ul>
		<p nd="44">When implementing the factory design pattern, follow these guidelines:</p>
		<ul>
				<li nd="45">Do not directly create an instance of the CAO on the server. Use an SAO instead, which acts like a factory and returns the actual CAO object to the client. 
</li>
				<li nd="46">Ensure that the SAO factory has one or more methods to create the CAO object. 
</li>
				<li nd="47">Do not expose the implementation of the CAO to the client. Create an interface and let the client use this interface to make calls on the CAO object. 
</li>
				<li nd="48">Do not expose the server code (remote object implementation) to the client. Distribute the interfaces. </li>
		</ul>
		<h2>Synchronous versus asynchronous calls</h2>
		<p nd="49">You can invoke remote calls by using synchronous or asynchronous calls. Choosing the most appropriate remote calling method lets you increase the responsiveness of your application and possibly improve performance.</p>
		<h3>Synchronous calls</h3>
		<p nd="50">Use synchronous calls when you do not need parallel processing of client requests. The advantage of using synchronous calls is that you catch return values and throw exceptions immediately. The disadvantage of using synchronous calls is that the responsiveness of the application is slower because the Remoting server has to wait until all clients acknowledge and then process the callback.</p>
		<h3>Asynchronous calls</h3>
		<p nd="51">If you require good client responsiveness, consider invoking remote method calls asynchronously. For example, use asynchronous calls with Microsoft Windows Forms applications. However, if your client is an ASP.NET application, invoking methods asynchronously and then blocking the calling thread to pick up return values could lead to ThreadPool starvation. Also, if the server goes down, exceptions are thrown at the client. This is because the client is not aware of the state of the server. The only real solution to this problem is a periodic recycling of the client application.</p>
		<h2>Whether to use events</h2>
		<p nd="52">Use of events and callbacks can cause overhead on the server because the server holds references to all the clients that registered an interest in a published service. Regardless of whether you use synchronous or asynchronous calls to register events, problems can occur at either the client or the server. To improve responsiveness, you can use Message Queuing (also known as MSMQ). Message Queuing enables the server to send messages to clients without having to wait for an acknowledgement from the client.</p>
		<h2>Whether to use the default object lifetime</h2>
		<p nd="53">.NET Remoting uses the lease sponsor process to manage the lifetime of an object. The default lifetime of an object is set to five minutes. To determine the appropriate object lifetime for your application, you must strike a balance between the resource usage on the server and the performance implications of frequently destroying and re-creating objects. Following are the general guidelines that you can use when you must change the default lifetime of an object:</p>
		<ul>
				<li nd="54">Use a longer lease time for objects whose creation incurs a high performance overhead. 
</li>
				<li nd="55">If you use objects whose creation incurs a high performance overhead, increase the default lease timeouts so that the object remains longer. 
</li>
				<li nd="56">Use shorter lease times for objects that consume many shared resources. 
</li>
				<li nd="57">If you create objects that use shared resources, use a shorter lease time for the object. When you reduce the default lifetime, the object is destroyed and resources are released quickly. </li>
		</ul>
		<h2>Hosting Options</h2>
		<p nd="58">Hosting remote applications on Microsoft Internet Information Services (IIS) involves less development work than hosting applications in Windows services. However, performance with IIS is slower than with Windows services because hosting applications in IIS adds the overhead of a <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/csharp/remotingdesigndecisions.asp#" target="_blank" itxtdid="3059973">Web server</a>. Also, IIS supports only the HTTP protocol. Because of this limitation, you cannot take advantage of the performance of TCP, which is a protocol with far less overhead. Windows services are useful if the computer does not have an IIS Web server installed on it for security reasons. It is also easier for technical support personnel to manage Windows services and to make sure that the services are running as they should. Finally, if you do not have to host Web pages, using Windows services is a better option because you do not have to maintain a Web server, which requires specific product support knowledge and additional server resources.</p>
		<table id="Table4" cellspacing="1" cellpadding="1" width="100%" border="1">
				<tbody>
						<tr>
								<td width="13%">
										<b>Quality</b>
								</td>
								<td width="15%">
										<b>Command-line windows</b>
								</td>
								<td width="14%">
										<b>Windows Forms</b>
								</td>
								<td width="14%">
										<b>Windows Service</b>
								</td>
								<td width="14%">
										<b>IIS</b>
								</td>
						</tr>
						<tr>
								<td nd="59">Recommended for prototyping</td>
								<td nd="60">yes</td>
								<td nd="61">yes, when using configuration files</td>
								<td nd="62">no</td>
								<td nd="63">no, requires virtual directory setup</td>
						</tr>
						<tr>
								<td nd="64">Automatic start up with Windows</td>
								<td nd="65">no</td>
								<td nd="66">no</td>
								<td nd="67">yes</td>
								<td nd="68">yes, if IIS is set to start auto.</td>
						</tr>
						<tr>
								<td nd="69">Recommended to be used for production</td>
								<td nd="70">no</td>
								<td nd="71">no</td>
								<td nd="72">yes, when you want to have host constant availability</td>
								<td nd="73">yes, when you want fast time production</td>
						</tr>
						<tr>
								<td nd="74">Fault tolerant</td>
								<td nd="75">no</td>
								<td nd="76">no</td>
								<td nd="77">yes, after shut down</td>
								<td nd="78">no</td>
						</tr>
						<tr>
								<td nd="79">Built-in security options</td>
								<td nd="80">no</td>
								<td nd="81">no</td>
								<td nd="82">none</td>
								<td nd="83">SSL, Certifications, built-in authentication</td>
						</tr>
				</tbody>
		</table>
		<h2>Best Practices for Remoting:</h2>
		<h3>Use Remoting for appropriate scenarios</h3>
		<p nd="84">.NET Remoting is the preferred communication mechanism for single process, cross– application domain communication. For communication across processes, across servers, or across deployment or trust boundaries, ASP.NET Web services are generally the recommended option because they are built on industry standards that make it easy to achieve interoperability. However, if state and raw performance—which can be achieved with the TCP channel and the binary formatter—are important, and if interoperability is not a concern, Remoting is a better choice.</p>
		<h3>Design chunky interfaces</h3>
		<p nd="85">Chatty interfaces result in multiple roundtrips to perform a single logical operation. To reduce the number of roundtrips and thereby increase performance, use chunky interfaces. In a chunky interface, you transfer the necessary data in a single roundtrip. In contrast, chatty interfaces are typically attributed by means of properties. This means that you set a number of properties and then run one or more methods. This is generally appropriate for Object Oriented Programming (OOP), but it is not a suitable option for Remoting.</p>
		<h3>Use efficient data types</h3>
		<p nd="86">It is important that you use the most efficient data types with Remoting. For example, if you use a <code nd="87">DataSet</code> object to store data, the object can hold multiple tables and can be serialized and deserialized to Extensible Markup Language (XML) with a single method call. However, you must realize that using <code nd="88">DataSet</code> objects can increase the network traffic because of the serialization and deserialization to XML.</p>
		<h3>Use SingleCall SAOs whenever possible</h3>
		<p nd="89">A Single-Call object has a unique instance for each client request. Therefore, Single-Call objects live only for the life of the method call. These objects do not maintain state information because they are destroyed after servicing the method. Because Single-Call objects do not store state information, they are free from synchronization code and therefore remove the overhead of state management. Single-Call objects hosted in IIS, using the HTTP channel, provide maximum scalability for your .NET Remoting solution.</p>
		<h3>Avoid blocking operations</h3>
		<p nd="90">In Remoting, blocking means that, while a process is running, other processes must wait until the running process finishes execution. Therefore, if many long-running operations are occurring in your application at the same time, many threads are blocked while waiting for the other processes to finish execution. This situation leads to low throughput and low CPU utilization. You must identify any potential tasks that can run at the same time and design your application to support these tasks. Designing your application in this way leads to better request/response performance for your Remoting servers.</p>
		<h3>In trusted server environments, use the TCP channel and the binary formatter</h3>
		<p nd="91">In trusted server environments, you do not have any firewalls. Therefore, for best performance, use the TCP channel with binary formatters. Also, because you have a trusted environment, you do not have to implement additional security mechanisms, such as encryption of data. For example, in an intranet, if all users authenticate against a domain, you could allow access to the Remoting application to all domain users.</p>
		<h3>Control object lifetime and avoid in-memory state management</h3>
		<p nd="92">You must control object lifetimes to be able to effectively manage the creation of objects and to release the resources associated with objects. Also, you must avoid storing state in memory. Poor in-memory state management hampers scalability and performance and might make failover support impossible.</p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>About alaac#</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">iam student of computer science in jordan i have a good intrest in .net remoting and asp.net ,and software engineering, <br />and visual studio.net 2005 ,<br />i have started my programming trip since 2001<br />i hope it will be good with codeproject<br /><p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=1323943">here</a> to view alaac#'s online profile.</p></td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21438.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 10:51 <a href="http://www.cnitblog.com/MartinYao/articles/21438.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebService Probe</title><link>http://www.cnitblog.com/MartinYao/articles/21437.html</link><dc:creator>玄铁剑</dc:creator><author>玄铁剑</author><pubDate>Wed, 03 Jan 2007 02:50:00 GMT</pubDate><guid>http://www.cnitblog.com/MartinYao/articles/21437.html</guid><wfw:comment>http://www.cnitblog.com/MartinYao/comments/21437.html</wfw:comment><comments>http://www.cnitblog.com/MartinYao/articles/21437.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/MartinYao/comments/commentRss/21437.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/MartinYao/services/trackbacks/21437.html</trackback:ping><description><![CDATA[
		<ul class="download">
				<li>
						<a href="http://www.codeproject.com/cs/webservices/WebServiceProbe/WebServiceProbe_src.zip">Download source files - 165 Kb</a>
				</li>
		</ul>
		<p nd="1"> </p>
		<p>
				<b>Contents</b>
		</p>
		<dl>
				<dd nd="2">    <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Introduction">Introduction</a></dd>
				<dd nd="3">    <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Probe Internals">Probe Internals</a></dd>
				<dd nd="4">       <small><a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Concept and Design">Concept and Design</a></small></dd>
				<dd>
						<small nd="5">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Interface contract">Interface contract</a></small>
				</dd>
				<dd>
						<small nd="6">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#What is published">What is published</a></small>
				</dd>
				<dd>
						<small nd="7">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Publisher">Publisher</a></small>
				</dd>
				<dd>
						<small nd="8">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Checkpoints">Checkpoints</a></small>
				</dd>
				<dd>
						<small nd="9">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Plug-in">Plug-in</a></small>
				</dd>
				<dd>
						<small nd="10">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Instalation">Installation</a></small>
				</dd>
				<dd nd="11">    <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Usage">Usage</a></dd>
				<dd>
						<small nd="12">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Subscribers">Subscribers</a></small>
				</dd>
				<dd>
						<small nd="13">         </small>
						<a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#SoapProbeSubscriber2">
								<small>SoapProbeSubscriber2</small>
						</a>
				</dd>
				<dd nd="14">    <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#WebService Analyzer Studio">WebService Analyzer Studio</a></dd>
				<dd>
						<small nd="15">         <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Analyzing First Sample">Analyzing First Sample</a></small>
				</dd>
				<dd nd="16">     <small nd="17">   <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Test environment">Test environment</a></small></dd>
				<dd nd="18">    <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Conclusion">Conclusion</a></dd>
		</dl>
		<h2>
				<a name="Introduction">Introduction </a>
		</h2>
		<p nd="19">Consuming a Web Service in distributed .NET applications is fully transparent using the web service client proxy. When the client invokes a method on the proxy, the SOAP infrastructure packs all information into the message <code nd="20">SoapClientMessage</code> and passes it through the communication network to the <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#" target="_blank" itxtdid="3059973">web server</a>, where its ASP.NET dispatcher forwards the message to the proper worker process. This worker process (aspnet_wp.exe) unpacks the message to obtain the information for invoking a method on the web service. The result of the SOAP call goes back to the caller in the same manner using the message <code nd="21">SoapServerMessage</code>.</p>
		<p nd="22">The SOAP Messages travel through the SOAP infrastructure located on both ends - client and sever. Using the SOAP extension feature makes it easy to plug-in a custom process for each processing stage and publish its state.</p>
		<p nd="23">Subscribing to the WebService Probes, we can obtain a knowledge base of the business workflows, which can be used for their post-processing tasks such as monitoring, analyzing, modeling, simulation, tuning, error catch tracer (like an aircraft black box) etc. </p>
		<p nd="24">This article describes how to design and implement the loosely coupled SoapMessage Publisher (called as the WebService Probe) using the SOAP extensions and COM+ Event System notifications. Also I will give you a lite tool - WebService Analyzer Studio to see how to subscribe and use the Probe notifications and business workflows between the web services and their consumers. For instance, you can catch the consumer call - <code nd="25">HttpRequest</code> and then simulate it many times from the Analyzer. It is a useful SOAP tool for any stage of the development and production environment. </p>
		<p nd="26">Let's look in-depth at the WebService Probe. I am assuming that you have a knowledge of the SOAP and Web Service infrastructure. Also, have a look at the similarl concept of the Probe for remoting, described in my previous article <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#[1]">[1]</a>.</p>
		<h2>
				<a name="Probe Internals">Probe Internals</a>
		</h2>
		<h3>
				<a name="Concept and Design">Concept and Design</a>
		</h3>
		<p nd="27">The concept of loosely coupled message notifications is based on using the COM+ Event Publisher/Subscriber design pattern, where the event call is fired in the proper place of the SOAP Extension. The following picture shows this:</p>
		<p>
				<img height="551" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image002.gif" width="550" border="0" />
		</p>
		<p nd="28">The SOAP ends exchange information using the <code nd="29">SoapClientMessage</code> and <code nd="30">SoapServerMessage</code> respectively in the Request/Response design pattern. ASP.NET has an extensibility mechanism for calling XML web services using SOAP built-in. This feature is based on overriding the <code nd="31">SoapExtension</code> methods to intercept the call at specific stages in the message processing on either the client or the server side.</p>
		<p nd="32">The message can be intercepted in the following processing stages:</p>
		<ul>
				<li>
						<code nd="33">BeforeSerialize</code>
				</li>
				<li>
						<code nd="34">AfterSerialize</code>
				</li>
				<li>
						<code nd="35">BeforeDeserialize</code>
				</li>
				<li>
						<code nd="36">AfterDeserialize</code>
				</li>
		</ul>
		<p nd="37">The Probe uses the <code nd="38">AfterSerialize</code> and <code nd="39">AfterDeserialize</code> stages to publish the SOAP request messsage and SOAP response message at the ends. The publisher fires the events all the time, without any knowledge of the subscriber in the loosely coupled manner. To stop the publishing it is necessary to unregister the Probe from the web service.</p>
		<p nd="40">Note that the <code nd="41">SoapExtension</code> object life is short; only during the Before and After message processing stages at the same end. The <code nd="42">SoapExtension</code> object is initiated each time when the SOAP Message has been created. According to the above picture, there are four different instances of the <code nd="43">SoapExtension</code>, actually each end only creates two of them. This is absolutely correct when we are handling a web service - see more details at the <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#[2]">[2]</a>. </p>
		<p nd="44">The Probe uses the internal SOAP header <code nd="45">_SoapProbeTicket</code> as a passport to travel through all the processing stages at the both ends. This is a logical header per each call (sample session) and it holds the message ID (GUID).</p>
		<p nd="46">The Probe only fires a local event call (that's the limitation of the COM+ Event Service, where an event class and their subscribers have to be on the same machine). Using a persistent subscriber such as <code nd="47">SoapProbeSubscriber</code> (included in this solution), the event call can be forwarded to the Enterprise subscriber using .NET Remoting. </p>
		<h3>
				<a name="Interface contract">Interface contract</a>
		</h3>
		<p nd="48">The contract between the Publisher and Subscriber is represented by the <code nd="49">SoapEventClass</code>, which is an event meta class derived from the <code nd="50">ISoapProbe</code> interface.</p>
		<div class="precollapse" id="premain0" style="WIDTH: 100%">
				<img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" />
				<span id="precollapse0" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="51" preid="0"> Collapse</span>
		</div>
		<pre id="pre0" style="MARGIN-TOP: 0px" nd="52">[Guid(<span class="cpp-string" nd="53">"927B319E-BF79-429b-8CA0-118FBF9724CD"</span>)]
<span class="cpp-keyword">public</span> interface ISoapProbe
{
   [OneWay]
   <span class="cpp-keyword">void</span> WriteLogMessage(
      DateTime timestamp,    <span class="cpp-comment">//timestamp of the sample </span>
      string strSource,      <span class="cpp-comment">//sourcer</span>
      string strDest,        <span class="cpp-comment">//destination - url webservice</span>
      string strAction,      <span class="cpp-comment">//action - webmethod</span>
      string strMsgType,     <span class="cpp-comment">//type of the call</span>
      string strMsgId,       <span class="cpp-comment">//identifier - guid</span>
      object objMsgBody);    <span class="cpp-comment">//http headers + soap envelop</span>
}

[Guid(<span class="cpp-string" nd="54">"C515BB2F-664B-4afa-A40F-CD15D119C7E8"</span>)]
[EventClass]
[Transaction(TransactionOption.NotSupported)] 
[ObjectPooling(Enabled=<span class="cpp-keyword">true</span>, MinPoolSize=<span class="cpp-literal">25</span>, MaxPoolSize=<span class="cpp-literal">100</span>)]
[EventTrackingEnabled]
<span class="cpp-keyword">public</span><span class="cpp-keyword">class</span> SoapEventClass : ServicedComponent, ISoapProbe
{
   <span class="cpp-keyword">public</span><span class="cpp-keyword">void</span> WriteLogMessage(
      DateTime timestamp,    <span class="cpp-comment">//timestamp of the sample </span>
      string strSource,      <span class="cpp-comment">//sourcer</span>
      string strDest,        <span class="cpp-comment">//destination - url webservice</span>
      string strAction,      <span class="cpp-comment">//action - webmethod</span>
      string strMsgType,     <span class="cpp-comment">//type of the call</span>
      string strMsgId,       <span class="cpp-comment">//identifier - guid</span>
      object objMsgBody      <span class="cpp-comment">//http headers + soap envelop</span>
   )
{
   <span class="cpp-keyword">throw</span><span class="cpp-keyword">new</span> Exception(<span class="cpp-string" nd="55">"This class can be called only by the COM+ Event System"</span>);
}</pre>
		<p nd="56">The contract has only one method passing the SOAP Message contents and additional info about the source to the subscriber(s). This method is [OneWay] attributed, which is important for the remoting subscriber. </p>
		<h3>
				<a name="What is published">What is published</a>
		</h3>
		<p nd="57">The Probe publishes a SAOP Extension state using the interface contract via the <code nd="58">WriteLogMessage</code> method with the following arguments, where:</p>
		<ul>
				<li>
						<p nd="60">
								<code nd="59">timestamp</code> is the checkpoint <code nd="61">DateTime</code></p>
				</li>
				<li>
						<p nd="63">
								<code nd="62">strSource</code> is the identifier of the stage, action, error, etc.</p>
				</li>
				<li>
						<p nd="65">
								<code nd="64">strDest</code> is the url address of the web service</p>
				</li>
				<li>
						<p nd="67">
								<code nd="66">strAction</code> is the web method</p>
				</li>
				<li>
						<p nd="69">
								<code nd="68">strMsgType</code> is the type of the event call. There are the following types generated by the Probe: </p>
						<pre nd="70">
								<span class="cpp-keyword">public</span>
								<span class="cpp-keyword">class</span> SoapMessageType
{
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ClientReq = <span class="cpp-string" nd="71">"0-SoapClientRequest"</span>;
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ServerReq = <span class="cpp-string" nd="72">"1-SoapServerRequest"</span>;
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ServerRsp = <span class="cpp-string" nd="73">"2-SoapServerResponse"</span>;
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ClientRsp = <span class="cpp-string" nd="74">"3-SoapClientResponse"</span>;
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ClientReqErr = <span class="cpp-string" nd="75">"0-SoapClientRequest_Err"</span>;
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ServerReqErr = <span class="cpp-string" nd="76">"1-SoapServerRequest_Err"</span>;
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ServerRspErr = <span class="cpp-string" nd="77">"2-SoapServerResponse_Err"</span>;
  <span class="cpp-keyword">public</span><span class="cpp-keyword">const</span> string ClientRspErr = <span class="cpp-string" nd="78">"3-SoapClientResponse_Err"</span>;
}</pre>
				</li>
				<li>
						<p nd="80">
								<code nd="79">strMsgId</code> is the GUID of the session call. Each call has own unique guid generated by the probe and using for all messages related with this call. It's travels in the <code nd="81">SoapProbeTicket</code> header. </p>
				</li>
				<li>
						<p nd="83">
								<code nd="82">objMsgBody</code> represents the log message body. It's a dictionary of the <code nd="84">HttpHeaders</code> and SOAP envelopes.</p>
				</li>
		</ul>
		<p nd="85">The following code snippet shows how <code nd="86">objMsgBody</code> is constructed:</p>
		<div class="precollapse" id="premain2" style="WIDTH: 100%">
				<img id="preimg2" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="2" />
				<span id="precollapse2" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="87" preid="2"> Collapse</span>
		</div>
		<pre id="pre2" style="MARGIN-TOP: 0px" nd="88">
				<span class="cpp-comment">//prepare a log message for its publishing </span>
				<span class="cpp-keyword">private</span>
				<span class="cpp-keyword">void</span> Write(string source, string msgType, string url, string action)
{
   string[] headers = null;
   HttpContext hc = HttpContext.Current;
   <span class="cpp-keyword">if</span>(hc != null) 
   {
      NameValueCollection nvc = HttpContext.Current.Request.Headers;
      headers = <span class="cpp-keyword">new</span> string[nvc.Count];
      <span class="cpp-keyword">for</span>(<span class="cpp-keyword">int</span> ii=<span class="cpp-literal">0</span>; ii&lt;nvc.Count; ii++) 
      {
         headers[ii] = nvc.GetKey(ii) + <span class="cpp-string" nd="89">"="</span> + nvc[ii];
      }
   }

   <span class="cpp-comment">//soap stream</span>
   newStream.Position = <span class="cpp-literal">0</span>;
   StreamReader sr = <span class="cpp-keyword">new</span> StreamReader(newStream);
   string strSoap = sr.ReadToEnd();
   newStream.Position = <span class="cpp-literal">0</span>;

   <span class="cpp-comment">//</span>
   Hashtable htSoapMsg = <span class="cpp-keyword">new</span> Hashtable();
   htSoapMsg[SoapMessageBody.HttpHeaders] = headers;
   htSoapMsg[SoapMessageBody.SoapStream] = strSoap;

   <span class="cpp-comment">//publishing in manner of the fire &amp; forget</span>
   delegatePublisher delegator = <span class="cpp-keyword">new</span> delegatePublisher(Publisher);
   delegator.BeginInvoke(DateTime.Now, source, url, action, msgType, 
                         m_ProbeTicket.ticketId, htSoapMsg, null, null);
}</pre>
		<h3>
				<a name="Publisher">Publisher</a>
		</h3>
		<p nd="90">The Probe Publisher is simply and straightforward code. Its responsibility is to initiate the Event class and invoke the contract method on that class. The following code snippet shows this:</p>
		<div class="precollapse" id="premain3" style="WIDTH: 100%">
				<img id="preimg3" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="3" />
				<span id="precollapse3" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="91" preid="3"> Collapse</span>
		</div>
		<pre id="pre3" style="MARGIN-TOP: 0px" nd="92">
				<span class="cpp-keyword">private</span>
				<span class="cpp-keyword">void</span> Publisher(
      DateTime timestamp,    <span class="cpp-comment">//timestamp of the sample </span>
      string strSource,      <span class="cpp-comment">//sourcer</span>
      string strDest,        <span class="cpp-comment">//destination - url webservice</span>
      string strAction,      <span class="cpp-comment">//action - webmethod</span>
      string strMsgType,     <span class="cpp-comment">//type of the call</span>
      string strMsgId,       <span class="cpp-comment">//identifier - guid</span>
      object objMsgBody      <span class="cpp-comment">//http headers + soap envelop</span>
      ) 
{
   <span class="cpp-keyword">try</span> 
   {
      <span class="cpp-comment">//publishing</span>
      using(SoapEventClass sec = <span class="cpp-keyword">new</span> SoapEventClass()) 
      {
         sec.WriteLogMessage(timestamp, strSource, strDest, strAction, strMsgType,
                             strMsgId, objMsgBody);
      }
   }
   <span class="cpp-keyword">catch</span>(Exception ex) 
   {
      Trace.WriteLine(string.Format(<span class="cpp-string" nd="93">"{0}[{1}]:Publisher catch = {2}"</span>,
                      <span class="cpp-keyword">this</span>.GetType().FullName, <span class="cpp-keyword">this</span>.GetHashCode(), 
                      ex.Message));
   }
}</pre>
		<p nd="94">Note that the Publisher runs in the background process using the delegate design pattern - see the previous code snippet implementation. This solution allows us to isolate the publisher from the subscribers. Because the publishers (worker threads) are running in the thread pool, there is no guarantee of their order in which they have been generated. It means that the Response message can be received before its Request one. So, the contents of the log message such as timestamp, id, and type are efficient to identify their order.</p>
		<h3>
				<a name="Checkpoints">Checkpoints</a>
		</h3>
		<p nd="95">Basically, there are four checkpoints suitable to publish their state marked as 0, 1, 2 and 3. The checkpoint calls the Publisher to make an event call to the Event System in the fire-forget manner.</p>
		<p nd="96">The Probe fires the following events;</p>
		<ul>
				<li nd="97">0 - SoapClientRequest 
</li>
				<li nd="98">1 - SoapServerRequest 
</li>
				<li nd="99">2 - SoapServerResponse 
</li>
				<li nd="100">3 - SoapClientResponse </li>
		</ul>
		<p nd="101">in the AfterSerialize and AfterDeserialize stages of the message processing. The following code snippet shows the checkpoints in the <code nd="102">SoapExtension</code> class:</p>
		<div class="precollapse" id="premain4" style="WIDTH: 100%">
				<img id="preimg4" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="4" />
				<span id="precollapse4" style="MARGIN-BOTTOM: 0px; CURSOR: hand" nd="103" preid="4"> Collapse</span>
		</div>
		<pre id="pre4" style="MARGIN-TOP: 0px" nd="104">
				<span class="cpp-keyword">public</span> override <span class="cpp-keyword">void</span> ProcessMessage(SoapMessage message) 
{
   <span class="cpp-keyword">switch</span> (message.Stage) 
   {
      <span class="cpp-keyword">case</span> SoapMessageStage.BeforeSerialize:
         SoapTicket(message);        <span class="cpp-comment">//passport</span><span class="cpp-keyword">break</span>;

      <span class="cpp-keyword">case</span> SoapMessageStage.AfterSerialize:
         WriteOutput(message);       <span class="cpp-comment">//publish state: 0-SoapClientRequest,</span><span class="cpp-comment">//2-SoapServerResponse</span><span class="cpp-keyword">break</span>;

      <span class="cpp-keyword">case</span> SoapMessageStage.BeforeDeserialize:
         Copy(oldStream, newStream); <span class="cpp-comment">//get the soap stream</span>
         newStream.Position = <span class="cpp-literal">0</span>;
         <span class="cpp-keyword">break</span>;

      <span class="cpp-keyword">case</span> SoapMessageStage.AfterDeserialize:
         SoapTicket(message);        <span class="cpp-comment">//passport </span>
         WriteInput(message);        <span class="cpp-comment">//publish state: 1-SoapServerRequest,</span><span class="cpp-comment">//3-SoapClientResponse</span><span class="cpp-keyword">break</span>;

      <span class="cpp-keyword">default</span>:
         <span class="cpp-keyword">throw</span><span class="cpp-keyword">new</span> Exception(<span class="cpp-string" nd="105">"invalid stage"</span>);
   }
}</pre>
		<h3>
				<a name="Plug-in">Plug-in</a>
		</h3>
		<p nd="106">The Probe can be plugged into the SOAP infrastructure performing in one of the following ways:</p>
		<ul>
				<li>
						<p nd="107">
								<b>programmatically</b> using the <code nd="108">SoapProbeAttribute</code> for the specified web method. In this case the client and server sides need a reference to the <code nd="109">SoapMessageProbe</code> assembly. This is a tight design pattern and hard coded solution. Note that this solution is suitable when only a specified web method on the server is going to be analyzed.</p>
				</li>
				<li>
						<p nd="110">
								<b>administratively</b> using the config file and &lt;soapExtensionTypes&gt; tag in the &lt;webServices&gt; config section. </p>
						<pre nd="111">&lt;configuration&gt;
  &lt;system.web&gt;
    &lt;webServices&gt;
      &lt;soapExtensionTypes&gt;
        &lt;add type=<span class="cpp-string" nd="112">"RKiss.SoapMessageProbe.SoapProbe, SoapMessageProbe, 
                   Version=1.0.1002.16362,Culture=neutral, 
                   PublicKeyToken=719b2bb9abf58c2d"</span> priority=<span class="cpp-string" nd="113">"0"</span> group=<span class="cpp-string" nd="114">"0"</span> /&gt;
      &lt;/soapExtensionTypes&gt;
    &lt;/webServices&gt;
  &lt;/system.web&gt;
&lt;/configuration&gt;</pre>
						<p nd="115">There are two possibilities using the &lt;soppExtensionTypes&gt; in the config file. The first place is to modify the machine.config file, which it will automatically plugged the Probes for all web services on the box. The other option is to modify only an application config file such as the .exe.config or web.config files. I am recommending to use this option, it's controllable and loosely coupled with the application. See the Analyzer solution, where the Probes can be registered/unregistered to/from the specified web service on the fly. </p>
				</li>
		</ul>
		<p nd="116">In the both cases the SOAP infrastructure requires the type of the SOAP extension and access to its assembly (the best way is to install the Probe's assemblies into the GAC). The Probe doesn't have any config properties to be initialized during the c'tor time. It's a stateless object following the web service rules.</p>
		<p nd="117">Note that the changes made in the .exe.config file will take effect after restarting its application. In the other applications such as ASP.NET where a web.config file is used, it's not necessary to manually restart the application - the web server will automatically refresh it.</p>
		<h4>
				<a name="Instalation">Installation</a>
		</h4>
		<p nd="118">The <i>WebServiceProbe</i> solution consists of the following projects:</p>
		<ul>
				<li nd="119">
						<i>SoapMessageProbe.dll</i>: this is the assembly of the SOAP extension 
</li>
				<li nd="120">
						<i>SoapProbeEventClass.dll</i>: this is an assembly of the event meta class and interface contract 
</li>
				<li nd="121">
						<i>SoapProbeSubscriber2.dll</i>this is an assembly of the persistent subscriber to distribute the event calls remotely </li>
		</ul>
		<p nd="122">Additionally there are also projects using the SOAP Probe:</p>
		<ol>
				<li nd="123">
						<i>SoapProbeSubscriber.dll</i> - Analyzer subscriber 
</li>
				<li nd="124">
						<i>WebServiceAnalyzerStudio.exe</i> - GUI to explore the SOAP Messages published by the Probes </li>
		</ol>
		<h3>Installation steps:</h3>
		<ol>
				<li>
						<p nd="125">Launch the <i>InstallSoapProbe.bat</i> file to perform installation of the assemblies into the GAC and registry of the Enterprise Services in the COM+ Catalog.</p>
				</li>
				<li>
						<p nd="126">Create administratively a persistent subscription for the <code nd="127">SoapProbeSubscriber</code> using the COM+ Explorer and Subscription Wizard. See the following picture:</p>
				</li>
		</ol>
		<p>
				<img height="353" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image002.jpg" width="491" border="0" />
		</p>
		<p nd="128">At this time, the WebService Probe is installed on your machine and ready to use it. But ..., you will see no response from the Probe until you plug the Probe into the application and its subscriber is launched (locally or remotely).</p>
		<p nd="129">The first condition has been described <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Plug-in">earlier</a>, now we'll look at the second one - Subscriber - more closely.</p>
		<h2>
				<a name="Usage">Usage</a>
		</h2>
		<p nd="130">The WebService Probe usage is dependant on its Subscriber. The Probe without using any subscriber is a passive publisher to fire an event for "null". Let's look at for them:</p>
		<h3>
				<a name="Subscribers">Subscribers</a>
		</h3>
		<p nd="131">There can be many different subscribers designed and implemented for the WebService Probes. From the simplest one, (like writing the messages to the file system) to more complicated business control loops based on the strobed business knowledge base. Some solutions can be leaded to a more sophisticated model than the main application.</p>
		<p nd="132">Basically, the Subscribers are divided into the following two groups:</p>
		<ul>
				<li nd="133">
						<b>passive</b>, where received information is used for post-processing such as monitoring, analyzing, modelling, simulation and other off-line tools. 
</li>
				<li nd="134">
						<b>active</b>, where received information is used to control business model - its workflow such as tuning, balancing and etc. - on-line solutions. </li>
		</ul>
		<p nd="135">I included one simple Subscriber to distribute the event call on the <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#" target="_blank" itxtdid="1664673">Enterprise Network</a> using .NET Remoting. Here are some details:</p>
		<h3>
				<a name="SoapProbeSubscriber2">SoapProbeSubscriber2</a>
		</h3>
		<p nd="136">This is a poolable <code nd="137">ServicedComponent</code> derived from the <code nd="138">ISoapProbe</code> interface. The object is initialized by the constructor string which represents a URL address of the remoting server object. Based on this string the remoting channel is registered either programmatically or administratively using the config file. Note that this component is activated as a server, so its host process is <i>dllhost.exe</i> and that's the reason why the config file is located in the <i>%WINDIR%\system32</i> folder. </p>
		<p>The Event method received the call from the Probe publisher and then forwarding it to the remoting infrastructure. It looks like a small bridge between the event system and remoting. Here is its implementation:</p>
		<div class="precollapse" id="premain6" style="WIDTH: 100%">
				<img id="preimg6" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="6" />
				<span id="precollapse6" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="6"> Collapse</span>
		</div>
		<pre id="pre6" style="MARGIN-TOP: 0px">
				<span class="cpp-preprocessor">#region ISoapProbe</span>
   [OneWay]
   <span class="cpp-keyword">public</span><span class="cpp-keyword">void</span> WriteLogMessage(
        DateTime timestamp, <span class="cpp-comment">//timestamp of the sample </span>
        string strSource, <span class="cpp-comment">//sourcer</span>
        string strDest, <span class="cpp-comment">//destination - url webservice</span>
        string strAction, <span class="cpp-comment">//action - webmethod</span>
        string strMsgType, <span class="cpp-comment">//type of the call</span>
        string strMsgId, <span class="cpp-comment">//identifier - guid</span>
        object objMsgBody <span class="cpp-comment">//http headers + soap envelop</span>
        )
{
   <span class="cpp-keyword">try</span> 
   {
      <span class="cpp-comment">//fire &amp; forget</span>
      DelegateWriteLogMessage sn = <span class="cpp-keyword">new</span> DelegateWriteLogMessage(onNotify);
      sn.BeginInvoke(timestamp, strSource, strDest, strAction, strMsgType,
                      strMsgId,objMsgBody, null, null);
   }
   <span class="cpp-keyword">catch</span>(Exception ex) 
   {
      string msg = string.Format(<span class="cpp-string">"Subscriber2.WriteLogMessage failed. Error={0}"</span>,
                                 ex.Message); 
      Trace.WriteLine(msg);
   }
} 

<span class="cpp-keyword">private</span><span class="cpp-keyword">void</span> onNotify(
       DateTime timestamp, <span class="cpp-comment">//timestamp of the sample </span>
       string strSource, <span class="cpp-comment">//sourcer</span>
       string strDest, <span class="cpp-comment">//destination - url webservice</span>
       string strAction, <span class="cpp-comment">//action - webmethod</span>
       string strMsgType, <span class="cpp-comment">//type of the call</span>
       string strMsgId, <span class="cpp-comment">//identifier - guid</span>
       object objMsgBody <span class="cpp-comment">//http headers + soap envelop</span>
       )
{
   <span class="cpp-keyword">try</span>
   {
      ISoapProbe robj = (ISoapProbe)Activator.GetObject(typeof(ISoapProbe), 
                                                        strTargetUrl);
      robj.WriteLogMessage(timestamp, strSource, strDest, strAction,
                            strMsgType, strMsgId, objMsgBody);
   }
   <span class="cpp-keyword">catch</span>(Exception ex) 
   {
      string msg = string.Format(<span class="cpp-string">"Subscriber2.onNotify failed. Error={0}"</span>, 
                                 ex.Message); 
      Trace.WriteLine(msg);
   }
}
<span class="cpp-preprocessor">#endregion</span></pre>
		<p>As you can see, the call spawns the worker thread to invoke the remoting method. Note that the remoting method is an <code>[Oneway]</code> attributed, so there is no waiting time for its response.</p>
		<p>As a target object any remoting object derived from <code>ISoapProbe</code> using the standard (tcp/http) or custom channels such as MSMQ, WebService, etc. can be used: see my articles <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#[3]">[3]</a> and <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#[4]">[4]</a>.</p>
		<p>How powerful is the SOAP details published by the Probe is shown in my included solution called WebService Analyzer Studio? Here are more details:</p>
		<h2>
				<a name="WebService Analyzer Studio">WebService Analyzer Studio</a>
		</h2>
		<p>The WebService Analyzer Studio is the GUI Subscriber to the WebService Probe(s) to explore the SOAP messages between the web services and their consumers. It has been designed and implemented (using the "as it is" methodology) to demonstrate power of the published knowledge base by SOAP Probe. It can be used for developing, testing and tracing the web service issues during the product lifecycle. Note that the Analyzer is a fully isolated, loosely coupled design pattern from your web services and their consumers.</p>
		<p>
				<img height="565" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image004.gif" width="550" border="0" />
		</p>
		<p>The major features of the WebService Analyzer Studio:</p>
		<ul>
				<li>
						<p>plug-in/unplug the Probes to/from the applications and web services on the fly</p>
				</li>
				<li>
						<p>storing the incoming samples from the Soap Probes in the TreeView layout</p>
				</li>
				<li>
						<p>exploring the samples (source, headers, soap, etc.)</p>
				</li>
				<li>
						<p>simulate the captured Request sample</p>
				</li>
				<li>
						<p>timestamp measurement</p>
				</li>
				<li>
						<p>soap message control flow</p>
				</li>
				<li>
						<p>storing samples in the circulated manner</p>
				</li>
				<li>
						<p>filtering samples on Error</p>
				</li>
				<li>
						<p>forwarding incoming samples to the remoting object</p>
				</li>
				<li>
						<p>subscribing to the Event System locally and remotely using the loosely coupled design pattern</p>
				</li>
				<li>
						<p>customizing the Analyzer behaviour on the fly</p>
				</li>
				<li>
						<p>store/load the customized option to/from the xml file</p>
				</li>
				<li>
						<p>it's free</p>
				</li>
		</ul>
		<p>The concept of the WebService Analyzer Studio is based on exploring the incoming event calls into the TreeView layout. Basically, there are two kinds of panels with the splitter. The left panel is dedicated to the probes, simulation of the SOAP Request calls and customizing. The second one, on the right side is for storing the incoming samples: event calls into the TreeView layout for the post-processing examination.</p>
		<h3>Probes Tab</h3>
		<p>The following screen shoot shows the list of the applications where the SOAP Probe has been registered or unregistered. The Analyzer will take care to modified or created config file for this application. The original config file (if it exists) is renamed for backup purposes. The TreeView nodes are persisted into the analyzer <i>xml</i> file and loaded during the start up process.</p>
		<p>
				<img height="341" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image102.jpg" width="550" border="0" />
		</p>
		<h3>Simulation Tab</h3>
		<p>The incoming Request samples (client or server) can be selected for their simulation. This selection will make a send a copy of the sample to the Simulator Tab:</p>
		<p>
				<img height="341" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image202.jpg" width="550" border="0" />
		</p>
		<p>Clicking on the Run selection of the pop-menu of the <code>HttpRequest</code> node causes the POST Request to be sent to the web service. Its SOAP Envelope is a copy of the original one. Based on this Request call, the simulator will receive <code>HttpResponse</code> - see the above screen shoot. This is a great feature of the Analyzer, which allows to simulate a client's request in the post-processing manner. For instance, we can simulate the Request calls which cause the exception on the web method to debug the business rules, validation, etc. in the web service.</p>
		<h3>Customize Tab</h3>
		<p>Some Analyzer properties can be customized using the following <code>PropertyGrid</code> control. </p>
		<p>
				<img height="341" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image302.jpg" width="550" border="0" />
		</p>
		<p>You can configure storing the samples into the TreeView control such as number of samples, circulation mode, filtering on error or keeping the original formatted SOAP Envelop. For instance: the Analyzer can be hooked to the web service and store the SOAP error messages only in circulated storage with a specified number of samples.</p>
		<p>Note that this feature is opened to enhance the Analyzer option. For instance, the triggering option can wait for a specified sample and then start or stop the storing samples in the storage.</p>
		<h3>Samples panel</h3>
		<p>This right-hand panel is dedicated to storing the incoming samples into the TreeView layout based on the general or the action node (web method node) option. All nodes are read-only. The TreeView layout is organized like is shown in the following screen shoot:</p>
		<p>
				<img height="513" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image402.gif" width="550" border="0" />
		</p>
		<p>The samples can be expanded to show their contents including name, schema, value, etc. The first SOAP message initiates the sample session identified by the unique ID (GUID). </p>
		<p>Some interesting pop-menu features:</p>
		<ul>
				<li>
						<p>
								<b>F2 Option</b> - allows to modify properties</p>
				</li>
				<li>
						<p>
								<b>F7 Simulate</b> - request to simulate this message</p>
				</li>
				<li>
						<p>
								<b>F9 Mark</b> - mark the sample timestamp as a reference point for the time measurement between the samples. This feature allows to see timing control flow between any samples in milliseconds. It's valuable information when a performance issue is arising.</p>
				</li>
		</ul>
		<p>The Analyzer can be disconnected from the incoming samples by pressing the Master Switch on the status bar. The green icon indicates that the analyzer is connected to the Probe (either via the Event System or Remoting).</p>
		<p>That's all for the WebService Analyzer Studio and I hope you will enjoy it. Now it's the time to make a first sample analyzing.</p>
		<h3>
				<a name="Analyzing First Sample">Analyzing First Sample</a>
		</h3>
		<p>I built the TestSample solution to demonstrate capability of the WebService Analyzer Studio. You can fond it in the separate folder <i>\Test</i>:</p>
		<ol>
				<li>TestWebService - ASP.NET Web Service 
</li>
				<li>TestWebForm - ASP.NET Web Application (client) 
</li>
				<li>TestWinForm - Windows Application (client) </li>
		</ol>
		<p>The Probe and Analyzer have been implemented using .NET SP2 and tested on Windows 2000 Advanced Server and Windows 2000 Professional. It should be work without any problem also on XP machines. </p>
		<p>Before using this test, the ASP.NET projects have to be deployed and installed on your web server. The TestSample solution is very simple like the "Hello world". The client invokes the web methods <code>Test</code> or <code>Echo</code> with many different types of the arguments included <code>DataSet</code>. </p>
		<h3>
				<a name="Test environment">Test environment</a>
		</h3>
		<p>First of all, it's necessary to have a functional test environment for TestSample solution without using the SOAP Probe and Analyzer. Be sure that their application config files have no plug-in of the <code>SoapMessageProbe</code> type in the webServices tag. Then try to make a quick test pressing the Test or Echo button on the Windows and Web Clients. After that, we can step-in to the using the SOAP Probe, (I am assuming that the <code>SoapMessageProbe</code> has been already installed - see <a href="http://www.codeproject.com/cs/webservices/webserviceprobe.asp#Instalation">Instalation</a>).</p>
		<p>There can be more scenarios to analyze the Web Service consuming. Depending from the Probe locations there can be the following major scenarios:</p>
		<h4>I. SOAP Probe on the client side</h4>
		<p>
				<img height="313" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image503.gif" width="550" border="0" />
		</p>
		<p>This is a simple scenario analyzing the web service consumer side - the Windows Application. The SOAP Probe is plugged-in to the SOAP extension of the web service client proxy. Here are the test steps:</p>
		<ol>
				<li>
						<p>Be sure that the applications such as <code>SoapProbeEventClass</code> and <code>SoapProbeSubscriber</code> are in the COM+ Catalog.</p>
				</li>
				<li>
						<p>Launch the WebServiceAnalyzerStudio program</p>
				</li>
				<li>
						<p>Be sure that the Analyzer is subscribed to the Event System (check the tooltip icon on the status bar)</p>
				</li>
				<li>
						<p>Select the Probes Tab</p>
				</li>
				<li>
						<p>Modify your Windows application path (full path name of the assembly, no extension)</p>
				</li>
				<li>
						<p>Click on <i>Register</i> on the pop-menu</p>
				</li>
				<li>
						<p>If the Registration succeeded, the Probe has been plugged-in to <i>TestWindowsForm.exe.config</i>.</p>
				</li>
				<li>
						<p>In this moment, the test environment is ready to make a test</p>
				</li>
				<li>
						<p>Press the <i>Test</i> or <i>Echo</i> Button on the client program</p>
				</li>
				<li>
						<p>You should see two received samples by the Analyzer (Samples panel).</p>
				</li>
				<li>
						<p>Now, the samples can be explored.</p>
				</li>
				<li>
						<p>Select the node: <code><span class="cpp-literal">0</span>-SoapClientRequest</code></p>
				</li>
				<li>
						<p>Click <i>Simulate</i> on the pop-menu</p>
				</li>
				<li>
						<p>Go to the Simulation panel and select the node: HttpRequest</p>
				</li>
				<li>
						<p>Click the <i>Run</i> on the pop-menu</p>
				</li>
				<li>
						<p>You should see the <code>HttpResponse</code> on the Simulation panel. Note that the Simulation using the POST/GET requests communication to the web service, so there are no samples published by the probe.</p>
				</li>
				<li>
						<p>Repeat step 9 to capture more samples by the Analyzer.</p>
				</li>
		</ol>
		<h3>II. SOAP Probe on the Web Server side</h3>
		<p>
				<img height="452" src="http://www.codeproject.com/cs/webservices/WebServiceProbe/image604.gif" width="550" border="0" />
		</p>
		<p align="left">This scenario requires access to the Web Server and a decision where the Analyzer has to be located. There are the following choices:</p>
		<ol>
				<li>
						<p>Web Server - the solution is using the local Event System</p>
				</li>
				<li>
						<p>Local Network - the solution is using the persistent subscription. Be sure it's enabled and the constructor string is addressed to your remote machine)</p>
				</li>
				<li>
						<p>Web Server and Network - the solution is using the local Event System and persistent subscription</p>
				</li>
		</ol>
		<p>Let's select the first choice. The test steps are:</p>
		<ol>
				<li>
						<p>Be sure that the applications such as <code>SoapProbeEventClass</code> and <code>SoapProbeSubscriber</code> are in the COM+ Catalog.</p>
				</li>
				<li>
						<p>Launch the WebServiceAnalyzerStudio program</p>
				</li>
				<li>
						<p>Be sure that the Analyzer subscribed to the Event System (check the tooltip icon on the status bar)</p>
				</li>
				<li>
						<p>Select the Probes Tab</p>
				</li>
				<li>
						<p>Modify your web service and web application path (full path name of the assembly, no extension)</p>
				</li>
				<li>
						<p>Click <i>Register</i> on the pop-menu for each of them Application nodes</p>
				</li>
				<li>
						<p>If the Registration succeeded, the Probe has been plugged-in to their <i>web.config</i> files</p>
				</li>
				<li>
						<p>In this moment, the test environment is ready to make a test</p>
				</li>
				<li>
						<p>Press the <i>Test</i> or Echo <i>Button</i> on the client program</p>
				</li>
				<li>
						<p>You should see, four received samples by the Analyzer (Samples panel).</p>
				</li>
				<li>
						<p>Now, the samples can be explored.</p>
				</li>
				<li>
						<p>Select the node: <i>0-SoapClientRequest</i> or <i>1-SoapServerRequest</i></p>
				</li>
				<li>
						<p>Click <i>Simulate</i> on the pop-menu</p>
				</li>
				<li>
						<p>Go to the Simulation panel and select the node: <code>HttpRequest</code></p>
				</li>
				<li>
						<p>Click <i>Run</i> on the pop-menu</p>
				</li>
				<li>
						<p>You should see the <code>HttpResponse</code> on the Simulation panel and captured samples by the Analyzer.</p>
				</li>
				<li>
						<p>Repeat the step 9 to capture more samples by the Analyzer.</p>
				</li>
		</ol>
		<p>When the above test is working, you can close the Analyzer program and launch it on the Local Network using the .NET Remoting feature. This case will require you to enable the persistent subscription <code>SoapProbeSubscriber</code> and set it up properly such that the URL address of the remote Analyzer located in the Construct string is correct.</p>
		<h2>Major .NET technologies and techniques used in this solution</h2>
		<ul>
				<li>
						<p>XML Web Services</p>
				</li>
				<li>
						<p>SOAP and SOAP extensions</p>
				</li>
				<li>
						<p>COM+ Event System</p>
				</li>
				<li>
						<p>Remoting</p>
				</li>
				<li>
						<p>Windows Form</p>
				</li>
				<li>
						<p>TreeView Controls</p>
				</li>
				<li>
						<p>
								<code>XmlDocument</code>
						</p>
				</li>
				<li>
						<p>
								<code>HttpRequest</code>
						</p>
				</li>
				<li>
						<p>Multi-threading techniques</p>
				</li>
				<li>
						<p>Updating GUI in the background process</p>
				</li>
				<li>
						<p>Loosely coupled design patterns</p>
				</li>
		</ul>
		<h2>
				<a name="Conclusion">Conclusion</a>
		</h2>
		<p>Using LCE WebService Probes in the Distributed Application Framework to strobe the business workflow gives you an additional post-processing Knowledge Base which can be used for many things. This is an incremental development pyramid based on developing the suitable subscribers. In this article, I presented one of them - WebService Analyzer Studio - which is a small tool to examine the contents of the SOAP Messages.</p>
		<h2>References:</h2>
		<p>
				<a name="[1]">[1]</a>
				<a href="http://www.codeproject.com/useritems/RemotingProbe.asp">http://www.codeproject.com/useritems/RemotingProbe.asp</a>
		</p>
		<p>
				<a name="[2]">[2]</a>
				<a href="ms-help://MS.NETFrameworkSDK/cpguidenf/html/cpconalteringsoapmessageusingsoapextensions.htm">//MS.NETFrameworkSDK/cpguidenf/html/cpconalteringsoapmessageusingsoapextensions.htm</a>
		</p>
		<p align="left">
				<a name="[3]">[3]</a>
				<a href="http://www.codeproject.com/csharp/msmqchannel.asp">http://www.codeproject.com/csharp/msmqchannel.asp</a>
		</p>
		<p align="left">
				<a name="[4]">[4]</a>
				<a href="http://www.codeproject.com/cs/webservices/remotingoverinternet.asp">http://www.codeproject.com/cs/webservices/remotingoverinternet.asp</a>
		</p>
		<p align="center"> </p>
		<!-- Article Ends -->
		<script src="/script/togglePre.js" type="text/javascript">
		</script>
		<h2>Roman Kiss</h2>
		<div style="OVERFLOW: hidden">
				<table border="0">
						<tbody>
								<tr valign="top">
										<td class="smallText" nowrap="">
												<br />
										</td>
										<td class="smallText">
												<p class="smallText">Click <a href="http://www.codeproject.com/script/profile/whos_who.asp?vt=arts&amp;id=24570">here</a> to view Roman Kiss's online profile.</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.cnitblog.com/MartinYao/aggbug/21437.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/MartinYao/" target="_blank">玄铁剑</a> 2007-01-03 10:50 <a href="http://www.cnitblog.com/MartinYao/articles/21437.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>