I have written an installation class that extends Installer and overrides afterInstall, but I'm getting a null pointer exception. How can I go about debugging my class?
14 Answers
Something that is handy for hard to debug sections of code is
System.Diagnostics.Debugger.Break()
Will throw a breakpoint caught by any installed debugger (VStudio, WinDbg, Remote debugger etc...).
Use it to debug really tricky areas where regular F5+Go or "Attach to Process" is difficult or impossible to perform, some examples include:
- short-lived processes
- time-sensitive processes
- breaking into spawned sub-processes
- installers
- service stop/start
- distributed systems
3 Comments
The best way I've found is to write a unit test, and new up and initialize your installer class from your unit test:
[TestClass] public class InstallerTest {
[TestMethod]
public void InstallTest() {
// substitute with your installer component here
DataWarehouseInstall installer = new DataWarehouseInstall();
string assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string installLogFilePath = Path.Combine(assemblyDirectory, "install.log");
installer.Context = new System.Configuration.Install.InstallContext(installLogFilePath, null);
// Refactor to set any parameters for your installer here
installer.Context.Parameters.Add("Server", ".");
//installer.Context.Parameters.Add("User", "");
//installer.Context.Parameters.Add("Password", "");
installer.Context.Parameters.Add("DatabaseName", "MyDatabaseInstallMsiTest");
//installer.Context.Parameters.Add("DatabasePath", "");
// Our test isn't injecting any save state so we give a default instance for the stateSaver
installer.Install(new Hashtable());
} }
At least then it takes advantage of the IDE tooling better. This is especially helpful for very large installers with LOTS of components. Then you can also create ordered unit tests and run them in sequence to mimic your installer during debug or your automated builds.
Another tip would be general SOLID/GRASS software principles...develop in neat/thin layers, keeping your actual "custom action" installer logic very simple and instead call into any reusable API stuff you have that is specific to your installer(s), just as we are used to with UI development. (The installer is just another UI anyway.) This is especially key if your goal is to have a certain UI experience shared across all installers of your products.
Comments
Surprised no one has actually answered. Put a MessageBox.Show("hello") into your custom action's Install() member. Build the deployment in debug config. Install. When the MessageBox appears, go into VS IDE, Debug, Attach Process and look for the instance of msiexec that is labeled "Managed". Attach the debugger to that instance of msiexec. Now go back to the source of your custom action and place a breakpoint right after the call to MessageBox.Show(). Close the MessageBox and your breakpoint will be hit, and you're debugging in the IDE!
2 Comments
Show All Processes checkbox is checked. In addition, one could GetCurrentProcess and positively identify the correct process with the Id. :)System.Diagnostics.Debugger.Break()? You have to add WindowsForms dll in order to use MessageBox. Also, for CustomInstallers, the process name will be InstallUtil.attach the installer process to Visual studio in Debug->Processes->Attach or CTRL + ALT + P set the breakpoint and you should be able to go
1 Comment
In your installer method add Debugger.Launch() statement which will launch "Visual Studio just in time debugger" where you can attach an instance of visual studio and debug your installer class (MSI). This should work in Visual Studio 2010 as well. But you need to have administrative rights to do this. If you don't have administrative rights, you might have issues. So, log in as administrator for debugging MSI. For example:
public override void Install(System.Collections.IDictionary stateSaver)
{
Debugger.Launch();
base.Install(stateSaver);
}
In visual studio 2005, even Debugger.Break() use to work but somehow this does not work with Visual Studio 2010.
1 Comment
I use EventLog.WriteEntry("source", "message"), and check the EventLog when installing. Maybe not optimal, but works for me :)
1 Comment
I use the following class to write a simple log into the target directory. In my opinion, it's easier than trying to use the Visual Studio debugger.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace MyCompany.Deployment
{
/// <summary>
/// Enables a quick and easy method of debugging custom actions.
/// </summary>
class LogFile
{
const string FileName = "MyCompany.Deployment.log";
readonly string _filePath;
public LogFile(string primaryOutputPath)
{
var dir = Path.GetDirectoryName(primaryOutputPath);
_filePath = Path.Combine(dir, FileName);
}
public void Print(Exception ex)
{
File.AppendAllText(_filePath, "Error: " + ex.Message + Environment.NewLine +
"Stack Trace: " + Environment.NewLine + ex.StackTrace + Environment.NewLine);
}
public void Print(string format, params object[] args)
{
var text = String.Format(format, args) + Environment.NewLine;
File.AppendAllText(_filePath, text);
}
public void PrintLine() { Print(""); }
}
}
Comments
Write the following code in the beginning of the method that you want to debug
#if DEBUG
MessageBox.Show(Process.GetCurrentProcess().Id.ToString());
#endif
So when your method is called, the above code will be hit and you can then attach the debugger to the process(ctrl+alt+p) using the above process ID. You may have to start VS with elevated permissions.
Comments
build a VM, install Visual studio, make a copy of it (or create a differencing Virtual HDD) and run the installer under the debugger under the VM.
That is what I would do (but I'm no expert).
1 Comment
You can also use the installUtil.exe utility to test your installer component.
In case you created a c# class assembly with your Installer class, Change your debug settings to start the external program 'C:\Windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe' and enter your commandline arguments accordingly (e.g. /Args=myargument "path to the assembly")
As last set your breakpoints, press f5 and you're set to debug your code. --paralax
Comments
You might automate debugging of installer projects by adding following section to either .csproj or .csproj.user file:
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<StartAction>Program</StartAction>
<StartProgram>$(MSBuildBinPath)\installutil.exe</StartProgram>
<StartArguments>$(AssemblyName).dll</StartArguments>
</PropertyGroup>
Use project file if you want other developers benefit from this change and .user file if you want to use it by yourself.