Tuesday, May 20, 2008

Debugging .Net Framework Code

When I started trying to write C# code, I spent most of my time with the Reflector trying to figure out what the .Net Framework was doing under the hood. My start routine was: start the debugger, see unexpected results, read source code in Reflector, repeat. I spent a lot of time searching for functions in the Reflector.

I spent years doing MFC programming. MFC shipped with complete source code from the very early days. Without that source code, it would have been almost impossible to solve many of the problems I ran into. (See my earlier blog about fixing problems with MFC symbols in Visual Studio 2005.)

I was tickled to discover today that Microsoft has made the source code of the Framework available for debugging purposes - even specialized code like Asp.Net. You must be running Visual Studio 2008. Take a look at Shawn Burke's Blog showing how to set it up.

Make sure you install the Visual Studio hotfix (QFE)!

Thursday, May 15, 2008

What do developers want for working conditions?

There's a great article by Joel Spolsky titled The Joel Test: 12 Steps to Better Code. This article talks about how to write better code, but it's equally relevant to building an environment where software developers want to work.

The article hits home for me because, in the mid-90s, I worked for a company that was developing software for Microsoft. By requirement, we adopted methodologies that Joel talks about, like the "zero defect methodology." This had an almost miraculous effect on our ability to ship stable software on time. Even today, I can tell with frightening accuracy when a project will ship by doing nothing more than looking at the bug database statistics. The last person to argue with me on the subject was the president of a $50M company. I said they'd ship in 2 to 3 years - if they were lucky. He said I had no idea what I was talking about because I was just a lowly engineer and he was certain that they'd ship in three months. Two years later, I was still waiting for the initial release.

We also adopted other Microsoft techniques, such as the 1 to 3 ratio of testers to developers. This change also had a dramatically positive effect, both on the ship schedule and on the morale of the developers. Consider that developers spend 80% of their time on the "normal" case. Testers spend 80% of their time on the boundary cases, thereby acting like customers who abuse the software right out of the box. If you really want to make a developer unhappy, tell him to spend 80% of his time testing (as opposed to developing) obscure boundary cases, like whether the software correctly handles 260 character filenames on multibyte character systems. Most developers will just read Slashdot instead.

Joel also talks about quiet working conditions, which is one of my pet peeves. The last five companies I worked for all had overhead intercom systems that would be used to page people throughout the day. Every time one of those pages came on, it interrupted six to twenty developers and broke them out of "the zone". Aggravated me no end.

The final point that Joel talks about is Netscape. Everyone says that Microsoft put Netscape out of business. However, Netscape did far more damage to themselves than Microsoft ever did. Netscape released version 4.5 in October, 1998. They didn't release a stable upgrade (7.0) until five long years later. This means that, for all intents, Netscape completely sat out the second half of the DotCom era. They tried to start fresh on an entirely new project, but their cadre of 20-something programmers simply didn't have the project management and large-scale software development experience to pull it off.

In summary, Joel's article is now eight years old, but many of the points discussed in the article, such as continuous builds, are now considered de riguer of any software development effort with any self respect. It's worth your time to read.

Friday, May 2, 2008

Fixing "PRJ0050 Failed: Failed to register output"

I'm in the process of converting a COM DLL over to .Net using C++/CLR and I've been plagued with this error:

RegAsm : error RA0000 : An error occurred while writing the registration information to the registry. You must have administrative credentials to perform this task. Contact your system administrator for assistance
Project : error PRJ0050: Failed to register output. Please try enabling Per-user Redirection or register the component from a command prompt with elevated permissions.

I used Google to try and find a solution and found more confusion than solutions. The correct solution (for me) didn't appear anywhere. So here's my guide to solving this error.

There are three possible causes. Diagnosis is usually straightforward. Run Regsvr32 and try to manually register the DLL. If you see "entry-point DllRegisterServer was not found" then jump down to Disable Registration. If you see "access denied" then jump down to User Account Control. Finally, if you get "missing dependency," jump down to Missing Dependency.

User Account Control

This error is most likely to happen on Vista when you try to register a COM DLL. The reason is that, even if you are running as Administrator, you aren't really an Administrator because of User Account Control (UAC). This problem is easy to test. Enable per-user redirection in the project properties under Linker / General. Force the project to relink (make a minor change to a source file) and see what happens. If the error goes away, then you've found the problem. Amazingly enough, PROJ0050 is specifically mentioned in the documentation for the Linker Property Pages.

Other more intrusive solutions:
  • Close Visual Studio and restart it as an Administrator by right-clicking the Visual Studio icon and selecting Run as Administrator. The screen should blink and you should see the UAC prompt.
  • Always run Visual Studio as Administrator as follows. Right-click the Visual Studio icon, select Properties, go to the Compatibility tab, and enable "Run this program as an administrator."
  • Disable UAC (not recommended.)
  • Ignore the problem. It's not hurting anything.
No matter which solution you choose, make sure you test the registration of the dialog on a clean machine when you build your installer.

Disable Registration

This was the solution that applied to my problem. My project used to be a COM object written using MFC. The COM object required registration and so the project was set to register the DLL after linking. My fancy new .Net object did not require registration, so there was no "DllRegisterServer" entry point in the DLL. I fixed the problem in the project properties under Linker / General by setting Register Output to No. Make sure you do this for both Release and Debug builds.

Missing Dependency

This problem happens because your DLL is dependent on another DLL that can't be located. It's easy for this problem to happen if your DLL compiles to one directory and a dependent DLL compiles to another directory.

To diagnose this problem, use the Depends utility. Don't use the version that ships with Visual Studio, it's out of date. Download the latest version of Depends from http://www.dependencywalker.com/. When you run Depends, make sure you open the DLL that's in the target directory where the linker put it. Now look down the list in the middle pane and see what's marked with a yellow question mark. Ignore any modules with an hour glass, they are delay loaded and don't cause a problem if they are missing.

If the missing DLL is one of your DLLs, then update your path to include that DLL. Problem solved.

If the missing DLL is one of the MFC or C Runtime DLLs (which start with MSVC or MFC, respectively), then life is much more complicated. You might be having a problem with WinSxs, which means there's an error in your manifest. These problems can be nasty to fix, as I described in this post.

Finally, there was a red herring for my project when I ran Depends. The file MSVCR90D.DLL was marked as missing. I'm not sure what was causing this, but the DLL ran and loaded properly, so I'm suspicious that the problem was with Depends.