<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5896148192050476388</id><updated>2012-01-07T12:25:08.380-08:00</updated><category term='C#'/><category term='Reflection'/><category term='TEMPer'/><category term='ASP.NET'/><category term='Web Services'/><category term='USB'/><category term='.NET'/><title type='text'>No Feature Left behind</title><subtitle type='html'>.NET/C# Development</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.no-feature.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://www.no-feature.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Fraschetti</name><uri>http://www.blogger.com/profile/00603425736088406167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_2QtvV7pKOM4/SauCNDGCk6I/AAAAAAAAABE/3pWcfeTx0Z0/S220/Brazil+Pics+057.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>4</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5896148192050476388.post-242128904697761645</id><published>2009-03-10T01:24:00.000-07:00</published><updated>2009-03-10T18:59:55.762-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Services'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Reflection'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Using reflection to hack .NET Web Services (Adding backwards compatibility)</title><content type='html'>ASP.NET Web Services provide a quick and easy way to implement a Web Service but they are not without their shortcomings. The high level nature of ASP.NET Web Services sadly means that some of the low level functionality required to produce a supportable and maintainable Web Service is missing. Because this missing functionality, small changes actually turn out to be breaking changes.&lt;br /&gt;&lt;br /&gt;Here a few breaking changes that this post will address:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. Renaming of the .asmx Web Service&lt;/strong&gt; - Product names change, one product becomes two and sometimes developers just get bored. Regardless of the reason, changing the .asmx Web Service name/filename will break all existing Web Service consumers.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. Renaming a specific Web Service method&lt;/strong&gt; - Typically the easiest solution here is to add a wrapper with the original name that can be phased out after existing Web Service consumers have made the transition. To cover all basis(and because some of you out there might not have the luxury of exposing two methods that have the same functionality) I've included this scenario in this post.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3. Renaming a parameter of a Web Service method&lt;/strong&gt; - As Web Services as essentially APIs, developers tend to name Web Service method parameters to coincide with their internal parameter names. While this obviously makes the developer's lives a bit easier (consistency is crucial for a developer), consumers of the Web Services may know those parameters by different names (a business web application developer will often use different terminologies for metrics than the business web site developer -- which of course if fine unless the web developer writes a web service intended for the business developer).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4. Adding a new parameter to a Web Service method&lt;/strong&gt; - .NET Web Services require all parameters to supplied when the method is called. To add a new parameter to a Web Service method without breaking existing deployments requires creating a wrapper with the original to overload the new method with the new parameters. As with renaming a Web Service method, phasing out method wrappers created for compatibility can be a documentation and supportability nightmare. A better alternative (and the #1 feature I wish ASP.NET Web Services had) is to implement default values for Web Service parameters. The overloaded method you create will ultimately pass in a default value anyways, so why not have a formal way of providing that same default to the Web Service method (therefore eliminating the need for the wrapper)?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This post is meant purely for educational purposes. Any decisions/code modifications/problems that develop as a result of this post are 100% not my responsibility (it's best to fully understand what the code below is doing before implementing it).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This post will only cover one potential solution to the aforementioned issues. While I won't go into the details of how I determined what fields of the ASP.NET objects needed to be touched, what I will say (as it might provide extremely useful to you) is that if you are using a .NET library (ASP.NET is basically just a large 3rd-party library sitting atop IIS) and are either having issues or simply want to know how something is implemented, give &lt;a href="http://www.red-gate.com/products/reflector/"&gt;.NET Reflector&lt;/a&gt; a try. With .NET Reflector, you can decompile .NET DLLs into a human readable (and compilable) form.&lt;br /&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;strong&gt;A simple Web Service (a starting point):&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;The following is a sample HTTP GET request and response. &lt;br /&gt;The placeholders shown need to be replaced with actual values.&lt;br /&gt;&lt;br /&gt;GET /SampleWebService/SimpleService.asmx/SimpleMethod?&lt;br /&gt;StrParam=string&amp;IntParam=string&amp;BoolParam=string&amp;EnumParam=string HTTP/1.1&lt;br /&gt;&lt;/pre&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;enum&lt;/span&gt; ENUM_ITEMS&lt;br /&gt;    {&lt;br /&gt;        ITEM1 = 1,&lt;br /&gt;        ITEM2 = 2,&lt;br /&gt;        ITEM3 = 3&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    [WebMethod]&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; SimpleMethod(String StrParam, &lt;span class="kwrd"&gt;int&lt;/span&gt; IntParam, &lt;span class="kwrd"&gt;bool&lt;/span&gt; BoolParam, ENUM_ITEMS EnumParam)&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="str"&gt;"The Params: "&lt;/span&gt;&lt;br /&gt;            + &lt;span class="str"&gt;"StrParam="&lt;/span&gt; + StrParam + &lt;span class="str"&gt;", "&lt;/span&gt;&lt;br /&gt;            + &lt;span class="str"&gt;"IntParam="&lt;/span&gt; + IntParam + &lt;span class="str"&gt;", "&lt;/span&gt;&lt;br /&gt;            + &lt;span class="str"&gt;"BoolParam="&lt;/span&gt; + BoolParam + &lt;span class="str"&gt;", "&lt;/span&gt;&lt;br /&gt;            + &lt;span class="str"&gt;"EnumParam="&lt;/span&gt; + EnumParam;&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;A standard request:&lt;pre&gt;http://localhost/SampleWebService/SimpleService.asmx/SimpleMethod?StrParam=TestString&amp;IntParam=9&amp;BoolParam=false&amp;EnumParam=ITEM3&lt;/pre&gt;The return value:&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;="utf-8"&lt;/span&gt; ?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://tempuri.org/"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;The Params: StrParam=TestString, IntParam=9, BoolParam=False, EnumParam=ITEM3&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What the follow code will allow:&lt;br /&gt;&lt;br /&gt;&lt;Strong&gt;1. &lt;/Strong&gt;Request to an alternate Web Service (OldWebService.asmx instead of SimpleService.asmx):&lt;pre&gt;http://localhost/SampleWebService/&lt;span style="color: #ff0000"&gt;&lt;Strong&gt;OldWebService.asmx&lt;/Strong&gt;&lt;/span&gt;/SimpleMethod?StrParam=TestString&amp;IntParam=9&amp;BoolParam=false&amp;EnumParam=ITEM3&lt;/pre&gt;&lt;br /&gt;&lt;Strong&gt;2. &lt;/Strong&gt;Request to an alternate Web Service method (TheOldMethod instead of SimpleMethod):&lt;pre&gt;http://localhost/SampleWebService/SimpleService.asmx/&lt;span style="color: #ff0000"&gt;&lt;Strong&gt;TheOldMethod&lt;/Strong&gt;&lt;/span&gt;?StrParam=TestString&amp;IntParam=9&amp;BoolParam=false&amp;EnumParam=ITEM3&lt;/pre&gt;&lt;br /&gt;&lt;Strong&gt;3. &lt;/Strong&gt;Request with an alternate parameter name (StringParam instead of StrParam):&lt;pre&gt;http://localhost/SampleWebService/SimpleService.asmx/SimpleMethod?&lt;span style="color: #ff0000"&gt;&lt;Strong&gt;StringParam&lt;/Strong&gt;&lt;/span&gt;=TestString&amp;IntParam=9&amp;BoolParam=false&amp;EnumParam=ITEM3&lt;/pre&gt;&lt;br /&gt;&lt;Strong&gt;4. &lt;/Strong&gt;Request using the default StrParam (DefaultString) and EnumParam (ITEM2) values:&lt;pre&gt;http://localhost/SampleWebService/SimpleService.asmx/SimpleMethod?IntParam=9&amp;BoolParam=false&lt;/pre&gt;The return value:&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;="utf-8"&lt;/span&gt; ?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://tempuri.org/"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;The Params: StrParam=DefaultString, IntParam=9, BoolParam=False, EnumParam=ITEM2&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;Strong&gt;5. &lt;/Strong&gt;Putting it all together - a request to an alternate Web Service, an alternate Web Service method and with no parameters (using all default parameter values):&lt;pre&gt;http://localhost/SampleWebService/OldWebService.asmx/TheOldMethod&lt;/pre&gt;The return value:&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;="utf-8"&lt;/span&gt; ?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://tempuri.org/"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;The Params: StrParam=DefaultString, IntParam=11, BoolParam=True, EnumParam=ITEM2&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The following solution intercepts the ASP.NET request object before ASP.NET reads the request variables to determine what Web Service and Web Service method to call and modifies the request so that ASP.NET will call the Web Service and Web Service method with the parameters of our choosing.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Scroll to the bottom of this post to download the entire solution (including the Web Service and preprocessor code).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This snippet is the preprocessor initialization code that will create a Web Service alias, a method alias, parameter aliases and parameter defaults.&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; m_InitLock = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;String, String&amp;gt; m_WebServiceMappings = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt; m_MethodMappings = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt;&amp;gt; m_ParameterMappings = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt;&amp;gt; m_ParameterDefaults = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Init()&lt;br /&gt;{&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (m_ParameterMappings != &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;lock&lt;/span&gt; (m_InitLock)&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (m_ParameterMappings != &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        m_WebServiceMappings = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, String&amp;gt;();&lt;br /&gt;        m_MethodMappings = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt;();&lt;br /&gt;        m_ParameterMappings = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt;&amp;gt;();&lt;br /&gt;        m_ParameterDefaults = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt;        String RawWebServiceName, WebServiceName, RawMethodName, MethodName;&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// The following will:&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// 1. Allow Web Service requests the Web Service 'OldWebService.asmx' to be remapped to 'SimpleService.asmx'&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// 2. Allow Web Service requests to the method 'TheOldMetod' to be remapped to method 'SimpleMethod'&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// 3. Allow calls to 'SimpleMethod' with the parameter 'StringParameter' to interpret that parameter as 'StrParam'&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// 4. Will apply defaults to 'SimpleMethod' parameters not supplied&lt;/span&gt;&lt;br /&gt;        &lt;span class="preproc"&gt;#region&lt;/span&gt; &lt;span class="str"&gt;'SimpleService'&lt;/span&gt; mappings/defaults&lt;br /&gt;&lt;br /&gt;        RawWebServiceName = &lt;span class="str"&gt;"SimpleService.asmx"&lt;/span&gt;;&lt;br /&gt;        WebServiceName = RawWebServiceName.ToUpper();&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//Allow for 'OldWebService.asmx' calls to be remapped to 'SimpleService.asmx'&lt;/span&gt;&lt;br /&gt;        m_WebServiceMappings[&lt;span class="str"&gt;"OldWebService.asmx"&lt;/span&gt;.ToUpper()] = RawWebServiceName;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        RawMethodName = &lt;span class="str"&gt;"SimpleMethod"&lt;/span&gt;;&lt;br /&gt;        MethodName = RawMethodName.ToUpper();&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//Add alternate method mappings for SimpleMethod&lt;/span&gt;&lt;br /&gt;        m_MethodMappings[WebServiceName] = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, String&amp;gt;();&lt;br /&gt;        m_MethodMappings[WebServiceName][&lt;span class="str"&gt;"TheOldMethod"&lt;/span&gt;.ToUpper()] = RawMethodName;&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//Add alternate parameter mappings for SimpleMethod&lt;/span&gt;&lt;br /&gt;        m_ParameterMappings[WebServiceName] = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt;();&lt;br /&gt;        m_ParameterDefaults[WebServiceName] = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, Dictionary&amp;lt;String, String&amp;gt;&amp;gt;();&lt;br /&gt;        m_ParameterMappings[WebServiceName][MethodName] = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, String&amp;gt;();&lt;br /&gt;        m_ParameterDefaults[WebServiceName][MethodName] = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, String&amp;gt;();&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//Allow for an alterate input (StringParameter) for&lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//the StrParam parameter of SimpleMethod&lt;/span&gt;&lt;br /&gt;        m_ParameterMappings[WebServiceName][MethodName][&lt;span class="str"&gt;"StringParameter"&lt;/span&gt;] = &lt;span class="str"&gt;"StrParam"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//Init the SimpleMethod defaults &lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//(cannot override params, only set params that don't already exist)&lt;/span&gt;&lt;br /&gt;        m_ParameterDefaults[WebServiceName][MethodName][&lt;span class="str"&gt;"StrParam"&lt;/span&gt;] = &lt;span class="str"&gt;"DefaultString"&lt;/span&gt;;&lt;br /&gt;        m_ParameterDefaults[WebServiceName][MethodName][&lt;span class="str"&gt;"IntParam"&lt;/span&gt;] = &lt;span class="str"&gt;"11"&lt;/span&gt;;&lt;br /&gt;        m_ParameterDefaults[WebServiceName][MethodName][&lt;span class="str"&gt;"BoolParam"&lt;/span&gt;] = &lt;span class="str"&gt;"true"&lt;/span&gt;;&lt;br /&gt;        m_ParameterDefaults[WebServiceName][MethodName][&lt;span class="str"&gt;"EnumParam"&lt;/span&gt;] = &lt;span class="str"&gt;"ITEM2"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;How the preprocessor is called (via the Global.asax file):&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Application_BeginRequest(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;br /&gt;    {&lt;br /&gt;        Preprocessor.PreprocessRequest(Server, Request, Response);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The preprocessor code (take a deep breath):&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; PreprocessRequest(HttpServerUtility Server, HttpRequest Request, HttpResponse Response)&lt;br /&gt;{&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request == &lt;span class="kwrd"&gt;null&lt;/span&gt; || Request.Url == &lt;span class="kwrd"&gt;null&lt;/span&gt; || Request.Url.Segments == &lt;span class="kwrd"&gt;null&lt;/span&gt;&lt;br /&gt;        || Request.Url.Segments.Length &amp;lt; 3) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    String RawWebServiceName = Request.Url.Segments[Request.Url.Segments.Length - 2];&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (RawWebServiceName != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; RawWebServiceName.EndsWith(&lt;span class="str"&gt;"/"&lt;/span&gt;))&lt;br /&gt;        RawWebServiceName = RawWebServiceName.Substring(0, RawWebServiceName.Length - 1);&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (!RawWebServiceName.EndsWith(&lt;span class="str"&gt;".asmx"&lt;/span&gt;, StringComparison.InvariantCultureIgnoreCase))&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    String RawMethodName = Request.Url.Segments[Request.Url.Segments.Length - 1];&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (RawMethodName.EndsWith(&lt;span class="str"&gt;".asmx"&lt;/span&gt;, StringComparison.InvariantCultureIgnoreCase))&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//Ensure everything has been initialized&lt;/span&gt;&lt;br /&gt;    Init();&lt;br /&gt;&lt;br /&gt;    String WebServiceName = RawWebServiceName.ToUpper();&lt;br /&gt;    String RawNewWebServiceName = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;    String NewWebServiceName = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    String MethodName = RawMethodName.ToUpper();&lt;br /&gt;    String RawNewMethodName = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;    String NewMethodName = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//Determine if the current Web Service (.asmx) call needs to be remapped&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (m_WebServiceMappings.ContainsKey(WebServiceName))&lt;br /&gt;    {&lt;br /&gt;        RawNewWebServiceName = m_WebServiceMappings[WebServiceName];&lt;br /&gt;        NewWebServiceName = RawNewWebServiceName.ToUpper();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String WebServiceKey = NewWebServiceName == &lt;span class="kwrd"&gt;null&lt;/span&gt; ? WebServiceName : NewWebServiceName;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//Determine if the current method call needs to be remapped&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (m_MethodMappings.ContainsKey(WebServiceKey)&lt;br /&gt;        &amp;amp;&amp;amp; m_MethodMappings[WebServiceKey].ContainsKey(MethodName))&lt;br /&gt;    {&lt;br /&gt;        RawNewMethodName = m_MethodMappings[WebServiceKey][MethodName];&lt;br /&gt;        NewMethodName = RawNewMethodName.ToUpper();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String MethodKey = NewMethodName == &lt;span class="kwrd"&gt;null&lt;/span&gt; ? MethodName : NewMethodName;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//POST (Form/SOAP) requests need to be handled a bit differently&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsPost = Request.RequestType.Trim().ToUpper() == &lt;span class="str"&gt;"POST"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//Determine if any new parameter values need to be added&lt;/span&gt;&lt;br /&gt;    Dictionary&amp;lt;String, String&amp;gt; NewParamValues = GetNewParamValues(Request, WebServiceKey, MethodKey, IsPost);&lt;br /&gt;    &lt;span class="rem"&gt;//if (NewParamValues == null || NewParamValues.Count == 0) return;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//Use reflection to manually insert parameters into the request&lt;/span&gt;&lt;br /&gt;    RewriteRequest(Request, RawWebServiceName, RawNewWebServiceName, &lt;br /&gt;        RawMethodName, RawNewMethodName, NewParamValues, IsPost);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;String, String&amp;gt; GetNewParamValues(HttpRequest Request, &lt;br /&gt;    String WebServiceKey, String MethodKey, &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsPost)&lt;br /&gt;{&lt;br /&gt;    Dictionary&amp;lt;String, String&amp;gt; NewParamValues = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, String&amp;gt;();&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//Determine if any parameters need remapping&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (m_ParameterMappings.ContainsKey(WebServiceKey) &lt;br /&gt;        &amp;amp;&amp;amp; m_ParameterMappings[WebServiceKey].ContainsKey(MethodKey))&lt;br /&gt;    {&lt;br /&gt;        String NewParameter;&lt;br /&gt;        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (String Param &lt;span class="kwrd"&gt;in&lt;/span&gt; m_ParameterMappings[WebServiceKey][MethodKey].Keys)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="rem"&gt;//Determine if the parameter needs to be remapped (exists)&lt;/span&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request.Params[Param] != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;            {&lt;br /&gt;                NewParameter = m_ParameterMappings[WebServiceKey][MethodKey][Param];&lt;br /&gt;                &lt;span class="rem"&gt;//Verify that the parameter that it would map to does not already exist&lt;/span&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (NewParameter != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; Request.Params[NewParameter] == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;                    NewParamValues[NewParameter] = Request.Params[Param];&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="rem"&gt;//Determine if any parameters need a default value applied&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (m_ParameterDefaults.ContainsKey(WebServiceKey)&lt;br /&gt;        &amp;amp; m_ParameterDefaults[WebServiceKey].ContainsKey(MethodKey))&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (KeyValuePair&amp;lt;String, String&amp;gt; DefaultValue &lt;span class="kwrd"&gt;in&lt;/span&gt; m_ParameterDefaults[WebServiceKey][MethodKey])&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (IsPost)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request.Form[DefaultValue.Key] == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;                    NewParamValues[DefaultValue.Key] = DefaultValue.Value;&lt;br /&gt;            }&lt;br /&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request.Params[DefaultValue.Key] == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;                NewParamValues[DefaultValue.Key] = DefaultValue.Value;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; NewParamValues;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RewriteRequest(HttpRequest Request, String CurrentWebService, String NewWebService,&lt;br /&gt;    String CurrentMethod, String NewMethod, Dictionary&amp;lt;String, String&amp;gt; NewParamValues, &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsPost)&lt;br /&gt;{&lt;br /&gt;    &lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;br /&gt;    {&lt;br /&gt;        MethodInfo WritableMethod;&lt;br /&gt;        MethodInfo ReadOnlyMethod;&lt;br /&gt;&lt;br /&gt;        BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//If needed, rewrite a few internal values of HttpWorkerRequest and HttpRequest &lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//that ASP.NET will use when trying to determine the Web Service/method to call&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;bool&lt;/span&gt; UpdateWebService = NewWebService != &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;            &lt;span class="kwrd"&gt;bool&lt;/span&gt; UpdateMethod = NewMethod != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; Request.PathInfo.EndsWith(CurrentMethod);&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (UpdateWebService || UpdateMethod)&lt;br /&gt;            {&lt;br /&gt;                FieldInfo WorkerField = Request.GetType().GetField(&lt;span class="str"&gt;"_wr"&lt;/span&gt;, Flags);&lt;br /&gt;&lt;br /&gt;                HttpWorkerRequest WorkerRequest = (HttpWorkerRequest)WorkerField.GetValue(Request);&lt;br /&gt;&lt;br /&gt;                FieldInfo FilePathField = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;                FieldInfo PathField = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;                FieldInfo PathInfoField = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;                String filePath = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;                String path = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;                String pathInfo = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (UpdateWebService)&lt;br /&gt;                {&lt;br /&gt;                    FilePathField = WorkerRequest.GetType().GetField(&lt;span class="str"&gt;"_filePath"&lt;/span&gt;, Flags);&lt;br /&gt;                    filePath = (String)FilePathField.GetValue(WorkerRequest);&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                PathField = WorkerRequest.GetType().GetField(&lt;span class="str"&gt;"_path"&lt;/span&gt;, Flags);&lt;br /&gt;                path = (String)PathField.GetValue(WorkerRequest);&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (UpdateMethod)&lt;br /&gt;                {&lt;br /&gt;                    PathInfoField = WorkerRequest.GetType().GetField(&lt;span class="str"&gt;"_pathInfo"&lt;/span&gt;, Flags);&lt;br /&gt;                    pathInfo = (String)PathInfoField.GetValue(WorkerRequest);&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (UpdateWebService)&lt;br /&gt;                {&lt;br /&gt;                    filePath = path.Substring(0, filePath.Length - CurrentWebService.Length);&lt;br /&gt;                    filePath += NewWebService;&lt;br /&gt;&lt;br /&gt;                    path = path.Replace(&lt;span class="str"&gt;"/"&lt;/span&gt; + CurrentWebService + &lt;span class="str"&gt;"/"&lt;/span&gt;,&lt;br /&gt;                        &lt;span class="str"&gt;"/"&lt;/span&gt; + NewWebService + &lt;span class="str"&gt;"/"&lt;/span&gt;);&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (UpdateMethod)&lt;br /&gt;                {&lt;br /&gt;                    path = path.Substring(0, path.Length - CurrentMethod.Length);&lt;br /&gt;                    path += NewMethod;&lt;br /&gt;&lt;br /&gt;                    pathInfo = pathInfo.Substring(0, pathInfo.Length - CurrentMethod.Length);&lt;br /&gt;                    pathInfo += NewMethod;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (UpdateWebService)&lt;br /&gt;                {&lt;br /&gt;                    FilePathField.SetValue(WorkerRequest, filePath);&lt;br /&gt;                    FilePathField = Request.GetType().GetField(&lt;span class="str"&gt;"_filePath"&lt;/span&gt;, Flags);&lt;br /&gt;                    FilePathField.SetValue(Request, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                PathField.SetValue(WorkerRequest, path);&lt;br /&gt;                PathField = Request.GetType().GetField(&lt;span class="str"&gt;"_path"&lt;/span&gt;, Flags);&lt;br /&gt;                PathField.SetValue(Request, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (UpdateMethod)&lt;br /&gt;                {&lt;br /&gt;                    PathInfoField.SetValue(WorkerRequest, pathInfo);&lt;br /&gt;                    PathInfoField = Request.GetType().GetField(&lt;span class="str"&gt;"_pathInfo"&lt;/span&gt;, Flags);&lt;br /&gt;                    PathInfoField.SetValue(Request, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        &lt;span class="kwrd"&gt;catch&lt;/span&gt; { }&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;//Remap or set defaults for Web Service parameters&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (NewParamValues.Count &amp;gt; 0)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (IsPost)&lt;br /&gt;            {&lt;br /&gt;                WritableMethod = Request.Form.GetType().GetMethod(&lt;span class="str"&gt;"MakeReadWrite"&lt;/span&gt;, Flags);&lt;br /&gt;                ReadOnlyMethod = Request.Form.GetType().GetMethod(&lt;span class="str"&gt;"MakeReadOnly"&lt;/span&gt;, Flags);&lt;br /&gt;&lt;br /&gt;                FieldInfo FormField = Request.GetType().GetField(&lt;span class="str"&gt;"_form"&lt;/span&gt;, Flags);&lt;br /&gt;&lt;br /&gt;                WritableMethod.Invoke(Request.Form, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (String NewParam &lt;span class="kwrd"&gt;in&lt;/span&gt; NewParamValues.Keys)&lt;br /&gt;                    Request.Form[NewParam] = NewParamValues[NewParam];&lt;br /&gt;&lt;br /&gt;                FormField.SetValue(Request, Request.Form);&lt;br /&gt;                ReadOnlyMethod.Invoke(Request.Form, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;            }&lt;br /&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;br /&gt;            {&lt;br /&gt;                WritableMethod = Request.QueryString.GetType().GetMethod(&lt;span class="str"&gt;"MakeReadWrite"&lt;/span&gt;, Flags);&lt;br /&gt;                ReadOnlyMethod = Request.QueryString.GetType().GetMethod(&lt;span class="str"&gt;"MakeReadOnly"&lt;/span&gt;, Flags);&lt;br /&gt;&lt;br /&gt;                FieldInfo QueryStringField = Request.GetType().GetField(&lt;span class="str"&gt;"_queryStringText"&lt;/span&gt;, Flags);&lt;br /&gt;                FieldInfo QueryStringOverridenField = Request.GetType().GetField(&lt;span class="str"&gt;"_queryStringOverriden"&lt;/span&gt;, Flags);&lt;br /&gt;&lt;br /&gt;                MethodInfo ResetMethod = Request.QueryString.GetType().GetMethod(&lt;span class="str"&gt;"Reset"&lt;/span&gt;, Flags);&lt;br /&gt;                MethodInfo FillMethod = Request.GetType().GetMethod(&lt;span class="str"&gt;"FillInQueryStringCollection"&lt;/span&gt;, Flags);&lt;br /&gt;&lt;br /&gt;                String QueryString = Request.QueryString.ToString();&lt;br /&gt;                WritableMethod.Invoke(Request.QueryString, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;                QueryString = QueryString.Replace(CurrentMethod, NewMethod);&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (String NewParam &lt;span class="kwrd"&gt;in&lt;/span&gt; NewParamValues.Keys)&lt;br /&gt;                    Request.QueryString[NewParam] = NewParamValues[NewParam];&lt;br /&gt;&lt;br /&gt;                QueryStringField.SetValue(Request, Request.QueryString.ToString());&lt;br /&gt;                QueryStringOverridenField.SetValue(Request, &lt;span class="kwrd"&gt;true&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;                ResetMethod.Invoke(Request.QueryString, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;                FillMethod.Invoke(Request, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;                ReadOnlyMethod.Invoke(Request.QueryString, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    &lt;span class="kwrd"&gt;catch&lt;/span&gt; (Exception) { }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Download the source code: &lt;a href="http://www.box.net/shared/x08oaqdpps"&gt;SampleWebService.zip&lt;/a&gt;&lt;br /&gt;&lt;br/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5896148192050476388-242128904697761645?l=www.no-feature.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.no-feature.com/feeds/242128904697761645/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.no-feature.com/2009/03/using-reflection-to-hack-net-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/242128904697761645'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/242128904697761645'/><link rel='alternate' type='text/html' href='http://www.no-feature.com/2009/03/using-reflection-to-hack-net-web.html' title='Using reflection to hack .NET Web Services (Adding backwards compatibility)'/><author><name>Fraschetti</name><uri>http://www.blogger.com/profile/00603425736088406167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_2QtvV7pKOM4/SauCNDGCk6I/AAAAAAAAABE/3pWcfeTx0Z0/S220/Brazil+Pics+057.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5896148192050476388.post-314854255493614150</id><published>2009-02-15T11:02:00.000-08:00</published><updated>2009-02-15T13:16:07.925-08:00</updated><title type='text'>New Domain - nofeature.com</title><content type='html'>As I've finally found the motivation to start regularly posting on this blog, I thought it was only appropriate that it grow up a bit. As I was a bit late in the domain name registration race, I've been forced to settle for 'no-feature.com' as opposed to the more appropriate (not that it really matters) 'nofeature.com'. As I don't speak German, I suspect it'll be a while (if ever) before I acquire both domains (although in a weeks time, I highly doubt I'll even care).&lt;br /&gt;&lt;br /&gt;All that side.. stay tuned.. there's more to come!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5896148192050476388-314854255493614150?l=www.no-feature.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.no-feature.com/feeds/314854255493614150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.no-feature.com/2009/02/new-domain-nofeaturecom.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/314854255493614150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/314854255493614150'/><link rel='alternate' type='text/html' href='http://www.no-feature.com/2009/02/new-domain-nofeaturecom.html' title='New Domain - nofeature.com'/><author><name>Fraschetti</name><uri>http://www.blogger.com/profile/00603425736088406167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_2QtvV7pKOM4/SauCNDGCk6I/AAAAAAAAABE/3pWcfeTx0Z0/S220/Brazil+Pics+057.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5896148192050476388.post-44209994599216887</id><published>2008-02-23T23:15:00.000-08:00</published><updated>2009-03-01T11:09:09.452-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Services'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='USB'/><category scheme='http://www.blogger.com/atom/ns#' term='TEMPer'/><title type='text'>A C# Web Service Wrapper For Your TEMPer 1.0 Device</title><content type='html'>In my previous post &lt;a href="http://nofeature.blogspot.com/2008/01/taking-advantage-of-your-temper-device.html"&gt;Taking advantage of your TEMPer 1.0 USB device&lt;/a&gt;, I provided a C# library for accessing your TEMPer 1.0 device (I say TEMPer 1.0 because my TEMPer 2.0 device has not arrived and I'm not sure if the current library will be compatible) and in this post I will provide a .NET Web Service to simplify accessing your devices.&lt;br /&gt;&lt;br /&gt;My reason for writing this web service: COM sucks. I decided to write a Windows Vista Sidebar gadget capable of displaying the temperature from my TEMPer device and found creating an exe for the widget to call and using COM both a royal pain. Creating an exe for the widget to call was of course the easiest and fastest solution, but it essentially meant that when I had the sidebar widget activated, the sidebar widget and only the sidebar widget would only ever be able to use the TEMPer device it was reading from. I then turned to COM and found COM's limitations (COM hates statics and for the life of me, I couldn't get a String[] to return correctly) were enough for me to throw in the towel (COM is sometimes useful, but if there's another path.. I'm taking it). I avoided the dedicated exe because I didn't want to have the TEMPer device restricted to only one application, so I needed a solution that would allow multiple applications to access the data and a solution that was language (and even OS) independent. The obvious solution: a web service. I won't go through the effort of writing a .NET Web Service tutorial because a Google search for 'C# web service' will give you everything you need to get things up and running.&lt;br /&gt;&lt;br /&gt;A web service provided two things: 1. a central place for all my applications to grab temperature data 2. a nice clean way for my sidebar widget to get data without the need of executing an application on disk or loading up a custom ActiveX control.&lt;br /&gt;&lt;br /&gt;The biggest issue with writing a web service to communicate with a hardware device is that only one connection to the device can be created/used/accessed at any given time. With that in mind, I wrote the TEMerData class that represents that since instance. Each time TEMPerData is called (via GetCurrentTEMPerValue), the curent time is recorded and the TEMPerData instance is locked. If your request has the lock, you'll make the actual ReadTemp on the TEMPerData's TEMPerInterface instance. If your request was blocking on the TEMPerData instance (someone else had already initiated a request for temperature data) but now has the lock all for itself, the current request's start time is used to determine if the temperature data in TEMPerData is more recent than the start of your request (the TEMPerData's time stamp will be updated by the whomever did have the lock). If the TEMPerData instance's time stamp is more recent than the request's start time stamp, the last temperature retrieved is returned, otherwise the new temperature is read and the TEMPerData's time stamp is updated.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;br /&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;br /&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web;&lt;br /&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web.Services;&lt;br /&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; TEMPer.Communication;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; TEMPer.WebService&lt;br /&gt;{&lt;br /&gt;    [WebService(Namespace = &lt;span class="str"&gt;"http://tempuri.org/"&lt;/span&gt;)]&lt;br /&gt;    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; TEMPerService : System.Web.Services.WebService&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;internal&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; String Version = &lt;span class="str"&gt;"2008.02.24.1"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;const&lt;/span&gt; String m_TEMPerContainer_Key = &lt;span class="str"&gt;"TEMPerContainer"&lt;/span&gt;;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; m_TEMPerContainer_Lock = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;        [WebMethod(EnableSession=&lt;span class="kwrd"&gt;false&lt;/span&gt;, Description=&lt;span class="str"&gt;"Lists the available TEMPer devices."&lt;/span&gt;)]&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; String[] FindDevices()&lt;br /&gt;        {&lt;br /&gt;            List&amp;lt;String&amp;gt; Devices = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;String&amp;gt;();&lt;br /&gt;            Devices.AddRange(TEMPerInterface.FindDevices());&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;lock&lt;/span&gt; (m_TEMPerContainer_Lock)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (TEMPerData data &lt;span class="kwrd"&gt;in&lt;/span&gt; TEMPerContainer.Values)&lt;br /&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (!Devices.Contains(data.COMPort))&lt;br /&gt;                        Devices.Add(data.COMPort);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            Devices.Sort();&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; Devices.ToArray();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        [WebMethod(EnableSession = &lt;span class="kwrd"&gt;false&lt;/span&gt;, Description = &lt;span class="str"&gt;"Reads the temperature (in Celsius) from the specified port. Returns double.MinValue (-1.79769e+308 / -1.7976931348623157E+308) on error."&lt;/span&gt;)]&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt; ReadTemp(String COMPort)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; GetCurrentTEMPerValue(COMPort);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt; GetCurrentTEMPerValue(String COMPort)&lt;br /&gt;        {&lt;br /&gt;            TEMPerData Data = GetTEMPerDataContainer(COMPort);&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (Data == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt;.MinValue;&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;lock&lt;/span&gt; (Data)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;double&lt;/span&gt; temp = Data.LastUpdate &amp;lt; DateTime.Now ? Data.ReadTemp() : Data.LastValue;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (temp == &lt;span class="kwrd"&gt;double&lt;/span&gt;.MinValue) RemoveTEMPerDataContainer(COMPort);&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; temp;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; TEMPerData GetTEMPerDataContainer(String COMPort)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (COMPort == &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;            String Key = COMPort.Trim().ToUpper();&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (Key.Length == 0) &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;            TEMPerData Data = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;lock&lt;/span&gt; (m_TEMPerContainer_Lock)&lt;br /&gt;            {&lt;br /&gt;                Dictionary&amp;lt;String, TEMPerData&amp;gt; Dict = TEMPerContainer;&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt;(Dict.ContainsKey(Key))&lt;br /&gt;                    Data = Dict[Key];&lt;br /&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;br /&gt;                {&lt;br /&gt;                    Data = &lt;span class="kwrd"&gt;new&lt;/span&gt; TEMPerData(Key);&lt;br /&gt;                    Dict[Key] = Data;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; Data;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RemoveTEMPerDataContainer(String COMPort)&lt;br /&gt;        {&lt;br /&gt;            TEMPerContainer.Remove(COMPort);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;String, TEMPerData&amp;gt; TEMPerContainer&lt;br /&gt;        {&lt;br /&gt;            get &lt;br /&gt;            {&lt;br /&gt;                HttpApplicationState App = HttpContext.Current.Application;&lt;br /&gt;&lt;br /&gt;                Dictionary&amp;lt;String, TEMPerData&amp;gt; dict = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;lock&lt;/span&gt; (m_TEMPerContainer_Lock)&lt;br /&gt;                {&lt;br /&gt;                    dict = (Dictionary&amp;lt;String, TEMPerData&amp;gt;)App[m_TEMPerContainer_Key];&lt;br /&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (dict == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;                    {&lt;br /&gt;                        dict = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;String, TEMPerData&amp;gt;();&lt;br /&gt;                        App[m_TEMPerContainer_Key] = dict;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; dict;&lt;br /&gt;            }&lt;br /&gt;            set {&lt;br /&gt;                HttpApplicationState App = HttpContext.Current.Application;&lt;br /&gt;&lt;br /&gt;                &lt;span class="kwrd"&gt;lock&lt;/span&gt; (m_TEMPerContainer_Lock)&lt;br /&gt;                {&lt;br /&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;value&lt;/span&gt; == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;                        App.Remove(m_TEMPerContainer_Key);&lt;br /&gt;                    &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;br /&gt;                        App[m_TEMPerContainer_Key] = &lt;span class="kwrd"&gt;value&lt;/span&gt;;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;internal&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; TEMPerData&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; String      m_COMPort;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; DateTime    m_LastUpdate;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt;      m_LastValue;&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; TEMPerInterface m_Interface;&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; TEMPerData(String COMPort)&lt;br /&gt;        {&lt;br /&gt;            m_COMPort = COMPort;&lt;br /&gt;            m_LastUpdate = DateTime.MinValue;&lt;br /&gt;            m_LastValue = &lt;span class="kwrd"&gt;double&lt;/span&gt;.MinValue;&lt;br /&gt;            m_Interface = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; String COMPort&lt;br /&gt;        {&lt;br /&gt;            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; m_COMPort; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; DateTime LastUpdate&lt;br /&gt;        {&lt;br /&gt;            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; m_LastUpdate; }&lt;br /&gt;            set { m_LastUpdate = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt; LastValue&lt;br /&gt;        {&lt;br /&gt;            get { &lt;span class="kwrd"&gt;return&lt;/span&gt; m_LastValue; }&lt;br /&gt;            set { m_LastValue = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;double&lt;/span&gt; ReadTemp()&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt;(m_Interface == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;                    m_Interface = &lt;span class="kwrd"&gt;new&lt;/span&gt; TEMPerInterface(COMPort);&lt;br /&gt;&lt;br /&gt;                m_LastValue = m_Interface.ReadTEMP();&lt;br /&gt;            }&lt;br /&gt;            &lt;span class="kwrd"&gt;catch&lt;/span&gt; (Exception) &lt;br /&gt;            {&lt;br /&gt;                m_LastValue = &lt;span class="kwrd"&gt;double&lt;/span&gt;.MinValue;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            m_LastUpdate = DateTime.Now;&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; m_LastValue;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;FindDevices output:&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;="utf-8"&lt;/span&gt; ?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;ArrayOfString&lt;/span&gt; &lt;span class="attr"&gt;xmlns:xsi&lt;/span&gt;&lt;span class="kwrd"&gt;="http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span class="attr"&gt;xmlns:xsd&lt;/span&gt;&lt;span class="kwrd"&gt;="http://www.w3.org/2001/XMLSchema"&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://tempuri.org/"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;COM19&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;string&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;ArrayOfString&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Successful ReadTemp output:&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;="utf-8"&lt;/span&gt; ?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;double&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://tempuri.org/"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;24.125&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;double&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Failed ReadTemp output:&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;="utf-8"&lt;/span&gt; ?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;double&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://tempuri.org/"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;-1.7976931348623157E+308&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;double&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Download the source code: &lt;a href="http://www.box.net/shared/rflfo0yo08" target="_blank"&gt;TEMPer.asmx.cs&lt;/a&gt; (v. 2008.02.24.1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Changes&lt;br /&gt;------------&lt;br /&gt;v.2008.02.24.1&lt;br /&gt;- Initial release&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5896148192050476388-44209994599216887?l=www.no-feature.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.no-feature.com/feeds/44209994599216887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.no-feature.com/2008/02/c-web-service-wrapper-for-your-temper.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/44209994599216887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/44209994599216887'/><link rel='alternate' type='text/html' href='http://www.no-feature.com/2008/02/c-web-service-wrapper-for-your-temper.html' title='A C# Web Service Wrapper For Your TEMPer 1.0 Device'/><author><name>Fraschetti</name><uri>http://www.blogger.com/profile/00603425736088406167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_2QtvV7pKOM4/SauCNDGCk6I/AAAAAAAAABE/3pWcfeTx0Z0/S220/Brazil+Pics+057.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5896148192050476388.post-4685368797308185776</id><published>2008-01-26T13:38:00.000-08:00</published><updated>2009-03-03T10:14:30.841-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='USB'/><category scheme='http://www.blogger.com/atom/ns#' term='TEMPer'/><title type='text'>Taking advantage of your TEMPer 1.0 USB device</title><content type='html'>I recently purchased a TEMPer (&lt;a href="http://www.usbfever.com/index_eproduct_view.php?products_id=257"&gt;http://www.usbfever.com/index_eproduct_view.php?products_id=257&lt;/a&gt;) device to assist me in yet another time wasting home improvement project and was shocked to see all the wasted potential in this device. After being blinded by the .NET application that came with the device, I decided to take it upon myself to write something better. I had heard about Reflector (&lt;a href="http://www.aisto.com/roeder/dotnet/"&gt;http://www.aisto.com/roeder/dotnet/&lt;/a&gt;) but had never actually used it for anything.. man was I impressed.. dropping TEMPer.exe into Reflector gave nicely formatted source code for literally every method required to use the TEMPer device. So here it is folks, a .NET (C#) dell you can use to access your TEMPer device (the download link is at the bottom).&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;br /&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; TEMPer.Communication;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; TEMPerDemo&lt;br /&gt;{&lt;br /&gt;    &lt;span class="kwrd"&gt;class&lt;/span&gt; Program&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;const&lt;/span&gt; String m_CallNumFormat = &lt;span class="str"&gt;"000000"&lt;/span&gt;;&lt;br /&gt;        &lt;span class="kwrd"&gt;const&lt;/span&gt; String m_TempFormat = &lt;span class="str"&gt;"00.0000"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Main(&lt;span class="kwrd"&gt;string&lt;/span&gt;[] args)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;double&lt;/span&gt; TempC;&lt;br /&gt;            &lt;span class="kwrd"&gt;double&lt;/span&gt; TempF;&lt;br /&gt;            &lt;span class="kwrd"&gt;int&lt;/span&gt; i;&lt;br /&gt;&lt;br /&gt;            String[] ComPorts = TEMPerInterface.FindDevices();&lt;br /&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (ComPorts.Length == 0) &lt;span class="kwrd"&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;            TEMPerInterface[] Devices = &lt;span class="kwrd"&gt;new&lt;/span&gt; TEMPerInterface[ComPorts.Length];&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;for&lt;/span&gt; (i = 0; i &amp;lt; ComPorts.Length; i++)&lt;br /&gt;                Devices[i] = &lt;span class="kwrd"&gt;new&lt;/span&gt; TEMPerInterface(ComPorts[i]);&lt;br /&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; CallNum = 1; CallNum &amp;lt;= 1000; CallNum++)&lt;br /&gt;            {&lt;br /&gt;                &lt;span class="kwrd"&gt;for&lt;/span&gt;(i = 0; i &amp;lt; Devices.Length; i++)&lt;br /&gt;                {&lt;br /&gt;                    TempC = Devices[i].ReadTEMP();&lt;br /&gt;                    TempF = TEMPerInterface.CtoF(TempC);&lt;br /&gt;&lt;br /&gt;                    Console.WriteLine(&lt;span class="str"&gt;"    "&lt;/span&gt; &lt;br /&gt;                        + CallNum.ToString(m_CallNumFormat)&lt;br /&gt;                        + &lt;span class="str"&gt;"     -     "&lt;/span&gt;&lt;br /&gt;                        + Devices[i].PortName&lt;br /&gt;                        + &lt;span class="str"&gt;"     -     "&lt;/span&gt;&lt;br /&gt;                        + TempC.ToString(m_TempFormat) + &lt;span class="str"&gt;" C"&lt;/span&gt;&lt;br /&gt;                        + &lt;span class="str"&gt;"     -     "&lt;/span&gt;&lt;br /&gt;                        + TempF.ToString(m_TempFormat) + &lt;span class="str"&gt;" F"&lt;/span&gt;);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The results:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    000001     -     COM19     -     22.3750 C     -     72.2750 F&lt;br /&gt;    000002     -     COM19     -     22.3750 C     -     72.2750 F&lt;br /&gt;    000003     -     COM19     -     22.3750 C     -     72.2750 F&lt;br /&gt;    000004     -     COM19     -     22.3750 C     -     72.2750 F&lt;br /&gt;....&lt;br /&gt;    000049     -     COM19     -     22.5000 C     -     72.5000 F&lt;br /&gt;    000050     -     COM19     -     22.5000 C     -     72.5000 F&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Requirements:&lt;br /&gt;As of version 2008.01.29.1, the DLL no longer requires USBRDXP.DLL (thanks to Bob for the code that removed this dependency).&lt;br /&gt;&lt;br /&gt;Notes:&lt;br /&gt;The device speaks Celsius.. and as such only returns degrees in 0.1250 increments.&lt;br /&gt;If you decide to use this library or the above code, please feel free to contact me with questions (not that I completely understand everything their original code is doing.. trust me, the decompiled code lacks a lot of context) or comments.. but please understand that I'm not responsible for any problems/issues that might crop up as a result of using this library.&lt;br /&gt;&lt;br /&gt;The library only does a few things:&lt;br /&gt;1. Reads the registry to list the system's com ports&lt;br /&gt;2. Queries each COM port to find those that have a TEMPer device&lt;br /&gt;3. Reads/writes to the COM port to get temperature data&lt;br /&gt;&lt;br /&gt;That's it.. there's no other magic or trickery going on inside the library.&lt;br /&gt;&lt;br /&gt;Download the DLL: &lt;a href="http://www.box.net/shared/1b7rerbfos" target="_blank"&gt;TEMPer.Communication.dll&lt;/a&gt; (v. 2008.02.23.1)&lt;br /&gt;Download the source code: &lt;a href="http://www.box.net/shared/lgyqu8w4ko" target="_blank"&gt;TEMPer.cs&lt;/a&gt; (v. 2008.02.23.1)&lt;br /&gt;&lt;br /&gt;Changes&lt;br /&gt;------------&lt;br /&gt;v.1&lt;br /&gt;- Initial release&lt;br /&gt;&lt;br /&gt;v.2008.01.29.1&lt;br /&gt;- Removed static reference to SerialPort (I suspect this is why reading from multiple devices would fail when multiple instances were created. I'll have to wait for my new devices to arrive before I can verify this).&lt;br /&gt;- Removed the dependency on the TEMPer driver/dll. Thanks to Bob (see comments below) for the C++ code to implement the TEMPer device verification manually. The code below was ported to C# to make things a bit easier on my end.&lt;br /&gt;- Added a version to the DLL&lt;br /&gt;&lt;br /&gt;v.2008.02.23.1&lt;br /&gt;- GetPortNames() now uses the managed SerialPort.GetPortNames() instead of querying the windows registry.&lt;br /&gt;- I experimented with the idea of making ReadTemp a static call (removing the need to create an instance of the TEMPerInterface class) but the overhead for initializing the device added more than 1 second to each read call (something I was not willing to live with). Instead, I cleaned up a bit of the code I borrowed form the original TEMPer exe which shaved a few hundred milliseconds off of each call.&lt;br /&gt;- The SerialPort is only kept open for the initialization of the device and for each ReadTemp (previously, the SerialPort was not being closed at the end of every ReadTemp call). Leaving the SerialPort open (eliminating the overhead of opening/closing the SerialPort each time) caused major freezing issues on my Vista laptop. The performance gain was insignificant, so the SerialPort is now closed at the end of each ReadTemp call. As the SerialPort is closed at the end of each read, there is no longer a need to manually call the Close function on the TEMPerInterface class (therefore that function has been removed).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5896148192050476388-4685368797308185776?l=www.no-feature.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.no-feature.com/feeds/4685368797308185776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.no-feature.com/2008/01/taking-advantage-of-your-temper-device.html#comment-form' title='84 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/4685368797308185776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5896148192050476388/posts/default/4685368797308185776'/><link rel='alternate' type='text/html' href='http://www.no-feature.com/2008/01/taking-advantage-of-your-temper-device.html' title='Taking advantage of your TEMPer 1.0 USB device'/><author><name>Fraschetti</name><uri>http://www.blogger.com/profile/00603425736088406167</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_2QtvV7pKOM4/SauCNDGCk6I/AAAAAAAAABE/3pWcfeTx0Z0/S220/Brazil+Pics+057.jpg'/></author><thr:total>84</thr:total></entry></feed>
