Shared.TaskDialogFunc.pas 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. unit Shared.TaskDialogFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2020 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. TaskDialogMsgBox function integrating with CmnFunc's MsgBox functions
  8. }
  9. interface
  10. uses
  11. Windows, Shared.CommonFunc.Vcl;
  12. function TaskDialogMsgBox(const Icon, Instruction, Text, Caption: String; const Typ: TMsgBoxType; const Buttons: Cardinal; const ButtonLabels: array of String; const ShieldButton: Integer; const VerificationText: String = ''; const pfVerificationFlagChecked: PBOOL = nil): Integer;
  13. implementation
  14. uses
  15. Classes, StrUtils, Math, Forms, Dialogs, SysUtils,
  16. Commctrl, Shared.CommonFunc, {$IFDEF SETUPPROJ} Setup.InstFunc, {$ENDIF} PathFunc;
  17. var
  18. TaskDialogIndirectFunc: function(const pTaskConfig: TTaskDialogConfig;
  19. pnButton: PInteger; pnRadioButton: PInteger;
  20. pfVerificationFlagChecked: PBOOL): HRESULT; stdcall;
  21. function ShieldButtonCallback(hwnd: HWND; msg: UINT; wParam: WPARAM; lParam: LPARAM; lpRefData: LONG_PTR): HResult; stdcall;
  22. begin
  23. if (msg = TDN_CREATED) and (lpRefData <> 0) then
  24. SendMessage(hwnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, lpRefData, 1);
  25. Result := S_OK;
  26. end;
  27. function DoTaskDialog(const hWnd: HWND; const Instruction, Text, Caption, Icon: PWideChar; const CommonButtons: Cardinal; const ButtonLabels: array of String; const ButtonIDs: array of Integer; const ShieldButton: Integer; const RightToLeft: Boolean; const TriggerMessageBoxCallbackFuncFlags: LongInt; var ModalResult: Integer; const VerificationText: PWideChar; const pfVerificationFlagChecked: PBOOL): Boolean;
  28. var
  29. Config: TTaskDialogConfig;
  30. NButtonLabelsAvailable: Integer;
  31. ButtonItems: TTaskDialogButtons;
  32. ButtonItem: TTaskDialogButtonItem;
  33. I: Integer;
  34. ActiveWindow: Windows.HWND;
  35. WindowList: Pointer;
  36. begin
  37. if Assigned(TaskDialogIndirectFunc) then begin
  38. ZeroMemory(@Config, Sizeof(Config));
  39. Config.cbSize := SizeOf(Config);
  40. Config.dwFlags := TDF_SIZE_TO_CONTENT;
  41. if RightToLeft then
  42. Config.dwFlags := Config.dwFlags or TDF_RTL_LAYOUT;
  43. Config.hInstance := HInstance;
  44. Config.hwndParent := hWnd;
  45. Config.dwCommonButtons := CommonButtons;
  46. Config.pszWindowTitle := Caption;
  47. Config.pszMainIcon := Icon;
  48. Config.pszMainInstruction := Instruction;
  49. Config.pszContent := Text;
  50. if VerificationText <> '' then
  51. Config.pszVerificationText := VerificationText;
  52. if ShieldButton <> 0 then begin
  53. Config.pfCallback := ShieldButtonCallback;
  54. Config.lpCallbackData := ShieldButton;
  55. end;
  56. ButtonItems := nil;
  57. try
  58. NButtonLabelsAvailable := Length(ButtonLabels);
  59. if NButtonLabelsAvailable <> 0 then begin
  60. ButtonItems := TTaskDialogButtons.Create(nil, TTaskDialogButtonItem);
  61. Config.dwFlags := Config.dwFlags or TDF_USE_COMMAND_LINKS;
  62. for I := 0 to NButtonLabelsAvailable-1 do begin
  63. ButtonItem := TTaskDialogButtonItem(ButtonItems.Add);
  64. ButtonItem.Caption := ButtonLabels[I];
  65. ButtonItem.ModalResult := ButtonIDs[I];
  66. end;
  67. Config.pButtons := ButtonItems.Buttons;
  68. Config.cButtons := ButtonItems.Count;
  69. end;
  70. TriggerMessageBoxCallbackFunc(TriggerMessageBoxCallbackFuncFlags, False);
  71. ActiveWindow := GetActiveWindow;
  72. WindowList := DisableTaskWindows(Config.hwndParent);
  73. try
  74. Result := TaskDialogIndirectFunc(Config, @ModalResult, nil, pfVerificationFlagChecked) = S_OK;
  75. finally
  76. EnableTaskWindows(WindowList);
  77. SetActiveWindow(ActiveWindow);
  78. TriggerMessageBoxCallbackFunc(TriggerMessageBoxCallbackFuncFlags, True);
  79. end;
  80. finally
  81. ButtonItems.Free;
  82. end;
  83. end else
  84. Result := False;
  85. end;
  86. procedure DoInternalError(const Msg: String);
  87. begin
  88. {$IFDEF SETUPPROJ}
  89. InternalError(Msg);
  90. {$ELSE}
  91. raise Exception.Create(Msg);
  92. {$ENDIF}
  93. end;
  94. function TaskDialogMsgBox(const Icon, Instruction, Text, Caption: String; const Typ: TMsgBoxType; const Buttons: Cardinal; const ButtonLabels: array of String; const ShieldButton: Integer; const VerificationText: String = ''; const pfVerificationFlagChecked: PBOOL = nil): Integer;
  95. var
  96. IconP: PChar;
  97. TDCommonButtons: Cardinal;
  98. NButtonLabelsAvailable: Integer;
  99. ButtonIDs: array of Integer;
  100. begin
  101. Application.Restore; { See comments in AppMessageBox }
  102. if Icon <> '' then
  103. IconP := PChar(Icon)
  104. else begin
  105. case Typ of
  106. mbInformation: IconP := TD_INFORMATION_ICON;
  107. mbError: IconP := TD_WARNING_ICON;
  108. mbCriticalError: IconP := TD_ERROR_ICON;
  109. else
  110. IconP := nil; { No other TD_ constant available, MS recommends to use no icon for questions now and the old icon should only be used for help entries }
  111. end;
  112. end;
  113. NButtonLabelsAvailable := Length(ButtonLabels);
  114. case Buttons of
  115. MB_OK, MB_OKCANCEL:
  116. begin
  117. if NButtonLabelsAvailable = 0 then
  118. TDCommonButtons := TDCBF_OK_BUTTON
  119. else begin
  120. TDCommonButtons := 0;
  121. ButtonIDs := [IDOK];
  122. end;
  123. if Buttons = MB_OKCANCEL then
  124. TDCommonButtons := TDCommonButtons or TDCBF_CANCEL_BUTTON;
  125. end;
  126. MB_YESNO, MB_YESNOCANCEL:
  127. begin
  128. if NButtonLabelsAvailable = 0 then
  129. TDCommonButtons := TDCBF_YES_BUTTON or TDCBF_NO_BUTTON
  130. else begin
  131. TDCommonButtons := 0;
  132. ButtonIDs := [IDYES, IDNO];
  133. end;
  134. if Buttons = MB_YESNOCANCEL then
  135. TDCommonButtons := TDCommonButtons or TDCBF_CANCEL_BUTTON;
  136. end;
  137. MB_RETRYCANCEL:
  138. begin
  139. if NButtonLabelsAvailable = 0 then
  140. TDCommonButtons := TDCBF_RETRY_BUTTON
  141. else begin
  142. TDCommonButtons := 0;
  143. ButtonIDs := [IDRETRY];
  144. end;
  145. TDCommonButtons := TDCommonButtons or TDCBF_CANCEL_BUTTON;
  146. end;
  147. MB_ABORTRETRYIGNORE:
  148. begin
  149. if NButtonLabelsAvailable = 0 then
  150. DoInternalError('TaskDialogMsgBox: Invalid ButtonLabels')
  151. else
  152. ButtonIDs := [IDRETRY, IDIGNORE, IDABORT]; { Notice the order, abort label must be last }
  153. TDCommonButtons := 0;
  154. end;
  155. else
  156. begin
  157. DoInternalError('TaskDialogMsgBox: Invalid Buttons');
  158. TDCommonButtons := 0; { Silence compiler }
  159. end;
  160. end;
  161. if Length(ButtonIDs) <> NButtonLabelsAvailable then
  162. DoInternalError('TaskDialogMsgBox: Invalid ButtonLabels');
  163. if not DoTaskDialog(GetOwnerWndForMessageBox, PChar(Instruction), PChar(Text),
  164. GetMessageBoxCaption(PChar(Caption), Typ), IconP, TDCommonButtons, ButtonLabels, ButtonIDs, ShieldButton,
  165. GetMessageBoxRightToLeft, IfThen(Typ in [mbError, mbCriticalError], MB_ICONSTOP, 0), Result, PChar(VerificationText), pfVerificationFlagChecked) then //note that MB_ICONEXCLAMATION (used by mbError) includes MB_ICONSTOP (used by mbCriticalError)
  166. Result := 0;
  167. end;
  168. procedure InitCommonControls; external comctl32 name 'InitCommonControls';
  169. initialization
  170. InitCommonControls;
  171. TaskDialogIndirectFunc := GetProcAddress(GetModuleHandle(comctl32), 'TaskDialogIndirect');
  172. end.