在Visual Studio的安装项目中,如何生成卸载脚本?

28

我有一个Visual Studio安装项目。 安装后,它会在应用程序文件夹中创建一个卸载批处理文件。 如果用户想要卸载产品,他可以转到“添加/删除程序”,或者只需双击uninstall.cmd即可。 其内容如下:

%windir%\system32\msiexec /x {CC3EB7BF-DD82-48B9-8EC5-1B0B62B6D285}

那里的GUID是来自Visual Studio安装项目中的ProductCode。

ProductCode

但是,为了使升级正常工作,我必须每次生成新的MSI时递增版本号。如果我递增版本号,那么还必须为ProductCode生成一个新的Guid。这意味着静态uninstall.cmd文件需要更改。

如何在构建时动态生成包含ProductCode的批处理文件?

8个回答

67
我在这里找到了解决方案(链接)

“使用Visual Studio 2005/2008,您无需编写任何代码就可以为安装程序项目添加卸载选项(是的,我知道有些人可以编写代码来实现它)

1)在Setup项目-->文件系统窗口中,右键单击“目标计算机上的文件系统”,然后添加一个特殊文件夹,选择系统文件夹;

2)在该系统文件夹中添加一个文件。浏览本地System32文件夹中的msiexec.exe并添加它。将此文件的默认属性覆盖如下:

Condition:= Not Installed (确保您完全按照此方式将“Not Installed”输入,大小写和所有内容都相同), Permanent:= True, System:= True, Transitive:= True, Vital:= False。

3)在“用户程序菜单”下创建一个新的快捷方式,将目标设置为您在步骤1中创建的系统文件夹,并指向msiexec.exe。将快捷方式重命名为“卸载您的应用程序”。将Arguments属性设置为/x{空格}[ProductCode]。

4)构建项目,忽略有关应该排除msiexec的警告,不要排除它,否则安装程序项目将无法构建。

“Not Installed”条件和Permanent:= True确保只有在安装时msiexec.exe不存在时才将其放置到系统文件夹中,并且在卸载时不会删除它-因此可以忽略该警告并继续进行操作。

(基于SlapHead的说明)”


1
现在这非常方便,比我采用的方法更加直接。 - Cheeso
8
很棒,在WinXP/Vista和Win7上都能很好地运行。值得注意的是,根据链接的文章,如果你使用比目标平台更高版本的msiexec.exe会有问题。这对我来说不是问题,因为我仍然故意使用XP进行构建 - 但显然,如果你想在Vista上构建一个安装程序并在XP上运行,那将会成为一个问题。 - Iain Collins
太棒了!正是我想要的内容。 - Martin
3
在Vista/7下构建安装程序时可能会出现问题,安装程序试图覆盖旧的XP机器上的msiexec。这可以通过创建一个空的.txt文件并将其重命名为"msiexec.exe"来进行更正。在安装程序中包含该文件,而不是实际的System32版本。由于空的假文件没有版本信息,它不会尝试覆盖XP的msiexec。 - BTownTKD
这是一个好主意。但我有一个问题。当我点击卸载快捷方式时,它会抛出错误:“无法打开此安装程序包。请验证该程序包是否存在并且您可以访问它,或联系应用程序供应商以验证这是有效的 Windows Installer 程序包。”为什么会出现这个错误?我知道已经过了几年,但现在我正在使用它... - NickName
2
@NickName 我也遇到了同样的错误。将Arguments属性设置为/x{space}[ProductCode]对我来说不起作用,但是当我将Arguments属性更改为/x [ProductCode]时,它就可以工作了。我猜测Visual Studio无法识别{space}的含义。顺便说一下,我在VS2005中尝试过。 - Rhain

10

我刚刚让这个正常工作:

将一个uninstall.bat文件添加到你的项目中,文件内容为:

msiexec.exe /x %1

创建该批处理文件的快捷方式(例如在“用户程序”菜单中),在快捷方式属性旁边的参数中指定:[ProductCode]


1
你可以使用 start msiexec.exe /x %1 来消除黑色窗口;但是黑色窗口仍然会闪烁。 - moala
可能是 start /B msiexec.exe /x %1 - MPelletier
尝试使用/min,它应该至少可以将其最小化。 - MPelletier
4
不,闪烁的窗口不是起始命令的窗口,而是.bat文件的窗口,因此添加其他开关并非解决方案;解决方案在于vdproj安装项目:将从值vsdscNormal值调用的快捷方式的ShowCmd属性从值更改为vsdscMinimized。问题已解决。 - moala

4
这是我编写的脚本,用于创建一个 uninstall.cmd 文件。它作为安装过程中的自定义操作运行。
var fso, ts;
var ForWriting= 2;
fso = new ActiveXObject("Scripting.FileSystemObject");

var parameters = Session.Property("CustomActionData").split("|"); 
var targetDir = parameters[0];
var productCode = parameters[1];

ts = fso.OpenTextFile(targetDir + "uninstall.cmd", ForWriting, true);

ts.WriteLine("@echo off");
ts.WriteLine("goto START");
ts.WriteLine("=======================================================");
ts.WriteBlankLines(1);
ts.WriteLine(" Uninstall.cmd");
ts.WriteBlankLines(1);
ts.WriteLine("=======================================================");
ts.WriteBlankLines(1);
ts.WriteLine(":START");
ts.WriteLine("@REM The uuid is the 'ProductCode' in the Visual Studio setup project");
ts.WriteLine("%windir%\\system32\\msiexec /x " + productCode);
ts.WriteBlankLines(1);
ts.Close();

结果是一个cmd文件,其中始终包含当前的ProductCode。
缺点是“创建uninstall.cmd脚本”仍然在安装目录中。虽然不是大问题,但我不喜欢安装目录里有垃圾文件。我还没有尝试过让“createInstaller.js”自删除,也许可以尝试一下。
编辑:是的,使createInstaller.js自删除工作得很好。
我会采纳我的回答!

1
很好的解决方案,但是有点hacky,我在想微软会怎么做。之前我试过的安装程序(比如inno)都会自动创建卸载文件。 - cnd
1
我知道已经过了几年,但我想知道你是如何使用Cheeso的。你是否覆盖了OnAfterInstall?如果是这样,你是如何在那里插入脚本的? - user153923

2

批处理文件方法的一个小变化。如果您不想显示命令窗口,请使用wscript文件。与之前一样,在参数中提供[ProductCode]。

<job>
<?job debug="true" error="true" ?>
<script language="JScript">
    var arg = [];
    for (var i=0; i<WSH.Arguments.length; i++) {
        arg.push( WSH.Arguments.Item(i) );
    }

    if (arg.length>0) {
        var productcode = arg[0];
        var v = new ActiveXObject("Shell.Application");
        v.ShellExecute("msiexec.exe", "/x "+productcode, "", "open", 10);
    }

    WSH.Quit(0);
</script>
</job>

1
我结合了被接受的答案和Locksmith的答案,以避免看到任何命令提示符的闪烁或命令提示符本身。这样做的一个优点是你不必创建快捷方式并设置其参数才能让它工作。
我的createUninstaller.js文件:
var fso, ts;
var ForWriting= 2;
fso = new ActiveXObject("Scripting.FileSystemObject");

var parameters = Session.Property("CustomActionData").split("|"); 
var targetDir = parameters[0];
var productCode = parameters[1];

ts = fso.OpenTextFile(targetDir + "Uninstall.js", ForWriting, true);

ts.WriteLine("var v = new ActiveXObject(\"Shell.Application\");");
ts.WriteLine("v.ShellExecute(\"msiexec.exe\", \"/x "+productCode+"\", \"\", \"open\",10);");
ts.Close();

这个文件被添加为提交操作“directory”的自定义操作。为了实际获得这些自定义操作: 右键单击您的安装程序项目>查看>自定义操作>右键单击提交“directory”>添加自定义操作。之后,您必须搜索您创建的createUninstaller.js文件并添加它。
现在要使createUninstaller.js读取变量targetDir和productCode,您必须
右键单击设置项目自定义操作'commit'目录中的createUninstaller.js文件,然后转到属性窗口。 在属性中,您将看到“CustomActionData”属性。在那里,您只需复制粘贴[TARGETDIR] | [ProductCode]
然后就完成了!它现在应该添加Uninstall.js文件,只需双击即可运行。

1

1
我知道这个问题已经有了答案,但是我所做的是创建一个批处理文件,其内容如下:
start msiexec.exe /x %1

使用“start”命令可以在卸载过程中防止命令提示符保持打开状态,“%1”将被替换为执行批处理文件时传递的第一个参数。我将批处理文件添加到应用程序文件夹,并创建了一个指向批处理文件的菜单快捷方式。如果您右键单击快捷方式并选择属性窗口,在其中将以下文本添加到参数属性中:
[ProductCode]

构建和运行安装程序后,您将拥有一个开始菜单快捷方式,调用批处理文件并传递产品代码,就像在构建该应用程序版本时一样。然后,用户将看到提示,询问是否应卸载该产品。
这种解决方案的唯一缺点是,如果用户浏览到应用程序文件夹并双击批处理文件,则卸载程序将显示一个错误,指出安装包不存在。

0
每次生成安装程序时,请始终更改产品代码。创建卸载程序快捷方式,您将找到命令行参数传递技术来处理快捷方式。在那里,您将始终编写“/u 产品代码”产品代码始终需要在此处编写。
将此代码放入program.cs文件的主要方法中。
[STAThread]
        static void Main()
        {
            bool flag = true;
            string[] arguements = Environment.GetCommandLineArgs();
            foreach (string str in arguements)
            {
                if ((str.Split('='))[0].ToLower() == "/u")
                {
                    if (MessageBox.Show("Do you want to uninstall job board", "Are you Sure?", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        flag = false;
                        RemoveRegSettings();
                        RemoveIniFile();
                        string guid = str.Split('=')[1];
                        string path = Environment.GetFolderPath(Environment.SpecialFolder.System);
                        ProcessStartInfo si = new ProcessStartInfo(path + @"\msiexec.exe", "/i" + guid);
                        Process.Start(si);
                        Application.Exit();
                        return;
                    }
                }
            }
            //

            //************************************************
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接