Uninstalling a ClickOnce application silently
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, processorArchite开发者_开发问答cture=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.
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.
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.
精彩评论