Developing For .NET

Real World .NET Methods, Tricks, and Examples

Archive for the '.NET' Category

I’m Back!

If you are (were?) a regular reader, I’m sure you’ve noticed that the site has been unavailable for a while. Long story short, our local power company decided to shut our power grid down one weekend for ‘upgrades’, and in the process my MySQL server suffered a catastrophic power spike. Both the power supply (easy to fix) and the mother board (not-so-easy and d*mned expensive to fix) were fried and the server never breathed again.

Are there a bunch of things I could have done to restore it? Yes. Did I want the site back? Mostly (more in a minute). Did I have the time to do it? No. Sort of like how the Contractor’s house is always in a state of disrepair, the developer’s site just wasn’t a priority. A friend of mine was messing around in here today and we were finally able to do the necessary work to get another install of MySQL running on a different server and import the database, and so as of today the site is back.

It’s hard to believe it, but it has been about 5 weeks since the site went down. I’ve had lots of adventures in that time, but most notably I’ve been very productive. I can only think that some of that was because I was not spending any time working on the site: no postings, no tracking, no blogging, etc. It was a nice little holiday, but I have to admit that I missed the outlet.

The Bad News

The bad news is that I know I lost some posts. Basically, anything written in April is gone. I think I had a couple of good posts about using Func<> to create local methods, but apparently my last backup was from before those were written. So if you find a bad link or can help me remember, maybe I’ll try to duplicate them.

ASP.NET MVC

My MVC adventures are moving on full bore. Preview 3 was released, and I am very happy with the updates. Last night I presented on MVC at RVNUG so I got to show off some of the recent goodies. I have two concurrent development efforts going on in MVC right now and will soon be adding a third. I’ve really been trying to dig into Routing, because I think my understanding of this core technology is lacking. I should be posting on this in the near future.

jQuery

I’ve done plenty of JavaScript in the past, but I’ve never employed a JavaScript library before now. I found out about jQuery while researching MVC and AJAX and let me tell you: I am WAY impressed! This is one awesome piece of software! I’ve spent a good bit of this week reading and playing with it and this is one tool that is definitely going in my bag of tricks.

I incorporated some of it into my presentation last night, which was part of a larger ASP.NET and AJAX group of presentations. What struck me most was that I actually find using jQuery for AJAX easier than the ASP.NET AJAX implementation. The code is certainly easier to read, and learning jQuery itself will add a lot more skill to my resume than just dropping an UpdatePanel. And we had a good discussion about how the term “AJAX” is frequently misapplied to more general DHTML rather than specific Server calls.

I’ll certainly be posting about using jQuery in MVC shortly.

My Laptop

I finally had a “Vista” moment, you know, the bad ones everyone talks about. I had previously installed the Beta of Vista SP1. Since the release version has been available for a while, on Friday I finally decided that I would upgrade. So I got on line, read the instructions, and followed the procedures. I uninstalled the beta (which took hours) and installed SP1. It cycled through all of its processes, the last of which is “Updating Configuration, Step 3 of 3″. This step completed and then entered Shut Down for like the 20th time. The problem is, it never came back.

I think now that this was because I was connected to my docking station and the prompts were sent to the wrong display - which was no longer visible - a mistake I’ll not repeat (more on this later). Since the machine would not come back, I restored the OS from a Rescue Disk. All seemed to be well, and I finished the week on a high note.

Unfortunately, Monday morning started on a low note: the machine would not start up. Instead, it was caught in some infinite loop between “updating configuration step 3 of 3″ and Shut Down. It simply toggled back and forth between these two steps ad infinitum. I finally got the rescue disk back out and tried it again, which worked. I did a few things I had to get done, and then I rebooted: same problem. This tune was getting old quickly. So I tried the rescue trick one more time, except now the backups were no longer available. I was stuck with the “Restore to Factory Settings” option, which meant losing all data and applications. Not catastrophic, because I had backups, but annoying and time consuming.

And then the kicker: the rescue disk installed XP instead of Vista even though the machine’s “factory settings” were OEM Vista Business. Fortunately, I had an upgrade disk to Ultimate available through my Microsoft Action Pack subscription, so I installed that. I then spent most of Tuesday reconfiguring my system. And this is where the display issue comes in: because I was connected to the docking station during all of this, the default display for my machine is now the monitor, which Vista incorrectly set to 800×600 (instead of its 1600×1050 default). Worse yet, it would not offer me the correct resolution and Vista refused to install the driver from the disk that came with the monitor. Several Windows Updates and reboots later, and the device was finally recognized. I have now finally managed to get the settings corrected, although for my presentation last night it caused some issues with the projector, but we got through it.

Two final notes: the upgrade from XP to Vista seems to have wiped out the OEM FingerPrint reader software, so my T61 FingerPrint reader will not function. Also, there is an update that refuses to install because the system is missing files. And I thought “Ultimate” had everything.

Windows Live OneCare

In all this hubub, I lost my Norton 360 software and information, so I installed Windows Live OneCare instead, and so far I like it. It appears to be less resource intensive than 360 was and less intrusive. As of yet, I’m not subscribing, but I probably will eventually do so. I’d be happy to hear any of your experiences with the product.

So in conclusion, I’ve been busy busy busy, but I’m glad to be back amongst the living.

No comments

[REPOST] - ASP.NET Server Variables, take 2

The last thing I did yesterday before I went home was write the code for (and post) a simple class for reading HTTP Request Server Variables. I like the idea, because it allows the consumer to use Intellisense and variable names instead of doing a value lookup using a string.

Nick at Coder Journal left a comment (thanks Nick!) suggesting we do this in a more static way to avoid unnecessarily processing all the variables. I had originally wanted to do something like that, but I also want to separate this class from any particular ASP.NET project, so here is my take on a static-only version that does not require access to the HttpContext object:

public static class ServerVariables2
{
public static NameValueCollection VariableCollection { get; private set; }public static void Clear()
{
VariableCollection = null;
}

public static void Initialize(NameValueCollection values)
{
Clear();
VariableCollection = values;
}

private static string ValidateAndAcquire(string VariableName)
{
if (VariableCollection == null)
{
throw new ArgumentNullException(“VariableCollection is not initialized.”);
}
else if (!VariableCollection.AllKeys.Contains(VariableName))
{
throw new ArgumentOutOfRangeException(
String.Format(“Variable {0} not found in VariableCollection.”, VariableName));
}
else
{
return VariableCollection[VariableName];
}
}

public static string ALL_HTTP { get { return ValidateAndAcquire(“ALL_HTTP”); } }
public static string ALL_RAW { get { return ValidateAndAcquire(“ALL_RAW”); } }
public static string APPL_MD_PATH { get { return ValidateAndAcquire(“APPL_MD_PATH”); } }
public static string APPL_PHYSICAL_PATH { get { return ValidateAndAcquire(“APPL_PHYSICAL_PATH”); } }
public static string AUTH_TYPE { get { return ValidateAndAcquire(“AUTH_TYPE”); } }
public static string AUTH_USER { get { return ValidateAndAcquire(“AUTH_USER”); } }
public static string AUTH_PASSWORD { get { return ValidateAndAcquire(“AUTH_PASSWORD”); } }
public static string LOGON_USER { get { return ValidateAndAcquire(“LOGON_USER”); } }
public static string REMOTE_USER { get { return ValidateAndAcquire(“REMOTE_USER”); } }
public static string CERT_COOKIE { get { return ValidateAndAcquire(“CERT_COOKIE”); } }
public static string CERT_FLAGS { get { return ValidateAndAcquire(“CERT_FLAGS”); } }
public static string CERT_ISSUER { get { return ValidateAndAcquire(“CERT_ISSUER”); } }
public static string CERT_KEYSIZE { get { return ValidateAndAcquire(“CERT_KEYSIZE”); } }
public static string CERT_SECRETKEYSIZE { get { return ValidateAndAcquire(“CERT_SECRETKEYSIZE”); } }
public static string CERT_SERIALNUMBER { get { return ValidateAndAcquire(“CERT_SERIALNUMBER”); } }
public static string CERT_SERVER_ISSUER { get { return ValidateAndAcquire(“CERT_SERVER_ISSUER”); } }
public static string CERT_SERVER_SUBJECT { get { return ValidateAndAcquire(“CERT_SERVER_SUBJECT”); } }
public static string CERT_SUBJECT { get { return ValidateAndAcquire(“CERT_SUBJECT”); } }
public static string CONTENT_LENGTH { get { return ValidateAndAcquire(“CONTENT_LENGTH”); } }
public static string CONTENT_TYPE { get { return ValidateAndAcquire(“CONTENT_TYPE”); } }
public static string GATEWAY_INTERFACE { get { return ValidateAndAcquire(“GATEWAY_INTERFACE”); } }
public static string HTTPS { get { return ValidateAndAcquire(“HTTPS”); } }
public static string HTTPS_KEYSIZE { get { return ValidateAndAcquire(“HTTPS_KEYSIZE”); } }
public static string HTTPS_SECRETKEYSIZE { get { return ValidateAndAcquire(“HTTPS_SECRETKEYSIZE”); } }
public static string HTTPS_SERVER_ISSUER { get { return ValidateAndAcquire(“HTTPS_SERVER_ISSUER”); } }
public static string HTTPS_SERVER_SUBJECT { get { return ValidateAndAcquire(“HTTPS_SERVER_SUBJECT”); } }
public static string INSTANCE_ID { get { return ValidateAndAcquire(“INSTANCE_ID”); } }
public static string INSTANCE_META_PATH { get { return ValidateAndAcquire(“INSTANCE_META_PATH”); } }
public static string LOCAL_ADDR { get { return ValidateAndAcquire(“LOCAL_ADDR”); } }
public static string PATH_INFO { get { return ValidateAndAcquire(“PATH_INFO”); } }
public static string PATH_TRANSLATED { get { return ValidateAndAcquire(“PATH_TRANSLATED”); } }
public static string QUERY_STRING { get { return ValidateAndAcquire(“QUERY_STRING”); } }
public static string REMOTE_ADDR { get { return ValidateAndAcquire(“REMOTE_ADDR”); } }
public static string REMOTE_HOST { get { return ValidateAndAcquire(“REMOTE_HOST”); } }
public static string REMOTE_PORT { get { return ValidateAndAcquire(“REMOTE_PORT”); } }
public static string REQUEST_METHOD { get { return ValidateAndAcquire(“REQUEST_METHOD”); } }
public static string SCRIPT_NAME { get { return ValidateAndAcquire(“SCRIPT_NAME”); } }
public static string SERVER_NAME { get { return ValidateAndAcquire(“SERVER_NAME”); } }
public static string SERVER_PORT { get { return ValidateAndAcquire(“SERVER_PORT”); } }
public static string SERVER_PORT_SECURE { get { return ValidateAndAcquire(“SERVER_PORT_SECURE”); } }
public static string SERVER_PROTOCOL { get { return ValidateAndAcquire(“SERVER_PROTOCOL”); } }
public static string SERVER_SOFTWARE { get { return ValidateAndAcquire(“SERVER_SOFTWARE”); } }
public static string URL { get { return ValidateAndAcquire(“URL”); } }
public static string HTTP_CONNECTION { get { return ValidateAndAcquire(“HTTP_CONNECTION”); } }
public static string HTTP_KEEP_ALIVE { get { return ValidateAndAcquire(“HTTP_KEEP_ALIVE”); } }
public static string HTTP_ACCEPT { get { return ValidateAndAcquire(“HTTP_ACCEPT”); } }
public static string HTTP_ACCEPT_CHARSET { get { return ValidateAndAcquire(“HTTP_ACCEPT_CHARSET”); } }
public static string HTTP_ACCEPT_ENCODING { get { return ValidateAndAcquire(“HTTP_ACCEPT_ENCODING”); } }
public static string HTTP_ACCEPT_LANGUAGE { get { return ValidateAndAcquire(“HTTP_ACCEPT_LANGUAGE”); } }
public static string HTTP_COOKIE { get { return ValidateAndAcquire(“HTTP_COOKIE”); } }
public static string HTTP_HOST { get { return ValidateAndAcquire(“HTTP_HOST”); } }
public static string HTTP_REFERER { get { return ValidateAndAcquire(“HTTP_REFERER”); } }
public static string HTTP_USER_AGENT { get { return ValidateAndAcquire(“HTTP_USER_AGENT”); } }

}

I like this approach, and in the small amount of testing I’ve done it works well while incurring less overhead.

While I was testing this, I started toying with the idea of a third version that has a single Get method based off an Enumeration. The more I thought about it, the more sense it made: this way, the user still gets the complete benefit of Intellisense by being able to see the list of possible values via the Enum. At the same time, the code is much shorter and no longer has a bunch of properties with hard coded string values. Making a change, like adding a variable or hiding certain values would only require maintaining the Enumeration. Below is the final version of the code, for both the Enum and the class

public enum ServerVariables
{
ALL_HTTP,
ALL_RAW,
APPL_MD_PATH,
APPL_PHYSICAL_PATH,
AUTH_TYPE,
AUTH_USER,
AUTH_PASSWORD,
LOGON_USER,
REMOTE_USER,
CERT_COOKIE,
CERT_FLAGS,
CERT_ISSUER,
CERT_KEYSIZE,
CERT_SECRETKEYSIZE,
CERT_SERIALNUMBER,
CERT_SERVER_ISSUER,
CERT_SERVER_SUBJECT,
CERT_SUBJECT,
CONTENT_LENGTH,
CONTENT_TYPE,
GATEWAY_INTERFACE,
HTTPS,
HTTPS_KEYSIZE,
HTTPS_SECRETKEYSIZE,
HTTPS_SERVER_ISSUER,
HTTPS_SERVER_SUBJECT,
INSTANCE_ID,
INSTANCE_META_PATH,
LOCAL_ADDR,
PATH_INFO,
PATH_TRANSLATED,
QUERY_STRING,
REMOTE_ADDR,
REMOTE_HOST,
REMOTE_PORT,
REQUEST_METHOD,
SCRIPT_NAME,
SERVER_NAME,
SERVER_PORT,
SERVER_PORT_SECURE,
SERVER_PROTOCOL,
SERVER_SOFTWARE,
URL,
HTTP_CONNECTION,
HTTP_KEEP_ALIVE,
HTTP_ACCEPT,
HTTP_ACCEPT_CHARSET,
HTTP_ACCEPT_ENCODING,
HTTP_ACCEPT_LANGUAGE,
HTTP_COOKIE,
HTTP_HOST,
HTTP_REFERER,
HTTP_USER_AGENT
}public static class RequestServerVariable
{
public static NameValueCollection VariableCollection { get; private set; }

public static void Clear()
{
VariableCollection = null;
}

public static void Initialize(NameValueCollection values)
{
Clear();
VariableCollection = values;
}

public static string ValidateAndAcquire(ServerVariables Variable)
{
string VariableName = Enum.GetName(typeof(ServerVariables), Variable);

if (VariableCollection == null)
{
throw new ArgumentNullException(“VariableCollection is not initialized.”);
}
else if (!VariableCollection.AllKeys.Contains(VariableName))
{
throw new ArgumentOutOfRangeException(
String.Format(“Variable {0} not found in VariableCollection.”, VariableName));
}
else
{
return VariableCollection[VariableName];
}
}
}

This approach still requires an initialization routine, but it wouldn’t surprise me if there was a better way to handle that issue. Let me know what you think.

No comments

[REPOST] - ASP.NET Server Variables

I have admitted in the past that I am not an experienced ASP.NET developer, so this may be a “DUH” post, but I thought it was a good idea and wanted to share.

For the ASP.NET MVC application I’ve been working on, I needed to send the user to another domain (on the same server) for some processing, after which they are returned to the original domain. I wanted to be able to verify that the return trip was actually coming from the correct domain and port, so I needed to work with some of the ServerVariables. I hunted around a bit and found the HttpRequest.ServerVariables collection that contains the data I needed as well as a wealth of other information. Working with it though, I felt a little out of date needing to supply the string names of the variables (they are Key values in a System.Collections.Specialized.NameValueCollection object), so I created a simple class so that would let me take advantage of Intellisense and let me work with the values by name.

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections.Specialized;namespace YourNamespace
{
public class ServerVariables
{
public string ALL_HTTP { get; private set; }
public string ALL_RAW { get; private set; }
public string APPL_MD_PATH { get; private set; }
public string APPL_PHYSICAL_PATH { get; private set; }
public string AUTH_TYPE { get; private set; }
public string AUTH_USER { get; private set; }
public string AUTH_PASSWORD { get; private set; }
public string LOGON_USER { get; private set; }
public string REMOTE_USER { get; private set; }
public string CERT_COOKIE { get; private set; }
public string CERT_FLAGS { get; private set; }
public string CERT_ISSUER { get; private set; }
public string CERT_KEYSIZE { get; private set; }
public string CERT_SECRETKEYSIZE { get; private set; }
public string CERT_SERIALNUMBER { get; private set; }
public string CERT_SERVER_ISSUER { get; private set; }
public string CERT_SERVER_SUBJECT { get; private set; }
public string CERT_SUBJECT { get; private set; }
public string CONTENT_LENGTH { get; private set; }
public string CONTENT_TYPE { get; private set; }
public string GATEWAY_INTERFACE { get; private set; }
public string HTTPS { get; private set; }
public string HTTPS_KEYSIZE { get; private set; }
public string HTTPS_SECRETKEYSIZE { get; private set; }
public string HTTPS_SERVER_ISSUER { get; private set; }
public string HTTPS_SERVER_SUBJECT { get; private set; }
public string INSTANCE_ID { get; private set; }
public string INSTANCE_META_PATH { get; private set; }
public string LOCAL_ADDR { get; private set; }
public string PATH_INFO { get; private set; }
public string PATH_TRANSLATED { get; private set; }
public string QUERY_STRING { get; private set; }
public string REMOTE_ADDR { get; private set; }
public string REMOTE_HOST { get; private set; }
public string REMOTE_PORT { get; private set; }
public string REQUEST_METHOD { get; private set; }
public string SCRIPT_NAME { get; private set; }
public string SERVER_NAME { get; private set; }
public string SERVER_PORT { get; private set; }
public string SERVER_PORT_SECURE { get; private set; }
public string SERVER_PROTOCOL { get; private set; }
public string SERVER_SOFTWARE { get; private set; }
public string URL { get; private set; }
public string HTTP_CONNECTION { get; private set; }
public string HTTP_KEEP_ALIVE { get; private set; }
public string HTTP_ACCEPT { get; private set; }
public string HTTP_ACCEPT_CHARSET { get; private set; }
public string HTTP_ACCEPT_ENCODING { get; private set; }
public string HTTP_ACCEPT_LANGUAGE { get; private set; }
public string HTTP_COOKIE { get; private set; }
public string HTTP_HOST { get; private set; }
public string HTTP_REFERER { get; private set; }
public string HTTP_USER_AGENT { get; private set; }

private ServerVariables()
{

}

public static ServerVariables Factory(NameValueCollection vars)
{
ServerVariables sv;
if (vars == null)
{
sv = new ServerVariables();
}
else
{
sv = new ServerVariables()
{
ALL_HTTP = vars["ALL_HTTP"],
ALL_RAW = vars["ALL_RAW"],
APPL_MD_PATH = vars["APPL_MD_PATH"],
APPL_PHYSICAL_PATH = vars["APPL_PHYSICAL_PATH"],
AUTH_TYPE = vars["AUTH_TYPE"],
AUTH_USER = vars["AUTH_USER"],
AUTH_PASSWORD = vars["AUTH_PASSWORD"],
LOGON_USER = vars["LOGON_USER"],
REMOTE_USER = vars["REMOTE_USER"],
CERT_COOKIE = vars["CERT_COOKIE"],
CERT_FLAGS = vars["CERT_FLAGS"],
CERT_ISSUER = vars["CERT_ISSUER"],
CERT_KEYSIZE = vars["CERT_KEYSIZE"],
CERT_SECRETKEYSIZE = vars["CERT_SECRETKEYSIZE"],
CERT_SERIALNUMBER = vars["CERT_SERIALNUMBER"],
CERT_SERVER_ISSUER = vars["CERT_SERVER_ISSUER"],
CERT_SERVER_SUBJECT = vars["CERT_SERVER_SUBJECT"],
CERT_SUBJECT = vars["CERT_SUBJECT"],
CONTENT_LENGTH = vars["CONTENT_LENGTH"],
CONTENT_TYPE = vars["CONTENT_TYPE"],
GATEWAY_INTERFACE = vars["GATEWAY_INTERFACE"],
HTTPS = vars["HTTPS"],
HTTPS_KEYSIZE = vars["HTTPS_KEYSIZE"],
HTTPS_SECRETKEYSIZE = vars["HTTPS_SECRETKEYSIZE"],
HTTPS_SERVER_ISSUER = vars["HTTPS_SERVER_ISSUER"],
HTTPS_SERVER_SUBJECT= vars["HTTPS_SERVER_SUBJECT"],
INSTANCE_ID = vars["INSTANCE_ID"],
INSTANCE_META_PATH = vars["INSTANCE_META_PATH"],
LOCAL_ADDR = vars["LOCAL_ADDR"],
PATH_INFO = vars["PATH_INFO"],
PATH_TRANSLATED = vars["PATH_TRANSLATED"],
QUERY_STRING = vars["QUERY_STRING"],
REMOTE_ADDR = vars["REMOTE_ADDR"],
REMOTE_HOST = vars["REMOTE_HOST"],
REMOTE_PORT = vars["REMOTE_PORT"],
REQUEST_METHOD = vars["REQUEST_METHOD"],
SCRIPT_NAME = vars["SCRIPT_NAME"],
SERVER_NAME = vars["SERVER_NAME"],
SERVER_PORT = vars["SERVER_PORT"],
SERVER_PORT_SECURE = vars["SERVER_PORT_SECURE"],
SERVER_PROTOCOL = vars["SERVER_PROTOCOL"],
SERVER_SOFTWARE = vars["SERVER_SOFTWARE"],
URL = vars["URL"],
HTTP_CONNECTION = vars["HTTP_CONNECTION"],
HTTP_KEEP_ALIVE = vars["HTTP_KEEP_ALIVE"],
HTTP_ACCEPT = vars["HTTP_ACCEPT"],
HTTP_ACCEPT_CHARSET = vars["HTTP_ACCEPT_CHARSET"],
HTTP_ACCEPT_ENCODING= vars["HTTP_ACCEPT_ENCODING"],
HTTP_ACCEPT_LANGUAGE= vars["HTTP_ACCEPT_LANGUAGE"],
HTTP_COOKIE = vars["HTTP_COOKIE"],
HTTP_HOST = vars["HTTP_HOST"],
HTTP_REFERER = vars["HTTP_REFERER"],
HTTP_USER_AGENT = vars["HTTP_USER_AGENT"]
};
}

return sv;
}
}
}

Again, I realize this is not some brilliant idea, but I thought I’d share for anyone else who might want it. To make this work, be sure to include a reference to System.Collections.Specialized.

No comments

Don’t forget to close StreamWriter

In the midst of all my testing, learning, and blogging, my employer occasionally expects me to write a little code. As a result, I’ve spent most of this week being a productive employee, so you’ll have to wait a little while longer for the LINQ articles. In the meantime, I wanted to share a bone head problem I ran into while working on this new project.

The new project is fairly straight forward: read some data from a database, analyze it, look for inconsistencies, correct the inconsistencies, update the database, log the changes in a text file. OK, there’s more going on, but this is the general gist of things.

Having completed the difficult bits, namely recognizing and correcting “user input irregularities”, I moved on to the seemingly mindless task of writing the before and after information to a log file. How many times have we all done this:

StreamWriter file = File.CreateText(logFileName);
foreach (var item in collection)
{
    file.WriteLine("Some Text");
}

Simple, right? I ran a test and opened the text file and all appeared to be well. A few tests later, while viewing the results in Excel 2007, I noticed that the last line was cut off. Surely the problem could not be with Excel, I thought, so I looked at the raw text file, and the last line was cut off so many characters in from the left.

Naturally, all sorts of questions start flying through my head. Can an open StreamWriter only handle so many lines? Or could it be a limitation on number of bytes? Is there some system or program memory setting I’m not aware of? The code was so simple that it had to be something more

But alas, a little digging quickly led me to the answer: it was in fact my code. I failed to Close the StreamWriter, and so there were bytes that even though I had issued WriteLine(), had not yet been saved (or flushed) to the Text file.

I added Close() and sure enough all my data was written to the text file.

StreamWriter file = File.CreateText(logFileName);
foreach (var item in collection)
{
    file.WriteLine("Some Text");
}
// Close the StreamWriter to flush all the data to the file!
file.Close();

It’s good once in a while to run into something like this: it keeps us humble. Like the Roman Generals of old it reminds us that “we are mortal”.

Cheers, and Merry Christmas.

4 comments

Windows Special Folders

We were watching a Microsoft Web Seminar yesterday on Windows Vista User Account Controls, and one of the code samples referenced the users LocalApp directory. This led to a discussion of where to store things like binary files, config files, and .ini files. In the past, you may have stored this in the Program Files directory. This is not a good practice, because there is no guarantee that this is where the user installed the software. Fortunately, in .NET, you can get the installed directory from an Assembly object, but this is still not a good practice
because if the software is uninstalled and reinstalled, you could lose those files, frustrating your users.

To compound this problem, in Vista the Program Files directory requires Administrator priveleges. The whole point of UAC, of course, is to limit potentially dangerous code from executing and to allow users to truly operate in a “Standard User” account environment. Even a user with Administrator priveleges executes programs with Standard User access [NOTE: this behaviour can be overridden by manually assigning a manifest to the assembly instructing it to run as Administrator]. This means that in order to access a config or binary file stored in Program Files requires the user to bump up their security by responding to a Dialog Box. One of the Vista goals that Microsoft has set forth for ISVs is that our software should run as Standard User. That and the user being constantly prompted to upgrade their security level are reasons enough not to rely on Program Files for storage in Vista.

Another typical solution is to simply create your own directory structure in C:\, but I find that unrealiable as well. Some users like to “clean up” their C:\ drive on occassion, and your application may all of a sudden not work anymore. Plus, it makes curious users into dangerous users if they can directly access config files or who knows what: I meant, these are users we’re talking about, and you just never know what they might do.

So the question becomes “Is there a safe, reliable directory we can use to store and retrieve such files from?” The answer is yes! .NET provides access to a series of special folders, enumerated in Environment.SpecialFolder. (The Environment namespace is part of System, so does not need to be referenced, it is always just accessible) Following the link will show you the list of Special Folders, most of which are pretty straight forward, like My Computer, My Documents, etc. There is one in particular, though, that is not so straight forward, but is exactly what we need: LocalApplicationData. This is a directory buried within the User structure. On my XP box, the path looks like this: C:\Documents and Settings\Joel\Local Settings\Application Data. Underneath this directory are a listing of other directories, usually company names (Microsoft has one, as does Google), and inside those directories are directories for specific Applications.

This is where we want to store our persistant data. It is safe from being overwritten, does not require Administrator priveleges, and most curious users aren’t going to go tampering with it. Another good thing about this approach is that we are going to use a system name to get the path: we are not going to hard code this path structure! I don’t have a Vista machine yet, but there is no guarantee that Microsoft kept the same structure. What they will keep, however, is .NET’s ability to find the path of this folder on any given system.

To get the path, you need to pass the SpecialFolder enumeration value you want to the Environment.GetFolderPath method:

// Get Special Folder path
string myLocalAppPath = Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData );

Your application directory will not be automatically created beneath this path. You will need to check for the existence of it and create it if necessary. This would be an ideal task for a Custom Action in the Installer.

This should provide the ideal location for your application settings and persistent configuration files.

1 comment

Deploying CF .NET 2.0 applications using MSI

So I’ve been developing Smart Device apps for the last two years, both in VS2003 and now in VS2005. But as the only CF developer in a tiny shop, I have always been able to install them by just using the Deployment option in Visual Studio. Now, however, one of the products I’ve been working on is ready to go to the field for some testing, which means other people have to be able to install it.

Frankly, most of the documentation on deplying CF.NET apps is pretty scary. CAB files, custom INI files, Registry entries, the works. I’ve definitely been spoiled by the MSI features available to Windows forms apps. So I’ve been digging around and experimenting, and I created a CAB file for install and then found the Smart Device CAB project, so I created that but it still required the user to manually copy it over to the device and double click it and yada yada yada. Too much for most of my target audience, and not very professional.

So I dug some more, and I was about to give up, when I found this article on MSDN about using MSI to handle the install to a Smart Device. I went through the article, and after a little trial an error I got it to work. Here are the highlights:

  1. Create your solution and build all the projects. Use Release to keep them as small as possible.
  2. Add a Smart Device CAB project to your solution. Add all your project outputs to the applications folder and add a shortcut to your EXE to the Program Files folder. Build this project.
  3. Add a new Class Library project to your solution. This is an MSI installer helper class that handles events like before_install and after_install. The cool thing is that these events will be found and used automagically later. Fill out the events and build this project. This file has some Registry stuff in it and references a custom INI file that you will need to create, but the forumla is very simple.
  4. Add a new Setup project to your solution. This will be the MSI file that is eventually used to communicate with ActiveSync and install the CF application. By adding custom actions to the installer and referencing the helper DLL we created in step 3, the before and after events will automagically find themselves and fire.

OK, so this is in no way a step by step instruction list. For that, you will want to read the article (like 20 times). It is a little dense but thorough, and I was able to get it to work. I did encounter a permissions problem in the afterInstall event, so I still need to hash that one out, but it did install the app on my device.

One quirk I found was that the article instructs you to add a reference in the MSI project to the System folder and to place your output there for the CABs and the INI file. Unfortunately, this doesn’t jive with the pathing samples in the Helper class code in the article, which references System/TEMP/MyProject. In looking at it, I think the TEMP path is the best way to go, so just create the TEMP/MyProject path in the System special folder and dump your output CAB and INI files there.

Next I’ll be tackling how to integrate CF .NET deployment in the same MSI, so stay tuned.

No comments

C# foreach howto.

I was getting sick of doing for loops so I decided to read up on the foreach statement. The foreach statement is small, clean, quick and much easier to read if you ever plan on having a programmer help you out with your coding. There is no need for me to ramble on so check out this code. The foreach statement basically lets you define an object, and loop through a collection of the same object type.

This simple example “checks” all the check boxes for all the items in a ListViewControl.

foreach (ListViewItem lvi in lvTBS.Items)
{
    lvi.Checked = true;
}

This is way better than doing this:

for ( i=0; i<=ListViewControl.Items.Count(); i++ )
{
    ListViewItem lvi = ListViewControl.Items[i];
    lvi.Checked = true;
}
1 comment

Finding an Enum value based on its text

I’ve been a fan of custom Enums since I started coding for .Net: like most software packages, we have tons of selection lists. Many of these are table driven, code-based lists like so:

Code : Description
10 : Asparagus
12 : Carrot
73 : Tomato

etc, etc.

Naturally, I don’t want my users to have to know that 73 means Tomato, so like a good little developer, I put these values into an Enum:

public enum Vegetables
{
    Asparagus = 10,
    Carrot = 12,
    Tomato = 73
}

Then I use that Enum to populate a ComboBox on my Form. To do so, I utilize the Enum class’s GetNames method.

foreach (string s in Enum.GetNames(typeof(MyClass.Vegetables)))
{
    this.cboxVegetableNames.Items.Add(s);
}

The trick here is to point the typeof parameter to the full class designation of the enumeration you want to list. This method returns an IEnumerable you can use to loop through the names.

So far so good: pretty straight forward and probably something most of us have already figured out. The problem I had recently was that I needed the Enum value in order to properly update a database. In other words, when the user selected Tomato, I needed 73. Getting the int value of an enum is easy enough via a simple cast:

MyClass.Vegetables veggie = MyClass.Vegetables.Tomato;
int veggieValue = (int)veggie;

So if I could figure out which Enum value was selected, I could easily get the int. The problem was, I only had the Text string from the ComboBox to go off of. I suppose I could do some sort of brute force loop through all the Enum members until I found a matching string, but that seems like overkill. It would be especially bad if I had lots of options in the ComboBox and lots of ComboBoxes.

Fortunately, I was able to find something a little better built right in to the Enum class:

MyClass.Vegetables veggie = (MyClass.Vegetables)Enum.Parse(typeof(MyClass.Vegetables), this.cboxVegetableNames.SelectedItem.ToString());
int veggieValue = (int)veggie;

It’s definitely a little ugly, but gets the job done. The trick to getting this to work is the cast into an Enum variable of the same type. Again we use typeof() to get a reference to the full class name of the Enum and then cast the results into an int.

Now you can easily work with Enums to put the list of strings into a ComboBox and later retireve the Enum value from the selected string. In a future article, I’ll demonstrate how you can use attributes to show different text than the actual member name.

No comments

The underlying connection was closed.

There seems to be a big problem with the underlying code behind the sockets classes that Microsoft uses in their .NET framework. Many people are getting a The underlying connection was closed. error message, but there doesnt seem to be any consistency to it. I sent a simple test program using the code below to a friend in Singapore and he gets the error. Well, I decided to create a very simple Socket program to see if he gets the error with it as well, and sure enough he does. His Windows XP computer is fully patched too. Some people are getting this error when connecting to a web service, and they have found a way to get around it most of the time, but the error still comes back from time to time even with that patch. This however, isnt a web service, just a simple WebClient piece of code that connects to a web server.

Microsoft has yet to release anything on this.

WebClient Class

a_url = http://www.google.com;
System.Net.WebClient WC = new System.Net.WebClient();
data = WC.DownloadData(a_url);

The follow socket class also throws the error, not with me, but with some people that I send it too. It seems to be random.

Read more

No comments

ini File Access with VB.NET

I thought I’d share a usefull VB.NET class for accessing .ini files. There are better ways to store local application settings than using a .ini. Sometimes it is nessessary to access legacy .ini files and this should help you get there.
Read more

No comments