| This article looks at the issues that you can
encounter using No-touch Deployment of Winforms applications that rely on
additional files that are not typically considered part of the application. An
example of this is the additional configuration file used by the Microsoft
Enterprise Library Configuration Management.
|
| No-Touch Deployments |
| No-Touch deployment (NTD) is the term given to
a Winforms application that is launched by navigating to a URL. This deployment
option provides the ability to provide a rich user interface with the ease of a
web deployment. NTD typically employ a Winforms user interface with web
services providing business and data services. This also allows the user client
to be fairly small and lightweight. |
| To set up a Winforms application using NTD all
that is needed is the exe, supporting dlls and the config file deployed within
a web site. The user simply navigates to the path specifying the exe and the
IEExec process will download and launch the application on the client machine.
By default a NTD application has a limited set of permissions for actions it
can perform on the client machine.
|
| File Management |
| Typically in a Winforms application
determining file information is a simple operation using the File and Path
classes in the System.IO namespace. When using NTD the File and Path classes
are not useful since they will not be able to find the files using the http:
prefix. The specific items that will be looked at are the following. |
-
Determine if a file exists.
-
Determining the last modified time of a file.
-
Retrieving the file data.
|
| Determining if a File Exists |
| When looking at the local file system,
determining if a file exists is simple. The following code sample determines if
a file called myConfig.config exists in the same directory as the application. |
string filePath = AppDomain.CurrentDomain.BaseDirectory + fileName;
bool exists = File.Exists(filePath);
|
| In this case the BaseDirectory property will
be in the format C:\MyApplications\MyProject\Bin\Debug. This format works fine
for the File.Exists method. When we use the same application using NTD we see
that the BaseDirectory points to the URL on the server similar to this,
http://localhost/Myproject/. If this is used in the File.Exists method it will
always return false. |
| In order to solve this dilemma the file path
needs to be converted to an Uri. By using the Scheme property of the Uri it can
be determined if the BaseDirectory uses http or the file scheme. If the file
scheme is used then the File.Exists method can be used. If the http scheme is
used then a web request can be used to determine if the file exists. The code
for the http scheme is the following. |
Uri uri = new Uri(filePath);
�
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "HEAD";
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
return (response.StatusCode != HttpStatusCode.NotFound);
}
catch(WebException e)
{
response = (HttpWebResponse)e.Response;
if( response.StatusCode == HttpStatusCode.NotFound)
return false;
else
throw;
}
finally
{
if(response != null)
response.Close();
}
|
| While the web request pattern can be used for
both file and http schemes it is not efficient. Only the HttpWebRequest allows
for setting the method. In this case we use the HEAD method which only returns
summary information about the file requested. This prevents the transfer of the
entire file which is more efficient especially if the file is large. The
HttpWebRequest does not support the file scheme so a case/switch statement
needs to be used with the appropriate method. The final combined code is as
follows. |
Uri uri = new Uri(filePath);
switch(uri.Scheme)
{
case "http":
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "HEAD";
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
return (response.StatusCode != HttpStatusCode.NotFound);
}
catch(WebException e)
{
response = (HttpWebResponse)e.Response;
if( response.StatusCode == HttpStatusCode.NotFound)
return false;
else
throw;
}
finally
{
if(response != null)
response.Close();
}
case "file":
return File.Exists(uri.LocalPath);
default:
throw new ArgumentException("Unsupported scheme for file.");
}
|
| Determining the File Modified Time |
| Determining the last modified date for a file
on the file system is just as simple as determining if it exists. The code is
as follows. |
DateTime lastWriteTime =
File.GetLastWriteTime(filePath);
|
| To perform this same operation for the NTD
client the web request is again used. The headers collection returns the
last-modified header which maps to the LastModified property of the
HttpWebResponse. The code to extract this is as follows.
|
Uri uri = new Uri(filePath);
�
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "HEAD";
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
return response.LastModified;
}
finally
{
if(response != null)
response.Close();
}
|
| Putting these to methods together results in
the following. |
Uri uri = new Uri(filePath);
switch(uri.Scheme)
{
case "http":
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "HEAD";
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
return response.LastModified;
}
finally
{
if(response != null)
response.Close();
}
case "file":
return File.GetLastWriteTime(uri.LocalPath);
default:
throw new ArgumentException("Unsupported scheme for file.");
}
|
| Retrieving the File Data |
| The last item to cover is getting the actual
file data. Getting the file data from a file system file is again a fairly
simple process. The code is as follows.
|
Stream s = new FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read);
StreamReader rdr = new StreamReader(s);
String data = rdr.ReadToEnd();
|
| Reading from a NTD location the code becomes
the following. |
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();v byte[] data = new
byte[response.ContentLength];
response.GetResponseStream().Read(data, 0, data.Length);
return new MemoryStream(data);
}
finally
{
if(response != null)
response.Close();
}
|
| Putting these two sections together results in
the following. |
Uri uri = new Uri(filePath);
switch(uri.Scheme)
{
case "http":
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
byte[] data = new byte[response.ContentLength];
response.GetResponseStream().Read(data, 0, data.Length);
return new MemoryStream(data);
}
finally
{
if(response != null)
response.Close();
}
case "file":
return new FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read);
default:
throw new ArgumentException("Unsupported scheme for file.");
}
|
| Summary |
This type of pattern can be used to allow the
Microsoft Enterprise Configuration Application Block to be used with NTD
applications. The current version I�m using does not and inserting code similar
to the preceding will correct the issues. Hopefully the demonstrations also
showed how to use the basics of the HttpWebRequest in communicating with web
servers.
Download the solution that accompanies this article |