Inno Setup默认查看PrivilegesRequired
安装变量,如果设置为admin
或poweruser
,则安装程序会将卸载注册表项安装到HKLM
。如果将其设置为lowest
,则会将注册表项安装到HKCU
。
我需要为用户提供一个选项来安装"只是我"或者"每个人",并通过用这两个选项的无线电选择替换目录选择页面来完成。我现在需要做的是根据此设置修改注册表安装位置。如果我将应用程序安装到本地用户应用程序数据中,则在HKLM
级别注册卸载数据是没有意义的,因为其他用户将在程序列表中看到它并仍然无法卸载或使用它。
修改:查看文档和Install.pas
的来源后,我找到了CreateUninstallRegKey
安装指令,该指令将禁止Inno完全安装注册表项。我可以添加自己的注册表项,但这真的是唯一的方法吗?
编辑#2(标记为重复):我已经看过这个Conditional Elevation问题(实际上已经实现了),但它不是和我一样。当前提升状态 不 更改,其中Inno Setup实际保存卸载注册表信息(在HKCU或HKLM中)。如果查看Inno source code (Install.pas #507),您会看到PrivilegesRequired
指令是存储注册表的主要因素。如果将其设置为lowest
,则无论安装程序是否提升都无关紧要 - 当所需行为是根据用户安装首选项选择一个或另一个时,它会将注册表项安装到HKCU,NOT当前的高程状态。所以这就是说,我正在寻找一种解决方案来根据代码变量更改注册表根目录,无论当前的PrivilegesRequired或Elevation设置如何。
答案 0 :(得分:2)
Inno Setup 6 beta内置了对selecting between "Install for all users" and "Install for me only"的支持。
基本上,您只需设置PrivilegesRequiredOverridesAllowed
:
PrivilegesRequired=none
对于Inno Setup 5:正如您自己发现的那样,逻辑是硬编码的。你无法控制它。
您可以获得的最接近的是使用未记录的(已弃用的)HKCU
。
使用此值(并在Windows中使用installer-autodetection帮助):
这不完全是你想要的,但我认为你不能再接近了。
您当然可以通过代码在HKLM
和function MoveHKCUUninstallKeyToHKLM: Boolean;
var
UninstallKey: string;
AppId: string;
I: Integer;
ValueNames: TArrayOfString;
ValueName: string;
ValueStr: string;
ValueDWord: Cardinal;
begin
if '{#emit SetupSetting("AppId")}' <> '' then
begin
AppId := '{#emit SetupSetting("AppId")}';
end
else
begin
AppId := '{#emit SetupSetting("AppName")}';
end;
Result := False;
if AppId = '' then
begin
Log('Cannot identify AppId');
end
else
begin
UninstallKey :=
'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + AppId + '_is1';
Log(Format(
'AppId identified as "%s", using uninstall key "%s"', [AppId, UninstallKey]));
if not RegKeyExists(HKCU, UninstallKey) then
begin
Log('HKCU uninstall key not found');
end
else
if RegKeyExists(HKLM, UninstallKey) then
begin
Log('HKLM uninstall key exists already');
end
else
begin
Log('HKCU uninstall key found and HKLM key not exists yet');
if not RegGetValueNames(HKCU, UninstallKey, ValueNames) then
begin
Log('Cannot list uninstall key values');
end
else
begin
I := 0;
Result := True;
while (I < GetArrayLength(ValueNames)) and Result do
begin
ValueName := ValueNames[I];
if RegQueryStringValue(HKCU, UninstallKey, ValueName, ValueStr) then
begin
if not RegWriteStringValue(HKLM, UninstallKey, ValueName, ValueStr) then
begin
Log(Format('Error moving "%s" string value', [ValueName]));
Result := False;
end
else
begin
Log(Format('Moved "%s" string value', [ValueName]));
end;
end
else
if RegQueryDWordValue(HKCU, UninstallKey, ValueName, ValueDWord) then
begin
if not RegWriteDWordValue(HKLM, UninstallKey, ValueName, ValueDWord) then
begin
Log(Format('Error moving "%s" dword value', [ValueName]));
Result := False;
end
else
begin
Log(Format('Moved "%s" dword value', [ValueName]));
end;
end
else
begin
{ All uninstall values written by Inno Setup are either string or dword }
Log(Format('Value "%s" is neither string nor dword', [ValueName]));
Result := False;
end;
Inc(I);
end;
if Result then
begin
if not RegDeleteKeyIncludingSubkeys(HKCU, UninstallKey) then
begin
Log('Error removing HKCU uninstall key');
Result := False;
end
else
begin
Log('Removed HKCU uninstall key');
end;
end;
if not Result then
begin
if not RegDeleteKeyIncludingSubkeys(HKCU, UninstallKey) then
begin
Log('Failed to move uninstall key to HKLM, ' +
'and also failed to rollback the changes');
end
else
begin
Log('Failed to move uninstall key to HKLM, rolled back the changes');
end;
end;
end;
end;
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
Log('Post install');
MoveHKCUUninstallKeyToHKLM;
end;
end;
之间复制(移动)注册表项:
'GLOBAL DECLARATIONS
Dim conString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Acer User\Documents\MAKLUMAT IVENTORI.accdb"
Dim con As OleDbConnection = New OleDbConnection(conString)
Dim adapter As New OleDbDataAdapter
Dim cmd As New OleDbCommand
Dim dt As New DataTable
Dim ds As New DataSet
Private Sub RefreshData()
dt = New DataTable
ds = New DataSet
ds.Tables.Add(dt)
adapter = New OleDbDataAdapter("Select * FROM product WHERE lab_kod='66'", con)
adapter.Fill(dt)
DataGridView1.DataSource = dt.DefaultView
labelID.Text = getAutoID()
lblLabCode.Text = "66"
cboProduct.Select()
Dim v_SQL As String = "SELECT * FROM kategori_product"
cmd = New OleDbCommand(v_SQL, con)
con.Open()
Dim v_dataReader As OleDbDataReader = cmd.ExecuteReader()
Dim v_dataTable As New DataTable
v_dataTable.Columns.Add("product_kod", Type.GetType("System.String"))
If v_dataReader.HasRows Then
While v_dataReader.Read
v_dataTable.Rows.Add(v_dataReader.GetString(0))
End While
cboProduct.DataSource = v_dataTable
End If
cboProduct.DisplayMember = "product_kod"
cboProduct.ValueMember = "product_kod"
v_dataReader.Close()
con.Close()
End Sub
答案 1 :(得分:1)
PrivilegesRequired=none
解决方案不是我想要的。在某些情况下,它仍会提示管理员帐户的提升,并且注册表目标仍未反映用户选择。
由于我在Inno Setup项目中已经使用了本机助手DLL,因此我用C ++编写了这个代码,因为我在那里更舒服。我在CurStepChanged
CurPage=ssDoneInstall
中调用此方法。只需使用[Setup]
AppId
调用此方法,以及是否应在本地安装注册表项。
#include <shlwapi.h>
extern "C" __declspec(dllexport)
bool DetectAndMoveRegKeyW(LPCWSTR app_id, bool install_local)
{
std::wstring s_app = app_id;
std::wstring path =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + s_app + L"_is1";
LPCWSTR c_path = path.c_str();
LRESULT res;
HKEY source = nullptr, subKey = nullptr;
// try to find source in HKLM
source = HKEY_LOCAL_MACHINE;
res = RegOpenKeyExW(source, c_path, 0, KEY_READ, &subKey);
if (subKey != nullptr)
RegCloseKey(subKey);
// try to find source in HKCU
if (res != ERROR_SUCCESS)
{
subKey = nullptr;
source = HKEY_CURRENT_USER;
res = RegOpenKeyExW(source, c_path, 0, KEY_READ, &subKey);
if (subKey != nullptr)
RegCloseKey(subKey);
}
if (res != ERROR_SUCCESS)
return false; // cant find the registry key
HKEY dest = install_local ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
if (source == dest)
return true; // registry already in the right place
// copy registry key to correct destination
HKEY hOldKey;
HKEY hNewKey;
bool bResult = false;
if (RegOpenKeyW(source, c_path, &hOldKey) == 0)
{
if (RegCreateKeyW(dest, c_path, &hNewKey) == 0)
{
bResult = (SHCopyKeyW(hOldKey, nullptr, hNewKey, 0) == 0);
RegCloseKey(hNewKey);
}
RegCloseKey(hOldKey);
if (bResult)
{
RegDeleteKeyW(source, c_path);
}
}
return bResult;
}
我将此方法导出为cdecl
而不是stdcall
,这是因为VC ++在使用stdcall
时无论如何都会忽略C extern和mangles方法名称。您需要在inno (see inno docs for this)中将其导入cdecl
。此外,当然这是仅限Unicode的实现,如果您需要Ansi版本,它应该足够简单。
重要提示:
此代码不完整,它不考虑64位注册表重定向。 Inno-Setup完全忽略了Windows注册表重定向,而且这个代码根本不搜索64位注册表,因为Inno本身在32位运行。