Tom Goff's .Net Musings

Tidbits of information regarding .Net, C#, and SQL Server.

Custom Setup Action Woes

with 3 comments

We use custom setup actions in our Visual Studio Setup project in order to create shortcuts to our application on the Quick Launch toolbar and the Desktop, if the user so chooses. The managed custom action is easy to get setup and running, but troubleshooting issues can be a real pain.

NOTE: If you don’t know what a custom action is then check out this link[^]

The main source of pain is the fact that the errors only occur at the very end of the installation of the application. This means that I have to build an installer every time I want to try to test a “fix”. When diagnosing the issue, there are a few areas to check for information on the problem.

Generally, the first thing to do is enable logging for the MSI. If you know that it’s a problem with your custom action, then this probably won’t help. Next, it’s best to verify that the assembly holding your custom action does not have any binding issues. To determine this you need to use fuslogvw[^]. If your assembly cannot load because of a missing reference, then this will point out the problem. The latest technique in my arsenal is to use installutil[^]. Let me explain how this helps.

The message box below is one error I was receiving. Again, this error occurred during installation and would force a rollback of the installation. So, once I clicked OK then the installed files would be removed. There is generally a second dialog after this with the same information because it is trying to perform the rollback of the custom action. In order to use installutil, I would leave the dialog open so the offending files would be (temporarily) left on my system.

I’m sure we have all seen this error in the Visual Studio designer. In fact, when searching for a solution to this issue, all of the sites I found dealt with the designer. Anyway, I used installutil to “re-run” my custom install. Since the files were still on my system, I could just open command-prompt  (with the VS directories in the PATH) to my application’s folder and type the following:

C:...>installutil /logtoconsole=true /showcallstack CustomActionTest.exe

 This allowed me to see the callstack of the exception that was being thrown:

System.Reflection.ReflectionTypeLoadException: Unable to load one or more 
of the requested types. Retrieve the LoaderExceptions property for 
more information. 
at System.Reflection.Module.GetTypesInternal(StackCrawlMark& stackMark) 
at System.Reflection.Module.GetTypes() 
at System.Configuration.Install.AssemblyInstaller.GetInstallerTypes(Assembly assem) 
at System.Configuration.Install.AssemblyInstaller.InitializeFromAssembly()

Now we know the type of exception that is being thrown and where it’s being thrown from. The first thing to note here is that the ReflectionTypeLoadException[^] class has a LoaderExceptions property. This property returns an  “array of exceptions thrown by the class loader.” What I really wanted was the information in the LoaderExceptions property. Unfortunately, this information is not logged or reported anywhere. So my only option was to build my own application that could reproduce the exception.

Luckily, the code that threw the exception was simply opening the assembly, iterating over it’s modules, then iterating over the types in each module. The basic code is shown here:

    Module[] modules = assembly.GetModules(); 
    foreach (Module module in modules) { 
        Type[] types = module.GetTypes(); 
        // ... 

After building an application that loaded my assembly and performed the code above, I was able to view the contents of the LoaderExceptions property. It turns out that some old, unfinished, and unused code was causing the problem. Specifically, I had been building P/Invoke calls to the Windows shell interface IShellFolder[^]. This interface uses a structure with a union called STRRET[^].

The quick conversion of STRET to C# equates to:

    public struct STRRET { 
        public Int32 uType;  

        public IntPtr pOleStr;  

        public Int32 uOffset;  

        public IntPtr cStr; 

At the time, I was trying to tweak the signature to use a .Net char array, like so:

    public struct STRRET { 
        public Int32 uType;  

        public IntPtr pOleStr;  

        public Int32 uOffset;  

        [FieldOffset(4), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 
        public Char[] cStr; 

Now as I said the code was unfinished and was not used anywhere in our application. In addition, the code compiles without any warnings or errors. Yet, this code completely disables the ability for my assembly to have it’s types reflected. I’m guessing this is an issue with the C# compiler, since it does not report this as invalid.

Anyway, I’ve put together a sample project[^] that illustrates all the points talked about above.


Written by Tom

May 24, 2007 at 9:18 am

Posted in .Net, C#, Gotcha

3 Responses

Subscribe to comments with RSS.

  1. this information is useful and interesting to me ;), thanks you!


    October 9, 2007 at 11:21 pm

  2. Thanks!,


    December 13, 2008 at 11:50 am

  3. Thanks for this great post. It helped me a lot! 🙂

    Rene Schulte

    June 25, 2010 at 8:02 am

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: