通过[代码]

时间:2016-05-14 02:40:59

标签: inno-setup

Inno Setup默认查看PrivilegesRequired安装变量,如果设置为adminpoweruser,则安装程序会将卸载注册表项安装到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设置如何。

2 个答案:

答案 0 :(得分:2)

Inno Setup 6 beta内置了对selecting between "Install for all users" and "Install for me only"的支持。

基本上,您只需设置PrivilegesRequiredOverridesAllowed

即可
PrivilegesRequired=none

enter image description here

对于Inno Setup 5:正如您自己发现的那样,逻辑是硬编码的。你无法控制它。

您可以获得的最接近的是使用未记录的(已弃用的)HKCU

使用此值(并在Windows中使用installer-autodetection帮助):

这不完全是你想要的,但我认为你不能再接近了。

您当然可以通过代码在HKLMfunction 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位运行。

相关问题