Friday, March 27, 2009

Runtime profiling a COM registration failure

As a continuation of my last post, we had another registration error with the same legacy installer. This time the problem was with mssoap30.dll. Loading the dll in Dependency Walker revealed a single error with dwmapi.dll.

With a bit of web searching we found that dwmapi.dll is only present on Vista and later machines. Since we weren't using that dll directly it shouldn't be the cause of our issue on Windows XP.

At this point it would appear we have all necessary dlls and that our problem must be elsewhere. The documentation on Dependency Walker, however, provides some useful information:

When a module is first opened by Dependency Walker, it is immediately scanned for all implicit, delay-load, and forwarded dependencies. Once all the modules have been scanned, the results are displayed. In addition to these known dependencies, modules are free to load other modules at run-time without any prior warning to the operating system. These types of dependencies are known as dynamic or explicit dependencies. There is really no way to detect dynamic dependencies without actually running the application and watching it to see what modules it loads at run-time.

Version 2.0 and later of Dependency Walker provides a Profile option from the menu. If you have a dll loaded, however, the item is disabled - you have to load an executable first.

For this to work, you need to load regsvr32.exe, which will enable the menu items. Once done, select Profile > Start Profiling to bring up the Profile Module dialog. The only necessary change is to add the dll being registered to the "Program arguments" setting.

Press OK and wait for the application to run. Eventually you will see the usual registration error message box, which you can simply click to continue. Once finished, scrolling through the results shows a couple errors for mssoapr3.dll.

The first error states that the file path specified can't be found. Double-clicking the error shows that it is looking for the dll under "c:\temp\1033". The second states that it cannot find the file - this time in the same folder as the dll we tried to register. So it seems either location is valid for this particular dependency.

Once we add mssoapr3.dll to the same folder, mssoap30.dll registers without issue.

Tuesday, March 24, 2009

COM dll registration failure

Every shop has at least one legacy piece of software they have to support. Written in VB6. With lots of copy/paste code and sections that may or may not still be active. By a developer long since gone from the company. It's bad enough to have to deal with the code itself, but try and rebuild the equally-convoluted installer and you might just go mad.

Our problem wasn't in rebuilding the installer, per-se, but in getting it to run successfully afterward. One issue we ran into were COM dlls that failed to self register.

When we attempted to run regsvr32 on this particular dll, we were given an error dialog stating:

LoadLibrary("C:\WINDOWS\system32\pcmcom.dll") failed - The specified module could not be found.

This particular issue is often due to a missing dependency. To verify this we opened up Dependency Walker and loaded pcmcom.dll. In the screenshot below, you can see the yellow question mark symbol next to pcm.dll - our missing dependency. Once we found pcm.dll and its dependencies, pcmcom.dll registered without issue.

Friday, March 20, 2009

Subversion revision corruption

Say that three times fast...

We ran into an interesting issue earlier this week with Subversion. When attempting to perform any sort of operation on one of our repositories, the operation failed with an error stating "Final line in revision file longer than 64 characters svn: PROPFIND of '/svn/myRepository/myProject'."

When a checkin is made to Subversion, the file differences are compressed and saved to disk under \myRepository\db\revs\. Each checkin will be in a separate file named to match the revision number. Thus revision 1200 is saved to a file named "1200" (no extension.) If you open one of these files in a text editor, you will mostly see unreadable binary. One noticeable feature in all of the files is a pair of numbers on the last line - "10550 10745" perhaps. In our particular case the final revision contained two additional lines following the number pair: - myUser [16/Mar/2009:10:12:06 -0500] "PROPFIND /svn/myRepository/!svn/bln/2914 HTTP/1.1" 207 464 - myUser [16/Mar/2009:10:12:06 -0500] "PROPFIND /svn/myRepository/myProject HTTP/1.1" 207 714

These lines are normally found in the Apache log file - not a good sign. A web search verified that it is a known issue with no resolution. The good news is that the page links to a Python script that attempts to repair corrupt revisions. (More information on using the script can be found here.)

The bad news? The script didn't entirely work for us - some of the header info on the compressed diffs had been lost. Fortunately, it did remove the Apache log info from the tail of the file, so other developers could again work. The only section now corrupt was the project where the revision error occurred. This project started returning an error stating "Checksum mismatch while reading representation."

With the issue isolated, the next step was to remove the corrupt revision. Unfortunately, the only way to do that in Subversion is to export the good revisions and then build a new repository using those exports. Say revision 120 is bad and we have a total of 150 revisions. We start by dumping revisions 1 through 119 to a file:

svnadmin dump c:\myRepository\ -r1:119 > c:\DumpPart1.dmp

We then need an incremental dump for changes 121 through 150:

svnadmin dump c:\myRepository\ -r121:150 -- incremental > c:\DumpPart2.dmp

Rename the folder "myRepository" to something else as a quick backup, then create a new database:

svnadmin create c:\myRepository

Copy any config files from the backup repository (such as user accounts) then load the dumps into the new database:

svnadmin load c:\myRepository < c:\DumpPart1.dmp
svnadmin load c:\myRepository < c:\DumpPart2.dmp

One item of note: Loading a Subversion dump effectively creates new checkins for each item in that dump. All items in the second dump above will have a revision number one lower than previously - checkin 121 in the old database becomes checkin 120 in the new database. Though I've not seen it, some developers may receive an error if their local code contains a newer revision number than the database. As I ran a few test checkins after creating the new database, this wasn't a problem.

Friday, March 6, 2009

Debugging InstallUtil.exe

The project I'm currently working on contains a Windows service with a number of performance counters. To run the code on a dev machine, we prefer to do so without first running an msi. So to get the performance counters installed, we tried to run installutil on the service executable. Which of course returned a vague error message and rolled back the changes. Fortunately, this type of problem wasn't too difficult to debug.

To demonstrate, I've created a new application and added a class that inherits from Installer. I've overridden the OnBeforeInstall event as follows:

protected override void OnBeforeInstall(IDictionary savedState)
    string nullString = null;
    int invalidLength = nullString.Length;

If I attempt to run "InstallUtil MyApp.exe" I receive the following within the console output:

An exception occurred in the OnBeforeInstall event handler of MyApp.MyAppInstaller.
System.NullReferenceException: Object reference not set to an instance of an object.

For trivial code such as my above sample, tracking down the problem should take most developers all of five minutes. Once you have a non-trivial amount of code (which was the case at work) you need a little more information. If I call "InstallUtil /ShowCallStack MyApp.exe" I receive the exact line where the problem occurs:

An exception occurred during the Install phase.
System.InvalidOperationException: An exception occurred in the OnBeforeInstall e
vent handler of MyApp.MyAppInstaller.
at System.Configuration.Install.Installer.Install(IDictionary stateSaver)
at System.Configuration.Install.Installer.Install(IDictionary stateSaver)
at System.Configuration.Install.AssemblyInstaller.Install(IDictionary savedState)
at System.Configuration.Install.Installer.Install(IDictionary stateSaver)
at System.Configuration.Install.TransactedInstaller.Install(IDictionary savedState)
The inner exception System.NullReferenceException was thrown with the following
error message: Object reference not set to an instance of an object..
at InstalUtilDebugging.MyAppInstaller.OnBeforeInstall(IDictionary savedState)
in C:\source\temp\InstalUtilDebugging\MyAppInstaller.cs:line 23
at System.Configuration.Install.Installer.Install(IDictionary stateSaver)

In many cases, this will be enough to solve the problem. There may be cases, however, where you need to attach a debugger to the process and step through the code to see where a problem actually started. To do this I add the following line to the beginning of OnBeforeInstall:


After I recompile and again run InstallUtil, I am given a JIT Debugger dialog which allow me to attach to the process.

I select the instance of VisualStudio that has my install code and click 'Yes' to begin debugging. You may be given a warning stating "There is no source code available for the current location." Click 'OK' and press F10 once - this will show the Debugger.Launch statement as the current line of code being executed. From here debugging works as in any application.