Inno Setup 在选择特定组件时禁用组件选择

Inno Setup disable component selection when a specific component is selected

我希望能够根据所选的特定组件来禁用对组件的选择。我不能通过嵌套组件来做到这一点,因为组件需要自己选择,但如果选择了另一个特定组件则不行。目前我使用显示消息的 NextButtonClick 事件处理此问题:

if IsComponentSelected('Client') and IsComponentSelected('Sync') then
  begin
    MsgBox('A Client installation cannot have the Synchronisation component selected.',
      mbError, MB_OK);
      Result := False;      
  end;

这会阻止用户继续,直到他们取消选择不兼容的组合。但是,如果我可以简单地禁用组件的选择而不是显示消息,那就更优雅了:

if CurPageID = wpSelectComponents then
  begin
    if IsComponentSelected('Client') then
      begin
        WizardForm.ComponentsList.Checked[15] := False;
        WizardForm.ComponentsList.ItemEnabled[15] := False;
      end
    else
      begin
        WizardForm.ComponentsList.ItemEnabled[15] := True;
      end;
  end;

问题是似乎没有允许它在用户选择或取消选择组件时动态更改的事件。是否有我可以将此代码放入的事件或其他方式来执行此操作?

你有 TLama 的解决方案,它可能比下面的更好,但这段代码仍然是实现目标的一种方法(尽管你需要 innocallback.dll):

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program

[Files]
Source: ".\InnoCallback.dll"; DestDir: "{tmp}"; Flags: dontcopy nocompression

[Components]
Name: "Client"; Description: "Client"; Types: custom
Name: "Sync"; Description: "Sync"; Types: custom

[Code]
var
TimerID: Integer;

type
TTimerProc = procedure(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR; 
SysTime: DWORD);
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord;
external 'wrapcallback@files:InnoCallback.dll stdcall';    
function SetTimer(hWnd: HWND; nIDEvent, uElapse: UINT;
lpTimerFunc: UINT): UINT; external 'SetTimer@user32.dll stdcall';

function KillTimer(hWnd: HWND; uIDEvent: UINT): BOOL; 
external 'KillTimer@user32.dll stdcall'; 

procedure KillComponentsTimer;
begin
  if TimerID <> 0 then 
    begin
      if KillTimer(0, TimerID) then
        TimerID := 1;
    end;
end;

procedure OnComponentsCheck(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR; 
SysTime: DWORD);
begin
    if IsComponentSelected('Client') then begin
        WizardForm.ComponentsList.Checked[1] := False;
        WizardForm.ComponentsList.ItemEnabled[1] := False;
    end
    else begin
        WizardForm.ComponentsList.ItemEnabled[1] := True;
    end;    
    if IsComponentSelected('Sync') then begin
        WizardForm.ComponentsList.Checked[0] := False;
        WizardForm.ComponentsList.ItemEnabled[0] := False;
    end
    else begin 
        WizardForm.ComponentsList.ItemEnabled[0] := True;
    end;
end;

procedure ComponentsCheck();
var
  TimerCallback: LongWord;
begin
  TimerCallback := WrapTimerProc(@OnComponentsCheck, 4);
  TimerID := SetTimer(0, 0, 100, TimerCallback);
end;

procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
  begin
    ComponentsCheck;
  end;
if CurPageID = not wpSelectComponents then 
  begin
   KillComponentsTimer;
  end;
end;

我最后使用的代码完全基于@TLama他之前提供的代码,因为这不需要使用外部DLL。

[Code]
const
//Define global constants
  CompIndexSync = 15;
  CompIndexSyncClient = 16;
  CompIndexSyncServer = 17;

var
//Define global variables
  CompPageVisited: Boolean;
  DefaultCompClickCheck: TNotifyEvent;
  DefaultCompTypeChange: TNotifyEvent;

//Uncheck and set the enabled state of the Sync components based on whether the Client component is selected
procedure UpdateComponents;
begin
  with WizardForm.ComponentsList do
    begin
      if IsComponentSelected('Client') then
        begin
          CheckItem(CompIndexSync, coUncheck);
        end; 
      ItemEnabled[CompIndexSync] := not IsComponentSelected('Client');
      ItemEnabled[CompIndexSyncClient] := not IsComponentSelected('Client');
      ItemEnabled[CompIndexSyncServer] := not IsComponentSelected('Client');
      Invalidate; //required for text state to update correctly
    end;
end;

//Update the component states if the component states change and restore the original event handler procedures
procedure ComponentsClickCheck(Sender: TObject);
begin
  DefaultCompClickCheck(Sender);
  UpdateComponents;
end;

procedure ComponentsTypesComboChange(Sender: TObject);
begin
  DefaultCompTypeChange(Sender);
  UpdateComponents;
end;

procedure InitializeWizard();
begin
//Store the original Components Page OnClickCheck and Types Combo Box OnChange event procedures and assign custom procedures
  DefaultCompClickCheck := WizardForm.ComponentsList.OnClickCheck;
  WizardForm.ComponentsList.OnClickCheck := @ComponentsClickCheck;
  DefaultCompTypeChange := WizardForm.TypesCombo.OnChange;
  WizardForm.TypesCombo.OnChange := @ComponentsTypesComboChange;
end;

procedure CurPageChanged(CurPageID: Integer);
begin
//Update the Components Page if entered for the first time
  if (CurPageID = wpSelectComponents) and not CompPageVisited then
    begin
      CompPageVisited := True;
      UpdateComponents;
    end;
end;

虽然这个和@RobeN 的解决方案都有效,但它们都存在图形更新问题,即当将组件的状态从活动更改为禁用时,反之亦然,文本不会自动变暗或取消变暗,直到滚动条被拖了。

请注意,上述刷新问题已通过添加“无效;”得到解决线。上面更新的代码带有评论以反映这一点。有关详细信息,请参阅 。谢谢@TLama。