Monday, July 02, 2007

Dreams and Mistakes

Firstly, in my recent post about the new .Net DateTimeOffset class I mentioned that I would love to see the SQL Server team include this type in databases. My wishes have been answered, we can expect to see this type and some other very interesting additions in the next CTP/Beta of SQL Server 2008. Unfortunately, all of our clients have bought into SQL Server 2005 and won't be interested in upgrading anytime soon so I'll have to find a new project to try all the new toys.

Secondly, also in a recent post I complained about the CollectionBase type being left behind when generics were added to .NET 2.0. I've since discovered the new Collection<T> class tucked away in the System.Collections.ObjectModel namespace. It doesn't offer all the events that the previous equivalent did but it is inheritable and overridable so it should do the job nicely. I obviously didn't search very hard for it all the other times I looked; very embarrassing.

Death, Taxes and Software

Tax Calculator The new 2007/2008 financial year has begun and as promised the Australian Taxation Office released the latest version of their e-tax software on July 1st. As I have for the past several years, I downloaded and installed the software and have started entering my income and deductions for the last 12 months.

When the official paperwork arrives, I'll confirm all my figures and submit my tax return electronically and receive my rebate usually within a week, deposited directly to my bank account. Considering the complexity of the income tax system, the software is excellent for non-accountant-types to complete their own tax return.

Unfortunately, e-tax 2007 is still stuck in the obsolete Windows administrator user world. Installation defaults to "c:\etax2007\". It has been over twelve years since Windows 95 was released and the standard was set for software to install into Program Files.

A single e-tax installation on a PC allows multiple people to fill in and submit their tax return, saving their work in progress locally until it is ready to send. However, the software assumes the user is an administrator and writes the *.tax progress file to the installation folder instead of Documents & Settings.

At several stages throughout the questionnaire, summaries and important details are presented to the user with a recommendation to print the information. However, the software always uses the default printer with the default settings, offering no dialogs, and only picking up changes to the default printer after restarting the software.

One of the biggest reasons why e-tax is so great is because the questions are presented in easy to understand language. Whenever more detail is required there are always hyperlinked keywords or a help button leading to a more extension definition with examples. Sadly, it's all useless under Vista because they've stayed with the deprecated WinHelp format for the documentation.

Lastly, for users of Apple computers (and presumably other OSes), the official solution is to use virtualization. I wonder if the purchase price of VMWare or Parallels and a Windows licence can be claimed as a cost of preparing the tax return.

By all appearances e-tax is a full Win32 application, probably written in C++ at first glance, and is expected to continue in that style for the majority of users next financial year too. Perhaps by 2009, they will have moved to a web-based client, which will open the system to non-Windows users but probably introduce other problems along the way.

 Wednesday, June 27, 2007

Ecstatic About NStatic

Ecstatic MascotWesner Moise is getting ever closer to public release of his very promising NStatic static analysis tool for .NET. In general static analysis tools are very expensive but as part of Wes' plans, a lower price to achieve market penetration is most likely. In fact, until he finds a suitable publisher he expects to selling independently for a few months, during which time we might be lucky to pick it up for a "bargain" price.

So far I think NStatic only supports C# 2.0 but there are plans to add support for C# 3.0 features and maybe VB.NET too if the demand is there. I, for one, sure hope that VB support becomes a reality, I think our current suite of software could really benefit from some in depth analysis.

Keep a close eye on Wesner's blog for the latest information.

DasBlog Flip Flop

A few months ago I upgraded this web site from the DasBlog 1.8 installed by my ISP to the latest version at the time, 1.9.6. It was remarkably easy to merge my web.config and site.config files and upload all the new binaries.

Yesterday Scott Hanselman announced DasBlog 1.9.7 had been released. Last night I tried to upgrade again, following a very similar procedure as I did the last time. Being a point release, I expected even less effort and was happy to find that very little merging was required with the config files.

Unfortunately, once I had uploaded the new files to my site I found I could no longer log in. I refreshed the browser several times, edited and uploaded the siteSecurity.config file several times and tried the default file with the admin/admin credentials. Nothing would let me login and all I had was a SecurityFailure message in the event log.

This took an hour or so and I eventually gave up and completely restored the backup I made before attempting the upgrade. Guess I'll have to configure IIS locally and experiment until it works.

On a better note, Scott says this is the final release for ASP.NET 1.1 and another release due very soon will be completely ASP.NET 2.0 and support Medium Trust. I was annoyed last time I downloaded the source and discovered that I would need to install VS2003 to work with it but finally I'll be able to contribute to the project.

 Tuesday, June 26, 2007

DateTimeOffset Is The New DateTime

SundialJustin Van Patten neatly summarises the new DateTimeOffset class introduced in .NET 3.5. In short, this new class combines the year, month, blah, blah, second storage of the DateTime with a time zone offset. The most important point to take from Justin's article is that apart from a few exceptions (which he describes), you should always use DateTimeOffset in new code instead of the old DateTime.

Naturally there will continue to be situations where you need to pass a DateTime but the DateTimeOffset will always be able to scale back to suit. It is much harder to infer time zone information from a DateTime to provide a DateTimeOffset. I'd love to see the SQL Server team introduce this data type concept to databases.

CollectionBase and Generics

In .NET 1.1 the Framework included an abstract class System.Collections.CollectionBase. The idea was that you could extend it to create your own custom collections to hold certain types. The Add, Remove, and other methods were overridable (to perform object validation or filter duplicates) and several events also existed to enable listening for changes.

Cardboard Box Most importantly it provided the mind-numbing mapping from the IList, ICollection and IEnumerable interfaces to the internal storage mechanism (which happened to be an ArrayList). When I created a new collection derived from CollectionBase I could override the OnValidate method to only accept a certain type and I was done.

Sadly, I'm left stranded in my .NET 2.0 world with Generics and an untyped CollectionBase. I cannot inherit from List<T> because none of it's methods are virtual and none of it's fields are protected. Ultimately, I'm stuck with writing my own class with a private List<T> field and coding most of the methods on the various interfaces as redirections to the private field. No thanks, that was one of the biggest reasons for moving from VB6 to .NET.

Interestingly, the SharePoint fellows noticed the same problem with CollectionBase's obsolescence in .NET 2.0, and cleverly decided to provide the more up-to-date CollectionBase<T>. Of course, not only did it end up in the Microsoft.SharePoint.Publishing assembly that I'm reluctant to reference and probably unable to distribute, but they also implemented only ICollection and IEnumerable<T>. I was dumbfounded that IList<T> and ICollection<T> had been missed and that they had also removed IList.

Why am I still forced to code this junk myself?

Windows Vista, UAC, Flame On!

If you are running Windows Vista and are bothered by "too many" UAC dialogs on a regular basis to the point that you want to turn UAC off, the problem is that you are futzing with your PC instead of doing real work.

 Saturday, June 23, 2007

Crashing Visual Studio Unit Testing

Yesterday I managed to crash vstesthost.exe, the program that runs Visual Studio Unit Tests when you press Ctrl+Shift+X. A few tests in my suite would run and pass, then BAM, fatal error. I ran the tests again, this time in debug mode and the tests broke into the debugger with a StackOverflowException, due to a poorly implemented interface in a mock object.

According to some searching, some exceptions including StackOverflowException, just cannot be caught in .NET 2.0 and I'm not sure it would be smart to try. Unfortunately when it happens in testing, the whole suite fails. In my case, I was able to pave over my mistake in the code and move on with my life.

The vstesthost crashing problem also happens when an exception is through on a secondary thread though. Testing asynchronous UI or background processing code could also kill the process when any exceptions occur, and these might be expected exceptions for a particular tests.

I haven't needed to use it yet, but there is a workaround to avoid the whole test suite falling over. It involves editing the vstesthost.exe.config file to enable the legacy unhandled exception policy as used by the earlier CLR versions. You will need to be careful to synchronise your threads though to ensure any exceptions are assigned to the correct test in the results.

 Friday, June 22, 2007

Feature Suggestion For DotNetKicks

DonkeyGavin Joyce announced a few months ago that the DotNetKicks community website will be going open source. Gavin has since created a DotNetKicks project on Google Code with some placeholders and has asked for people interested in contributing to join. One thing that isn't clear is whether the engine will be open source for others to build their own sites or if all the updates will get deployed back to DotNetKicks.com.

Regardless, I already have one idea in mind. In a moment of complete clumsiness, while trying to quickly submit a post to DotNetKicks before going to bed, I associated the wrong Title and Description with the wrong Url. Unfortunately, even though I had submitted the item, I was unable to correct the Title or Description and I was unable to delete the submission altogether and start again. I unkicked the post back to zero kicks but that didn't help.

My suggestion therefore, is that the submitter of an article should be able to delete that submission. Perhaps, to be safe, it could be restricted to only allow deletion if no one else has kicked it.

Until then, I'll just have to live with looking stupid for my story on DotNetKicks which, strangely, someone other than me has kicked anyway.

Drive By PowerShelling

I have been using an external USB hard drive to take backup copies of certain files on several machines. Each machine has different drive configurations though so the USB drive will be assigned different drive letters on each machine.

I could have found an unused drive letter common to all the machines and assign it to USB drive on the first use but that's no fun. I decided to do it the PowerShell way. I wrote a small function to find a System.IO.DriveInfo object by providing the volume label and then I can get the drive letter and pass it to Copy-Item to do the work.

function Get-DriveInfo ([string] $label = $(throw "Please specify a volume label.")) { 
   [System.IO.DriveInfo]::GetDrives() | ? { $_.IsReady -eq $True -and $_.VolumeLabel -match $label }
}

 Wednesday, June 20, 2007

Beware The Evil Shadow

Evil Shadow Object-oriented programming principles have been an excellent addition to software development but one component that inevitably appears in OO languages has confounded me by it's uselessness. In VB.NET it is known as the "Shadows" modifier, in C# it is found in class member declarations sharing the name "new".

The purpose of this modifier keyword is to allow a subclass to redefine a property, field, or method of the base class where the base class has not been declared "Overridable" or "virtual" in VB.NET or C# respectively. Unfortunately it suffers from a major drawback, as demonstrated by the following code:

class Base
{
   virtual public string Extendable() { return "Extend"; }
   public string Locked() { return "Lock"; }
}

class Foo : Base
{
   override public string Extendable() { return "Food"; }
   new public string Locked() { return "Picked"; }
}

Base b = new Base();
b.Extendable(); // returns "Extend"
b.Locked(); // returns "Lock"

Foo f = new Foo();
f.Extendable(); // returns "Food"
f.Locked(); // returns "Picked"

Base bf = new Foo();
bf.Extendable(); // returns "Food"
bf.Locked(); // returns "Lock" !!!

As you can see, if the Foo methods are called via a Foo-typed variable, there is no difference between shadowing and overriding. However, as soon as you call a Foo method via a Base-typed variable, or pass a Foo instance to another class expecting a Base, you lose the functionality of Foo.

If "new" is omitted from Foo's implementation of Locked, Visual Studio reports an error: The keyword 'new' is required on 'Locked' because it hides method 'Base.Locked()'. It is very easy for someone to simply add the keyword as required with realising the consequences.

Occasionally, all you might want to do is utilise some existing behaviour in a base class and add some more features and shadowing or member hiding is fine. The fact that you have used shadowing is not obvious in the calling code though.

Most of the time, I want to alter the behaviour of a base class and pass my new subclass to existing code, perhaps in the Framework, that is only expecting the base type. Beware.

Waste Management

The script in my last post for removing old files from the temporary folder is only necessary because so many applications don't clean up after themselves. Hopefully, if the applications you are developing are doing the right thing they will be using in memory streams where ever possible. Only write to disk when absolutely necessary, creating random temporary files in the environment temp folder, not program folders with restricted access to non-admin users:

string myTempFile = System.IO.Path.GetTempFileName();

However, remembering to track and delete these files when you are finished requires more effort and is often ignored, creating the ever growing temp folder problem. There is a lesser known class in the framework that can help you though:

var myTempColl = new System.CodeDom.Compiler.TempFileCollection();
string myTempImageFile = myTempColl.AddExtension("png");

Excuse the C# 3.0 syntax in the last snippet, I hate the redundancy of repeating long type names, especially on the same line. When you have finished using all the temp files you created with the TempFileCollection, you can dispose it explicitly, automatically deleting the files, or if you're really lazy, let the GC take care of it.

myTempColl.Dispose();

PowerCleaning

I've often wanted to easily delete all old files below a folder that haven't been modified for a while. I've written a very small PowerShell function to do just that. By default it emulates the Disk Cleanup tool in Windows and deletes all files older than one week from the environment temp folder.

function Remove-TemporaryItems ([string] $path = ($env:TEMP), [TimeSpan] $age = (New-Object System.TimeSpan 7, 0, 0, 0) ) {
    $files = Get-ChildItem $env:TEMP -recurse | `
        Where-Object { ! $_.PSIsContainer -and $_.LastWriteTime.Add($age) -le [DateTime]::Now };
    $files | Remove-Item;
    $files;
}

 Tuesday, June 19, 2007

Malware And Least Privilege, Secure Enough?

Jeff Atwood had some trouble with malware last week. His digging led him to blame a particular website for infesting his system. In a comment on his post, I proposed, based on my experience, that simply running as a non-administrator user instead on his clean unpatched Windows XP SP2 installation should have protected him for the most part.

I'm not suggesting that patching is a low priority but from my (potentially outdated) understanding of malware attack vectors, running as a least privileged user would have gone a long way. I tried to find some good websites to verify my assumptions but didn't have much luck. What follows is my interpretation of the situation, please leave a comment if I have underestimated the capabilities of today's malware.

  • Ethernet connector virus figure Most malware wants to get installed on your PC and survive a reboot: A non-admin user only has privileges to write to "safe" areas of your file system and registry. Malware would be unable to replace commonly used system files. It would be unable to install itself as a driver or service with higher privilege. It would be unable to schedule itself to run when another user logs on. It would be unable to place shortcuts in an area that another user would see in their profile. Lease privilege users cannot install browser plugins. System level root-kits should be unable to hook in and a reboot with a Live CD should be able to see and remove anything evil.
  • Most malware wants to provide remote access to your PC: XP SP2 ships with the firewall enabled. Malware would be able to phone home but would be unable to open a port and await incoming connections. Modern broadband routers naturally prevent incoming connections too. The malware would still only be able to give the remote user least privilege access to the system.
  • Most malware wants you to give it something: As a non-admin malware would not be able to modify your drivers/etc/hosts file to redirect network traffic. It would be able to spawn many popup windows attempting to sell you something but this would be nothing more than annoying. It could scan your user documents for email addresses but hopefully your contact list is online (eg Gmail) or in Outlook (which warns the user when accessed programmatically). It could log key presses and grab sensitive passwords, which is definitely a concern, but this may be reduced by the "remember my password" features in software.

Jeff's situation was an extreme example of an unprotected system. Personally, I'm using Vista 64-bit which immediately reduces the amount of malware that is actually compatible. I'm also running non-admin with UAC on so anything questionable needs my permission. I have Internet Explorer 7 which displays the Information Bar whenever something wants special permission. Data Execution Prevention and ASLR are on by default to minimise exploitation of buggy system code. I still have Windows Defender and run Ad-Aware occasionally. Every installed application is patched to the latest update. Lastly, I avoid questionable material and have another separate non-admin user account if I really need to try something.

I'm sure I'm not 100% safe, but I don't think that's currently attainable. Any machines I remove malware from for friends get returned with a non-admin account and they only come back to me with more problems after sheepishly admitting they went back to an admin account. I have read that the new hardware-virtualization features in new CPUs (which I have enabled) could allow malicious code, and then there is everything else I haven't heard of.

I think I've rambled enough and probably missed many things but I'm interested in reading your thoughts on the matter. If you can recommend a good source for the latest PC security information I'd appreciate that too.

 Monday, June 18, 2007

ClickOnce and Terminal Services

Under just the right conditions that I have been lucky enough to meet, .NET applications deployed by ClickOnce will not start under a Terminal Services session. At the heart of the problem is this error message:

Shortcut activation from http/https or UNC sources is not allowed.

I could not find anything in Google, on the MSDN forums or in the Microsoft Support Knowledge Base. With some trial and error and a little reflection I've tracked it down to this very special combination:

  • ClickOnce offline install mode is enabled
  • and the application is started via the Start Menu shortcut
  • and the user is logged into a Terminal Services session
  • and Terminal Services is redirecting the Start Menu to a UNC.

Changing any one of these conditions is enough to avoid the problem and would probably explain why it is documented anywhere. The error message is triggered by code in the System.Deployment assembly and it it cannot be overridden by a system setting either.

Redirecting the Terminal Services Start Menu to a UNC is also a common practice when running multiple terminal servers, or a shared Start Menu for all users (as was my case).

I will be recommending users simply start the apps via the same ClickOnce deployment URL that was used for the initial install and try to disable TS folder redirection where possible.

It seems ClickOnce uses the shortcut location later in the bootstrap process and the problem exists in Orcas too so I don't think Microsoft will be fixing this one.

 Friday, June 15, 2007

Rounding

In the .NET Framework, the System.Math class has methods for Ceiling, Floor, Round, and Truncate. Round has an overload for specifying the number of decimal places to round to but it doesn't support other intervals, for example: rounding to the nearest five cents.

The mathematics involved for supporting other intervals is quite simple when you know it but getting there and verifying it can take a few moments. Here is the general solution to save time next time I need it, or if someone else is looking for the same thing:

decimal offset = 0.05M;        // interval we are rounding to (5 cents)
decimal value = 1.23M;         // value to be rounded
decimal temp = value / offset;
temp = MyRoundToInteger(temp); // toward zero or banker's rounding function
decimal result = temp * offset;

You can exchange MyRoundToInteger for any of the afore mentioned Math class methods or even one of your own custom rounding techniques, as long as it rounds to zero decimal places

 Thursday, June 14, 2007

Premature Analysation

I was thinking today as I was resolving a few Code Analysis violations that perhaps I should stop. I will assume you are familiar with the quote "Premature optimisation is the root of all evil" and if you're not, you should be.

I've often had the feeling that something wasn't quite right when I adjusted my code to satisfy the various CA performance warnings. Today I have investigated that feeling further:

Winner of Zurich Marathon 2007 CA1809: Avoid excessive locals - Triggers when you have more than 64 local variables in a method because the processor registers can no longer hold them all and some will be moved to and from the stack. If you have this many variables in a single method you have other problems to worry about.

CA1811: Avoid uncalled private code - Triggers when a private method exists that isn't called directly. This is great for finding and removing dead code but this is more about readability and not runtime performance.

CA1812: Avoid uninstantiated internal classes - Very similar to CA1811 but at the class level. Once again, good for removing dead code but not really performance related.

CA1807: Avoid unnecessary string creation - This is for people who still insist of using ToUpper or ToLower on strings before performing comparisons. Embrace the Unicode world and pass the StringComparison enumeration to String.Equals and String.Compare calls. This rule belongs in the Globalization set.

CA1813: Avoid unsealed attributes - This might be the first real performance rule so far but it does apply specifically to reflection on custom attributes. If your application makes heavy use of attributes, you may want to pay attention here. I think though that your personal philosophy on preferring extendable classes versus sealed classes may have more weight here.

CA1801: Review unused parameters - Once again, this is an excellent rule for locating dead code that may be misleading or unnecessarily complicated to maintain. I am beginning to wonder if the Microsoft.Performance CA category is a misnomer.

This covers the first third of the 18 rules on the performance warnings list. So far only one could potentially undermine the performance of your application given the right conditions and I'd run an actual test to time this before I started making big changes.

However, the rules shouldn't be ignored, perhaps they should just be considered from a point of view other than performance. Hopefully investigating the rest of the list, which I plan to do in future posts, will reveal valid rules for improving performance. I'm expecting at least CA1818 to come though for me there.

Strength In Numbers

Since Visual Studio 2005 has made the whole process much easier, I strong name every assembly I work on. It's one of the first things I do when I add a new project to a solution: Choose a name, set strict compilation warnings, enable Code Analysis, assign a strong name key.

I have used a few open source projects lately and have been disappointed that the binaries haven't always been strong name signed. This means I have to get the source instead, setup a new key and adjust their build script to use it before I can even add it as a reference to my own project.

Weight Lifting There are several benefits to using strong names:

  • Optional deployment to the GAC.
  • Satisfy Code Analysis rule CA2210.
  • Use of the InternalsVisibleTo attribute.
  • Reference from other strong named assemblies.

I don't think I've ever deployed any assemblies to the GAC, so only the last three are relevant for me but I'm sure GAC deployment is important to people writing COM interop code or add-ins for unmanaged applications or it will be important one day when their project gets reused in that context.

The counter-argument is that strong names are supposed to provide security and handing out the private key file with rest of the source would defeat the purpose. However, Mike Downen, program manager for security on the CLR team, says this:

They have no revocation capabilities and they don't expire. Nor do strong names inherently provide any way to securely map a public key to a particular publisher. Thus, strong names should be used very cautiously when making security decisions.

For these reasons I think it is a good idea to strong name all assemblies. Perhaps use a different strong name private key for each program. The owner(s) of an open source project could even retain a private key separate from the one distributed by the open source repository and use it for signing official binary releases.