【问题标题】:Uninstalling a ClickOnce application silently静默卸载 ClickOnce 应用程序
【发布时间】:2011-06-26 21:59:01
【问题描述】:

我们有一个使用 Visual Studio 的内置 ClickOnce 部署工具部署的生产应用程序。我正在编写一个批处理文件来卸载应用程序:

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

批处理文件运行,应用程序的卸载被调用。但是,我希望默默地做到这一点。我试过/Q /q /S /s /Silent,但没有任何乐趣。

我该怎么做?


我确实想要隐藏批处理文件窗口。只有 ClickOnce 窗口。

【问题讨论】:

  • 您的意思是静默“不显示命令窗口”?并且批处理也是从clickonce部署中调用的?
  • @rene 批处理文件调用卸载,然后需要用户交互,这是我不想要的。我希望卸载保持静默

标签: batch-file clickonce uninstallation


【解决方案1】:

由于似乎没有好的解决方案,我实现了一个新的 ClickOnce 卸载程序。它可以通过命令行、.NET 调用,或者作为自定义操作集成到 WiX 设置项目中。

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

我们将它用于我们的 Wunderlist 2.1 版本,我们从 ClickOnce 切换到 Windows Installer 包。它已集成到安装过程中,对用户完全透明。

【讨论】:

  • Christian,我喜欢这个卸载程序!这是我的问题...我们的应用程序在名称中安装了版本号,即:“AppName v4.3.2.1”...有没有办法说明所有版本号?即:“AppName v%”?我正在查看 ClickOnceUninstaller.UninstallInfo 文件,但我只看到 == 比较...我只是将其更改为某种“包含”功能?
  • @Christian - 出色的工作。正是我在想我将不得不自己实施并且很高兴找到相反的东西。你打算用这个制作一个nuget包吗?会让有需要的人的生活更轻松。如果没有,您介意我使用您的代码并自己制作一个吗?
  • 这太棒了!谢谢!
  • 这个优秀的库帮助我克服了在 Citrix XenApp 上运行 ClickOnce 程序的主要障碍(Citrix 抑制了一些关键的弹出窗口,导致头痛)。非常感谢。
  • 太棒了,谢谢!但是我注意到它依赖于应用程序的公钥令牌,而某些应用程序没有公钥令牌(例如 NuGet 包资源管理器,它的 PK 令牌为 0000000000000000)
【解决方案2】:

我可以确认 WMIC 不适用于 ClickOnce 应用程序。它们只是没有在其中列出...

我想把这个放在这里,因为我一直在努力寻找解决这个问题的方法很长时间,但找不到完整的解决方案。

我在整个编程方面还是个新手,但我认为这可以提供有关如何进行的想法。

它基本上验证应用程序当前是否正在运行,如果是,则将其终止。然后它检查注册表以找到卸载字符串,将其放入批处理文件并等待该过程结束。然后它会使用 Sendkeys 自动同意卸载。就是这样。

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();
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    您可以尝试使用Hidden Start

    【讨论】:

    • Nope 不起作用,因为我无法安装其他应用程序或使用其他 dll。只是我的批处理文件:(
    • @Josefvz,我明白了。实现此目的的一种方法是从受信任的发布者处安装 ClickOnce 应用程序。然后卸载对用户来说是非交互的。但是,在这种情况下,这种方法可能不值得。
    【解决方案4】:

    您不能禁止 ClickOnce 应用程序的卸载对话框。您可以编写一个小的 .NET 应用程序来卸载 ClickOnce 应用程序并以编程方式点击对话框上的按钮,因此用户无需执行任何操作。这是你能做的最好的事情。

    【讨论】:

      【解决方案5】:

      不要过度复杂化并保持简单 - 这适用于Windows XP &amp; 7

      转到Add/Remove Programs 并记下程序的确切名称。 打开Notepad并粘贴以下文本:

      wmic product where name="PROGRAM NAME" 卸载

      但请确保在引号之间键入程序的确切名称并选择Save As /All Files 并将文件命名为Uninstall.bat,然后对其进行测试以确保其正常工作。

      【讨论】:

      • 我认为您的建议仅适用于通过 MSI 安装的应用程序,不适用于 ClickOnce。
      猜你喜欢
      • 2011-09-11
      • 2017-09-06
      • 1970-01-01
      • 1970-01-01
      • 2012-06-09
      • 2013-08-31
      • 2023-04-09
      • 2011-02-09
      • 1970-01-01
      相关资源
      最近更新 更多