none
NullReferenceException on Uninstalling a service NOT via InstallUtil.exe

    Question

  • Hi,

    I've written a service that should be installed. So I created an Installer class (automaticly via VS2008). When I am installing and uninstalling the service via InstallUtil.exe (from framework) then everything is ok and works fine. but when I am going to call the Installer directly then everything works fine with install. But when I call Uninstall, then I get a NullReferenceException. Here is the Exception Info:

    System.NullReferenceException wurde nicht behandelt.
      Message="Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."
      Source="System.Configuration.Install"
      StackTrace:
           bei System.Configuration.Install.Installer.Uninstall(IDictionary savedState)
           bei Com.Os.FileTrigger.TriggerServiceInstaller.Uninstall(IDictionary savedState) in E:\VSS\As600\Tools\axFileTrigger\Service\Install\TriggerServiceInstaller.cs:Zeile 62.
           bei Com.Os.FileTrigger.Commands.UninstallServiceCommand.Execute() in E:\VSS\As600\Tools\axFileTrigger\Commands\UninstallServiceCommand.cs:Zeile 24.
           bei Com.Os.FileTrigger.Commands.AbstractCommand.ExecuteCommand() in E:\VSS\As600\Tools\axFileTrigger\Commands\AbstractCommand.cs:Zeile 22.
           bei Com.Os.FileTrigger.Program.Main(String[] args) in E:\VSS\As600\Tools\axFileTrigger\Program.cs:Zeile 97.
           bei System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
           bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
           bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
           bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
           bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
           bei System.Threading.ThreadHelper.ThreadStart()
      InnerException:

    The Code looks like the following:

    This is my Installer

    1    [RunInstaller(true)] 
    2    public partial class TriggerServiceInstaller : Installer 
    3    { 
    4        public TriggerServiceInstaller() 
    5        { 
    6            InitializeComponent(); 
    7            this.serviceInstaller.ServiceName = ServiceSettings.SERVICE_INTERNAL_NAME; 
    8            this.serviceInstaller.DisplayName = ServiceSettings.SERVICE_NAME; 
    9            this.serviceInstaller.Description = ServiceSettings.SERVICE_DESCRIPTION; 
    10        } 
    11 
    12        public override void Install(IDictionary stateSaver) 
    13        { 
    14            ServiceInstallForm installSetupDialog = new ServiceInstallForm(); 
    15            if (installSetupDialog.ShowDialog() == DialogResult.OK) 
    16            { 
    17                this.serviceInstaller.StartType = installSetupDialog.StartMode; 
    18                this.serviceProcessInstaller.Account = installSetupDialog.Account; 
    19                this.serviceProcessInstaller.Password = installSetupDialog.Password; 
    20                this.serviceProcessInstaller.Username = installSetupDialog.User; 
    21                base.Install(stateSaver); 
    22            } 
    23        } 
    24 
    25        public override void Uninstall(IDictionary savedState) 
    26        { 
    27            ServiceInstallForm installSetupDialog = new ServiceInstallForm(); 
    28            if (installSetupDialog.ShowDialog() == DialogResult.OK) 
    29            { 
    30                this.serviceInstaller.StartType = installSetupDialog.StartMode; 
    31                this.serviceProcessInstaller.Account = installSetupDialog.Account; 
    32                this.serviceProcessInstaller.Password = installSetupDialog.Password; 
    33                this.serviceProcessInstaller.Username = installSetupDialog.User; 
    34                base.Uninstall(savedState); 
    35            } 
    36        } 
    37    } 

    This is the code-behind of the Installer:

    1    partial class TriggerServiceInstaller 
    2    { 
    3        private ServiceProcessInstaller serviceProcessInstaller; 
    4        private ServiceInstaller serviceInstaller; 
    5 
    6        private IContainer components = null
    7 
    8        protected override void Dispose(bool disposing) 
    9        { ... } 
    10 
    11        private void InitializeComponent() 
    12        { 
    13            this.serviceProcessInstaller = new ServiceProcessInstaller(); 
    14            this.serviceInstaller = new ServiceInstaller(); 
    15            this.serviceProcessInstaller.Password = null
    16            this.serviceProcessInstaller.Username = null
    17            this.Installers.AddRange(new Installer[] { this.serviceProcessInstaller, this.serviceInstaller }); 
    18        } 
    19    } 

    Here my Application where I create the (un)Installer object and call the uninstall:

    1    public class UninstallServiceCommand : AbstractCommand 
    2    { 
    3        protected override void Execute() 
    4        { 
    5            TriggerServiceInstaller installer = new TriggerServiceInstaller(); 
    6            installer.Context = new InstallContext(); 
    7            installer.Context.Parameters.Add("assemblypath", Assembly.GetExecutingAssembly().Location); 
    8            installer.Uninstall(new Hashtable()); 
    9        } 
    10 
    11        protected override string CommandName 
    12        { 
    13            get { return "UninstallServiceCommand"; } 
    14        } 
    15    } 

    I am sorry for my bad english but maybe someone out there can help me.

    Thanks a lot!

    BIR
    Friday, December 12, 2008 2:40 PM

Answers

  • I'm not sure if it is causing your problems but I see a couple of things about your code that I don't like.

    1) Your uninstall code is bringing up a UI which is probably not a good idea.  Remove it.
    2) Your uninstall command creates an instance of your installer and then a context and uninstalls.  This is not how it is normally done.  You should create an instance of the TransactedInstaller class instead.  This ensures that either everything works or it doesn't.  Refer to the documentation for the class for an example of how InstallUtils works (simplified).  You should then add your custom installer into the transacted installer's child list.

    If none of that helps then try debugging your code.  You can load the .NET symbols and debug into the installer to see where the actual problem lies.

    Michael Taylor - 12/12/08
    http://p3net.mvps.org
    • Marked as answer by Birke Monday, December 15, 2008 12:08 PM
    Friday, December 12, 2008 6:22 PM
  • Thank you for your answer. At first ... I solved the problem.

    The problem was, that I passed an hashtable in the Unistaller method. The correct way to call this method is:

    1installer.Uninstall(null); 

    Totaly no idea why the framework can't handle an empty hashtable. I got this information from the code example in the msdn article MSDN article TransatedInstaller.

    PS: I need the UI element in the Installer in order to tell my installer which kind of service and so on to install. therefore i want to show a self-defined GUI. Do you have any suggestions how to do this in a better style?

    Thanks a lot for the help!

    BIR
    • Marked as answer by Birke Monday, December 15, 2008 12:08 PM
    Monday, December 15, 2008 12:07 PM

All replies

  • I'm not sure if it is causing your problems but I see a couple of things about your code that I don't like.

    1) Your uninstall code is bringing up a UI which is probably not a good idea.  Remove it.
    2) Your uninstall command creates an instance of your installer and then a context and uninstalls.  This is not how it is normally done.  You should create an instance of the TransactedInstaller class instead.  This ensures that either everything works or it doesn't.  Refer to the documentation for the class for an example of how InstallUtils works (simplified).  You should then add your custom installer into the transacted installer's child list.

    If none of that helps then try debugging your code.  You can load the .NET symbols and debug into the installer to see where the actual problem lies.

    Michael Taylor - 12/12/08
    http://p3net.mvps.org
    • Marked as answer by Birke Monday, December 15, 2008 12:08 PM
    Friday, December 12, 2008 6:22 PM
  • Thank you for your answer. At first ... I solved the problem.

    The problem was, that I passed an hashtable in the Unistaller method. The correct way to call this method is:

    1installer.Uninstall(null); 

    Totaly no idea why the framework can't handle an empty hashtable. I got this information from the code example in the msdn article MSDN article TransatedInstaller.

    PS: I need the UI element in the Installer in order to tell my installer which kind of service and so on to install. therefore i want to show a self-defined GUI. Do you have any suggestions how to do this in a better style?

    Thanks a lot for the help!

    BIR
    • Marked as answer by Birke Monday, December 15, 2008 12:08 PM
    Monday, December 15, 2008 12:07 PM
  • The MSDN sample passes null to the installer during uninstall.  I would agree that it shouldn't cause an exception however.  I dug into the source and I see why it throws the exception but IMHO it shouldn't.  The problem is that in the base class it checks to see if the parameter is null.  If it isn't then it assumes that there is a nested state object in the dictionary and tries to look it up.  If it fails then an exception occurs.  This is bad design.  The framework is exposing a public method that must be called with a parameter and yet it doesn't allow anything other than null to be passed via the public method.  Understandably it might need to be able to pass this information internally.  In this case the framework probably should have exposed a public method with no parameters and a protected, virtual method with the single parameter.  As it stands now the public method is just bad. 

    I am going to add a comment to the MSDN documentation for Installer.Uninstall to mention the fact that the saved state must either contain the system-defined data or be null to avoid an exception.  This will hopefully allow others to not make the same mistake.

    As for your UI I didn't say that you shouldn't show a UI but that during uninstall it doesn't make much sense, in general.  During installation a UI is reasonable.  In fact the default service installer will display a UI to get the UN/PWD of the service account if you use the User account type.  If you really need information during uninstall (perhaps for a multi-instanced service) then it would make sense but otherwise it seems redundant.

    Michael Taylor - 12/15/08
    http://p3net.mvps.org

    Monday, December 15, 2008 2:44 PM