Qt Installer Framework 不覆盖现有安装的解决方法
Workaround for Qt Installer Framework not overwriting existing installation
这个问题是关于 Qt Installer Framework 2.0 版的。
此时,使用 Qt Installer Framework 的人都知道,如果不进行自定义,您根本无法通过安装程序覆盖现有安装。这显然是为了解决在使用 Qt 框架时出现的一些问题。
但是,对于较小的、相对简单的项目,覆盖非常好,而且比必须事先手动 运行 维护工具方便得多。
我正在寻找一个涉及自定义 UI + 组件脚本的解决方案,该脚本将一个按钮添加到目标目录页面,允许用户
- 删除指定的目录,如果存在,或者
- 运行那个目录下的维护工具
最好能够运行目标目录中的维护工具,让它自动删除给定的包,但是我意识到那是要求有点高了。
我已阅读此站点上有关解决同一问题的其他问题的答案,但 none 的解决方案工作正常。我还想提一下,我有一个组件脚本 运行ning,但没有自定义 UIs.
您需要做的事情很少:
要通过添加此代码可以实现的 TargetDirectoryPage
installer.setValue("RemoveTargetDir", false)
允许您运行此代码的自定义UI(或消息框)。此 UI 应插入到 TargetDirectoryPage 之后。
// you need to append .exe on the maintenance for windows installation
installer.execute(installer.findPath(installer.value("MaintenanceToolName"), installer.value("TargetDir")));
我终于找到了可行的解决方案。
你需要三件事来完成这个:
- 组件脚本,
- 目标目录页面的自定义UI,以及
- 自动点击卸载程序的控制器脚本。
我现在将逐字列出对我有用的东西(以及我的项目特定的东西)。我的组件名为 Atlas4500 Tuner
config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Installer>
<Name>Atlas4500 Tuner</Name>
<Version>1.0.0</Version>
<Title>Atlas4500 Tuner Installer</Title>
<Publisher>EF Johnson Technologies</Publisher>
<StartMenuDir>EF Johnson</StartMenuDir>
<TargetDir>C:\Program Files (x86)\EF Johnson\Atlas4500 Tuner</TargetDir>
</Installer>
packages/Atlas4500 Tuner/meta/package.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>Atlas4500Tuner</DisplayName>
<Description>Install the Atlas4500 Tuner</Description>
<Version>1.0.0</Version>
<ReleaseDate></ReleaseDate>
<Default>true</Default>
<Required>true</Required>
<Script>installscript.qs</Script>
<UserInterfaces>
<UserInterface>targetwidget.ui</UserInterface>
</UserInterfaces>
</Package>
自定义组件脚本packages/Atlas4500Tuner/meta/installscript.qs:
var targetDirectoryPage = null;
function Component()
{
installer.gainAdminRights();
component.loaded.connect(this, this.installerLoaded);
}
Component.prototype.createOperations = function()
{
// Add the desktop and start menu shortcuts.
component.createOperations();
component.addOperation("CreateShortcut",
"@TargetDir@/Atlas4500Tuner.exe",
"@DesktopDir@/Atlas4500 Tuner.lnk",
"workingDirectory=@TargetDir@");
component.addOperation("CreateShortcut",
"@TargetDir@/Atlas4500Tuner.exe",
"@StartMenuDir@/Atlas4500 Tuner.lnk",
"workingDirectory=@TargetDir@");
}
Component.prototype.installerLoaded = function()
{
installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
installer.addWizardPage(component, "TargetWidget", QInstaller.TargetDirectory);
targetDirectoryPage = gui.pageWidgetByObjectName("DynamicTargetWidget");
targetDirectoryPage.windowTitle = "Choose Installation Directory";
targetDirectoryPage.description.setText("Please select where the Atlas4500 Tuner will be installed:");
targetDirectoryPage.targetDirectory.textChanged.connect(this, this.targetDirectoryChanged);
targetDirectoryPage.targetDirectory.setText(installer.value("TargetDir"));
targetDirectoryPage.targetChooser.released.connect(this, this.targetChooserClicked);
gui.pageById(QInstaller.ComponentSelection).entered.connect(this, this.componentSelectionPageEntered);
}
Component.prototype.targetChooserClicked = function()
{
var dir = QFileDialog.getExistingDirectory("", targetDirectoryPage.targetDirectory.text);
targetDirectoryPage.targetDirectory.setText(dir);
}
Component.prototype.targetDirectoryChanged = function()
{
var dir = targetDirectoryPage.targetDirectory.text;
if (installer.fileExists(dir) && installer.fileExists(dir + "/maintenancetool.exe")) {
targetDirectoryPage.warning.setText("<p style=\"color: red\">Existing installation detected and will be overwritten.</p>");
}
else if (installer.fileExists(dir)) {
targetDirectoryPage.warning.setText("<p style=\"color: red\">Installing in existing directory. It will be wiped on uninstallation.</p>");
}
else {
targetDirectoryPage.warning.setText("");
}
installer.setValue("TargetDir", dir);
}
Component.prototype.componentSelectionPageEntered = function()
{
var dir = installer.value("TargetDir");
if (installer.fileExists(dir) && installer.fileExists(dir + "/maintenancetool.exe")) {
installer.execute(dir + "/maintenancetool.exe", "--script=" + dir + "/scripts/auto_uninstall.qs");
}
}
自定义目标目录小部件 packages/Atlas4500 Tuner/meta/targetwidget.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TargetWidget</class>
<widget class="QWidget" name="TargetWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>491</width>
<height>190</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>491</width>
<height>190</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="description">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="targetDirectory">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="targetChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="warning">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>122</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
packages/Atlas4500 Tuner/data/scripts/auto_uninstall.qs:
// Controller script to pass to the uninstaller to get it to run automatically.
// It's passed to the maintenance tool during installation if there is already an
// installation present with: <target dir>/maintenancetool.exe --script=<target dir>/scripts/auto_uninstall.qs.
// This is required so that the user doesn't have to see/deal with the uninstaller in the middle of
// an installation.
function Controller()
{
gui.clickButton(buttons.NextButton);
gui.clickButton(buttons.NextButton);
installer.uninstallationFinished.connect(this, this.uninstallationFinished);
}
Controller.prototype.uninstallationFinished = function()
{
gui.clickButton(buttons.NextButton);
}
Controller.prototype.FinishedPageCallback = function()
{
gui.clickButton(buttons.FinishButton);
}
这里的想法是检测当前目录中是否有安装,如果有,运行 该目录中的维护工具以及只需单击它的控制器脚本。
请注意,我将控制器脚本放在作为实际组件数据一部分的脚本目录中。如果你有多个组件,你可能会做一些更干净的事情,但这就是我正在使用的。
您应该能够自己复制这些文件,只需调整字符串即可使其正常工作。
我做了一个破解。将其放在 installscript.qs.
的末尾
component.addOperation("AppendFile", "@TargetDir@/cleanup.bat",
"ping 127.0.0.1 -n 4\r\ndel /F /Q maintenancetool.exe \
&& for /F %%L in ('reg query HKEY_USERS /v /f \"@TargetDir@\maintenancetool.exe\" /d /t REG_SZ /s /e') do \
reg query %%L /v DisplayName \
&& reg delete %%L /f\r\ndel /F /Q cleanup.bat \
&& exit\r\n")
component.addOperation("Execute", "workingdirectory=@TargetDir@",
"cmd", "/C", "start", "/B",
"Cleaning up", "cmd /C ping 127.0.0.1 -n 2 > nul && cleanup.bat > nul")
这将删除 waiting 3 seconds 之后的 maintenancetool.exe,这会导致安装程序仅警告目标文件夹不为空,而不是拒绝安装。它还会删除用于卸载程序的注册表项,因此它不会在 add/remove 个程序中累积。显然,在删除维护工具后,您不能再使用它来进行卸载或更新等操作,但我仅通过 运行 安装程序再次支持它。 maintenancetool 只在安装完成后编写,cmd start cmd
hackery 是为了让安装程序不会注意到还有一个步骤 运行。如果您有多个可选组件,您可能需要增加延迟或使其更可靠地检查是否仍然存在 运行.
理论上应该不需要写批处理文件然后执行。您应该能够直接执行命令。在实践中,我还没有找到正确转义引号以使正确的 cmd 实例评估正确部分的方法。
好的,这个答案是基于最新的安装程序框架(3.2.2),我不确定它是否适用于旧版本。
要覆盖目标目录,您只需在配置文件中将 RemoveTargetDir 设置为 false:
<RemoveTargetDir>false</RemoveTargetDir>
成功了。
官方文档对这个元素的解释如下:
Set to false if the target directory should not be deleted when uninstalling.
如果你不知道它是如何使用的,有点混乱:
bool TargetDirectoryPage::validatePage()
{
m_textChangeTimer.stop();
if (!isComplete())
return false;
if (!isVisible())
return true;
///
/// NOTICE HERE:
/// If you set RemoveTargetDir to false, function return true here.
const QString remove = packageManagerCore()->value(QLatin1String("RemoveTargetDir"));
if (!QVariant(remove).toBool())
return true;
const QString targetDir = this->targetDir();
const QDir dir(targetDir);
// the directory exists and is empty...
if (dir.exists() && dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty())
return true;
const QFileInfo fi(targetDir);
if (fi.isDir()) {
QString fileName = packageManagerCore()->settings().maintenanceToolName();
#if defined(Q_OS_MACOS)
if (QInstaller::isInBundle(QCoreApplication::applicationDirPath()))
fileName += QLatin1String(".app/Contents/MacOS/") + fileName;
#elif defined(Q_OS_WIN)
fileName += QLatin1String(".exe");
#endif
QFileInfo fi2(targetDir + QDir::separator() + fileName);
///
/// AND NOTICE HERE:
/// Do exists check here.
if (fi2.exists()) {
return failWithError(QLatin1String("TargetDirectoryInUse"), tr("The directory you selected already "
"exists and contains an installation. Choose a different target for installation."));
}
return askQuestion(QLatin1String("OverwriteTargetDirectory"),
tr("You have selected an existing, non-empty directory for installation.\nNote that it will be "
"completely wiped on uninstallation of this application.\nIt is not advisable to install into "
"this directory as installation might fail.\nDo you want to continue?"));
} else if (fi.isFile() || fi.isSymLink()) {
return failWithError(QLatin1String("WrongTargetDirectory"), tr("You have selected an existing file "
"or symlink, please choose a different target for installation."));
}
return true;
}
通知评论“此处注意:”。
我找到了一种无需嵌入式控制器脚本即可使用 的方法!
您所要做的就是使用 purge
命令启动维护工具并将 yes
发送到其标准输入。
因此,将组件脚本中的这一行 installer.execute(dir + "/maintenancetool.exe", "--script=" + dir + "/scripts/auto_uninstall.qs");
替换为这一行 installer.execute(dir + "/maintenancetool.exe", ["purge"], "yes");
这样,安装被替换,Windows 中的 add/remove 程序 UI 不包含任何重复项。
对于 Windows 用户,请确保原始安装目录未被终端打开。如果是,该目录将不会被删除并且安装将失败。该目录将保持错误状态,您无法删除或访问它,直到您重新启动会话。
这个问题是关于 Qt Installer Framework 2.0 版的。
此时,使用 Qt Installer Framework 的人都知道,如果不进行自定义,您根本无法通过安装程序覆盖现有安装。这显然是为了解决在使用 Qt 框架时出现的一些问题。
但是,对于较小的、相对简单的项目,覆盖非常好,而且比必须事先手动 运行 维护工具方便得多。
我正在寻找一个涉及自定义 UI + 组件脚本的解决方案,该脚本将一个按钮添加到目标目录页面,允许用户
- 删除指定的目录,如果存在,或者
- 运行那个目录下的维护工具
最好能够运行目标目录中的维护工具,让它自动删除给定的包,但是我意识到那是要求有点高了。
我已阅读此站点上有关解决同一问题的其他问题的答案,但 none 的解决方案工作正常。我还想提一下,我有一个组件脚本 运行ning,但没有自定义 UIs.
您需要做的事情很少:
要通过添加此代码可以实现的 TargetDirectoryPage
installer.setValue("RemoveTargetDir", false)
允许您运行此代码的自定义UI(或消息框)。此 UI 应插入到 TargetDirectoryPage 之后。
// you need to append .exe on the maintenance for windows installation installer.execute(installer.findPath(installer.value("MaintenanceToolName"), installer.value("TargetDir")));
我终于找到了可行的解决方案。
你需要三件事来完成这个:
- 组件脚本,
- 目标目录页面的自定义UI,以及
- 自动点击卸载程序的控制器脚本。
我现在将逐字列出对我有用的东西(以及我的项目特定的东西)。我的组件名为 Atlas4500 Tuner
config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Installer>
<Name>Atlas4500 Tuner</Name>
<Version>1.0.0</Version>
<Title>Atlas4500 Tuner Installer</Title>
<Publisher>EF Johnson Technologies</Publisher>
<StartMenuDir>EF Johnson</StartMenuDir>
<TargetDir>C:\Program Files (x86)\EF Johnson\Atlas4500 Tuner</TargetDir>
</Installer>
packages/Atlas4500 Tuner/meta/package.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>Atlas4500Tuner</DisplayName>
<Description>Install the Atlas4500 Tuner</Description>
<Version>1.0.0</Version>
<ReleaseDate></ReleaseDate>
<Default>true</Default>
<Required>true</Required>
<Script>installscript.qs</Script>
<UserInterfaces>
<UserInterface>targetwidget.ui</UserInterface>
</UserInterfaces>
</Package>
自定义组件脚本packages/Atlas4500Tuner/meta/installscript.qs:
var targetDirectoryPage = null;
function Component()
{
installer.gainAdminRights();
component.loaded.connect(this, this.installerLoaded);
}
Component.prototype.createOperations = function()
{
// Add the desktop and start menu shortcuts.
component.createOperations();
component.addOperation("CreateShortcut",
"@TargetDir@/Atlas4500Tuner.exe",
"@DesktopDir@/Atlas4500 Tuner.lnk",
"workingDirectory=@TargetDir@");
component.addOperation("CreateShortcut",
"@TargetDir@/Atlas4500Tuner.exe",
"@StartMenuDir@/Atlas4500 Tuner.lnk",
"workingDirectory=@TargetDir@");
}
Component.prototype.installerLoaded = function()
{
installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
installer.addWizardPage(component, "TargetWidget", QInstaller.TargetDirectory);
targetDirectoryPage = gui.pageWidgetByObjectName("DynamicTargetWidget");
targetDirectoryPage.windowTitle = "Choose Installation Directory";
targetDirectoryPage.description.setText("Please select where the Atlas4500 Tuner will be installed:");
targetDirectoryPage.targetDirectory.textChanged.connect(this, this.targetDirectoryChanged);
targetDirectoryPage.targetDirectory.setText(installer.value("TargetDir"));
targetDirectoryPage.targetChooser.released.connect(this, this.targetChooserClicked);
gui.pageById(QInstaller.ComponentSelection).entered.connect(this, this.componentSelectionPageEntered);
}
Component.prototype.targetChooserClicked = function()
{
var dir = QFileDialog.getExistingDirectory("", targetDirectoryPage.targetDirectory.text);
targetDirectoryPage.targetDirectory.setText(dir);
}
Component.prototype.targetDirectoryChanged = function()
{
var dir = targetDirectoryPage.targetDirectory.text;
if (installer.fileExists(dir) && installer.fileExists(dir + "/maintenancetool.exe")) {
targetDirectoryPage.warning.setText("<p style=\"color: red\">Existing installation detected and will be overwritten.</p>");
}
else if (installer.fileExists(dir)) {
targetDirectoryPage.warning.setText("<p style=\"color: red\">Installing in existing directory. It will be wiped on uninstallation.</p>");
}
else {
targetDirectoryPage.warning.setText("");
}
installer.setValue("TargetDir", dir);
}
Component.prototype.componentSelectionPageEntered = function()
{
var dir = installer.value("TargetDir");
if (installer.fileExists(dir) && installer.fileExists(dir + "/maintenancetool.exe")) {
installer.execute(dir + "/maintenancetool.exe", "--script=" + dir + "/scripts/auto_uninstall.qs");
}
}
自定义目标目录小部件 packages/Atlas4500 Tuner/meta/targetwidget.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TargetWidget</class>
<widget class="QWidget" name="TargetWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>491</width>
<height>190</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>491</width>
<height>190</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="description">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="targetDirectory">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="targetChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="warning">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>122</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
packages/Atlas4500 Tuner/data/scripts/auto_uninstall.qs:
// Controller script to pass to the uninstaller to get it to run automatically.
// It's passed to the maintenance tool during installation if there is already an
// installation present with: <target dir>/maintenancetool.exe --script=<target dir>/scripts/auto_uninstall.qs.
// This is required so that the user doesn't have to see/deal with the uninstaller in the middle of
// an installation.
function Controller()
{
gui.clickButton(buttons.NextButton);
gui.clickButton(buttons.NextButton);
installer.uninstallationFinished.connect(this, this.uninstallationFinished);
}
Controller.prototype.uninstallationFinished = function()
{
gui.clickButton(buttons.NextButton);
}
Controller.prototype.FinishedPageCallback = function()
{
gui.clickButton(buttons.FinishButton);
}
这里的想法是检测当前目录中是否有安装,如果有,运行 该目录中的维护工具以及只需单击它的控制器脚本。
请注意,我将控制器脚本放在作为实际组件数据一部分的脚本目录中。如果你有多个组件,你可能会做一些更干净的事情,但这就是我正在使用的。
您应该能够自己复制这些文件,只需调整字符串即可使其正常工作。
我做了一个破解。将其放在 installscript.qs.
的末尾component.addOperation("AppendFile", "@TargetDir@/cleanup.bat",
"ping 127.0.0.1 -n 4\r\ndel /F /Q maintenancetool.exe \
&& for /F %%L in ('reg query HKEY_USERS /v /f \"@TargetDir@\maintenancetool.exe\" /d /t REG_SZ /s /e') do \
reg query %%L /v DisplayName \
&& reg delete %%L /f\r\ndel /F /Q cleanup.bat \
&& exit\r\n")
component.addOperation("Execute", "workingdirectory=@TargetDir@",
"cmd", "/C", "start", "/B",
"Cleaning up", "cmd /C ping 127.0.0.1 -n 2 > nul && cleanup.bat > nul")
这将删除 waiting 3 seconds 之后的 maintenancetool.exe,这会导致安装程序仅警告目标文件夹不为空,而不是拒绝安装。它还会删除用于卸载程序的注册表项,因此它不会在 add/remove 个程序中累积。显然,在删除维护工具后,您不能再使用它来进行卸载或更新等操作,但我仅通过 运行 安装程序再次支持它。 maintenancetool 只在安装完成后编写,cmd start cmd
hackery 是为了让安装程序不会注意到还有一个步骤 运行。如果您有多个可选组件,您可能需要增加延迟或使其更可靠地检查是否仍然存在 运行.
理论上应该不需要写批处理文件然后执行。您应该能够直接执行命令。在实践中,我还没有找到正确转义引号以使正确的 cmd 实例评估正确部分的方法。
好的,这个答案是基于最新的安装程序框架(3.2.2),我不确定它是否适用于旧版本。
要覆盖目标目录,您只需在配置文件中将 RemoveTargetDir 设置为 false:
<RemoveTargetDir>false</RemoveTargetDir>
成功了。
官方文档对这个元素的解释如下:
Set to false if the target directory should not be deleted when uninstalling.
如果你不知道它是如何使用的,有点混乱:
bool TargetDirectoryPage::validatePage()
{
m_textChangeTimer.stop();
if (!isComplete())
return false;
if (!isVisible())
return true;
///
/// NOTICE HERE:
/// If you set RemoveTargetDir to false, function return true here.
const QString remove = packageManagerCore()->value(QLatin1String("RemoveTargetDir"));
if (!QVariant(remove).toBool())
return true;
const QString targetDir = this->targetDir();
const QDir dir(targetDir);
// the directory exists and is empty...
if (dir.exists() && dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty())
return true;
const QFileInfo fi(targetDir);
if (fi.isDir()) {
QString fileName = packageManagerCore()->settings().maintenanceToolName();
#if defined(Q_OS_MACOS)
if (QInstaller::isInBundle(QCoreApplication::applicationDirPath()))
fileName += QLatin1String(".app/Contents/MacOS/") + fileName;
#elif defined(Q_OS_WIN)
fileName += QLatin1String(".exe");
#endif
QFileInfo fi2(targetDir + QDir::separator() + fileName);
///
/// AND NOTICE HERE:
/// Do exists check here.
if (fi2.exists()) {
return failWithError(QLatin1String("TargetDirectoryInUse"), tr("The directory you selected already "
"exists and contains an installation. Choose a different target for installation."));
}
return askQuestion(QLatin1String("OverwriteTargetDirectory"),
tr("You have selected an existing, non-empty directory for installation.\nNote that it will be "
"completely wiped on uninstallation of this application.\nIt is not advisable to install into "
"this directory as installation might fail.\nDo you want to continue?"));
} else if (fi.isFile() || fi.isSymLink()) {
return failWithError(QLatin1String("WrongTargetDirectory"), tr("You have selected an existing file "
"or symlink, please choose a different target for installation."));
}
return true;
}
通知评论“此处注意:”。
我找到了一种无需嵌入式控制器脚本即可使用
您所要做的就是使用 purge
命令启动维护工具并将 yes
发送到其标准输入。
因此,将组件脚本中的这一行 installer.execute(dir + "/maintenancetool.exe", "--script=" + dir + "/scripts/auto_uninstall.qs");
替换为这一行 installer.execute(dir + "/maintenancetool.exe", ["purge"], "yes");
这样,安装被替换,Windows 中的 add/remove 程序 UI 不包含任何重复项。
对于 Windows 用户,请确保原始安装目录未被终端打开。如果是,该目录将不会被删除并且安装将失败。该目录将保持错误状态,您无法删除或访问它,直到您重新启动会话。