Question

We have an production application that is deployed using Visual Studio's built-in ClickOnce deployment tool. I am writing a batch file to uninstall the application:

rundll32.exe dfshim.dll,ShArpMaintain AppName.application, Culture=neutral,
PublicKeyToken=XXXXXX, processorArchitecture=x86

The batch file works and the application's uninstall is called. However, I'm looking to do this silently. I have tried /Q /q /S /s /Silent but with no joy.

How can I do this?


I do not want to hide the batch file window. Only the ClickOnce window.

Was it helpful?

Solution

Since there seemed to be no good solution for this, I implemented a new ClickOnce uninstaller. It can be called via command-line, from .NET, or integrated into a WiX setup project as custom action.

https://github.com/6wunderkinder/Wunder.ClickOnceUninstaller

We used this for our Wunderlist 2.1 release, where we switched from ClickOnce to a Windows Installer package. It's integrated into the installation process and completely transparent to the user.

OTHER TIPS

I can confirm that WMIC doesn't work for ClickOnce applications. They are just not listed in there...

I thought of putting this here as I've been working on finding how to resolve this for a long time now and couldn't find a complete solution.

I'm rather new in this whole programming thing but I think this could give ideas of how to proceed.

It basically verifies if the application is currently running, and if so, it kills it. Then it checks the registry to find the uninstall string, put it in a batch file and wait for the process to end. Then it does Sendkeys to automatically agree to uninstall. That's it.

namespace MyNameSpace
{
    public class uninstallclickonce
    {
        [System.Runtime.InteropServices.DllImport("user32.dll")]

        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [System.Runtime.InteropServices.DllImport("user32.dll")]

        private static extern bool SetForegroundWindow(IntPtr hWnd);

        private Process process;
        private ProcessStartInfo startInfo;

        public void isAppRunning()
        {
            // Run the below command in CMD to find the name of the process
            // in the text file.
            //
            //     WMIC /OUTPUT:C:\ProcessList.txt PROCESS get Caption,Commandline,Processid
            //
            // Change the name of the process to kill
            string processNameToKill = "Auto-Crop"; 

            Process [] runningProcesses = Process.GetProcesses();

            foreach (Process myProcess in runningProcesses)
            {
                // Check if given process name is running
                if (myProcess.ProcessName == processNameToKill)
                {
                    killAppRunning(myProcess);
                }
            }
        }

        private void killAppRunning(Process myProcess)
        {
            // Ask the user if he wants to kill the process
            // now or cancel the installation altogether
            DialogResult killMsgBox =
                MessageBox.Show(
                    "Crop-Me for OCA must not be running in order to get the new version\nIf you are ready to close the app, click OK.\nClick Cancel to abort the installation.",
                    "Crop-Me Still Running",
                    MessageBoxButtons.OKCancel,
                    MessageBoxIcon.Question);

            switch(killMsgBox)
            {
                case DialogResult.OK:
                    //Kill the process
                    myProcess.Kill();
                    findRegistryClickOnce();
                    break;
                case DialogResult.Cancel:
                    //Cancel whole installation
                    break;
            }
        }

        private void findRegistryClickOnce()
        {
            string uninstallRegString = null; // Will be ClickOnce Uninstall String
            string valueToFind = "Crop Me for OCA"; // Name of the application we want
                                                    // to uninstall (found in registry)
            string keyNameToFind = "DisplayName"; // Name of the Value in registry
            string uninstallValueName = "UninstallString"; // Name of the uninstall string

            //Registry location where we find all installed ClickOnce applications
            string regProgsLocation = 
                "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";

            using (RegistryKey baseLocRegKey = Registry.CurrentUser.OpenSubKey(regProgsLocation))
            {
                //Console.WriteLine("There are {0} subkeys in here", baseLocRegKey.SubKeyCount.ToString());

                foreach (string subkeyfirstlevel in baseLocRegKey.GetSubKeyNames())
                {
                   //Can be used to see what you find in registry
                   // Console.WriteLine("{0,-8}: {1}", subkeyfirstlevel, baseLocRegKey.GetValueNames());

                    try
                    {
                        string subtest = baseLocRegKey.ToString() + "\\" + subkeyfirstlevel.ToString();

                        using (RegistryKey cropMeLocRegKey =
                                 Registry.CurrentUser.OpenSubKey(regProgsLocation + "\\" + subkeyfirstlevel))
                        {
                            //Can be used to see what you find in registry
                            //  Console.WriteLine("Subkey DisplayName: " + cropMeLocRegKey.GetValueNames());

                            //For each
                            foreach (string subkeysecondlevel in cropMeLocRegKey.GetValueNames())
                            {
                                // If the Value Name equals the name application to uninstall
                                if (cropMeLocRegKey.GetValue(keyNameToFind).ToString() == valueToFind)
                                {
                                    uninstallRegString = cropMeLocRegKey.GetValue(uninstallValueName).ToString();

                                    //Exit Foreach
                                    break;
                                }
                            }
                        }
                    }
                    catch (System.Security.SecurityException)
                    {
                        MessageBox.Show("security exception?");
                    }
                }
            }
            if (uninstallRegString != null)
            {
                batFileCreateStartProcess(uninstallRegString);
            }
        }

        // Creates batch file to run the uninstall from
        private void batFileCreateStartProcess(string uninstallRegstring)
        {
            //Batch file name, which will be created in Window's temps foler
            string tempPathfile = Path.GetTempPath() + "cropmeuninstall.bat";

            if (!File.Exists(@tempPathfile))
            {
                using (FileStream createfile = File.Create(@tempPathfile))
                {
                    createfile.Close();
                }
            }

            using (StreamWriter writefile = new StreamWriter(@tempPathfile))
            {
                //Writes our uninstall value found earlier in batch file
                writefile.WriteLine(@"Start " + uninstallRegstring);
            }

            process = new Process();
            startInfo = new ProcessStartInfo();

            startInfo.FileName = tempPathfile;
            process.StartInfo = startInfo;
            process.Start();
            process.WaitForExit();

            File.Delete(tempPathfile); //Deletes the file

            removeClickOnceAuto();
        }

        // Automation of clicks in the uninstall to remove the
        // need of any user interactions
        private void removeClickOnceAuto()
        {
            IntPtr myWindowHandle = IntPtr.Zero;

            for (int i = 0; i < 60 && myWindowHandle == IntPtr.Zero; i++)
            {
                Thread.Sleep(1500);

                myWindowHandle = FindWindow(null, "Crop Me for OCA Maintenance");
            }

            if (myWindowHandle != IntPtr.Zero)
            {
                SetForegroundWindow(myWindowHandle);

                SendKeys.Send("+{TAB}"); // Shift + TAB
                SendKeys.Send("{ENTER}");
                SendKeys.Flush();
            }
        }
    }
}

You could try to use Hidden Start.

You can not suppress the uninstall dialog for a ClickOnce application. You can write a small .NET application to uninstall the ClickOnce application and programmatically hit the button on the dialog, so no action is required by the user. That's about the best you can do.

Don't over complicate it & keep it simple - this works on both Windows XP & 7:

Go to Add/Remove Programs and make note of the exact name of the program. Open Notepad and paste the text below:

wmic product where name="PROGRAM NAME" uninstall

but make sure to type the exact name of the program in between the quotation marks and chose Save As /All Files and name the file Uninstall.bat and then test it out to make sure it works.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top