TaskDialog.pas 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. unit TaskDialog;
  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, CmnFunc;
  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, Commctrl, CmnFunc2, InstFunc, PathFunc;
  16. var
  17. TaskDialogIndirectFunc: function(const pTaskConfig: TTaskDialogConfig;
  18. pnButton: PInteger; pnRadioButton: PInteger;
  19. pfVerificationFlagChecked: PBOOL): HRESULT; stdcall;
  20. function ShieldButtonCallback(hwnd: HWND; msg: UINT; wParam: WPARAM; lParam: LPARAM; lpRefData: LONG_PTR): HResult; stdcall;
  21. begin
  22. if (msg = TDN_CREATED) and (lpRefData <> 0) then
  23. SendMessage(hwnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, lpRefData, 1);
  24. Result := S_OK;
  25. end;
  26. 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;
  27. var
  28. Config: TTaskDialogConfig;
  29. NButtonLabelsAvailable: Integer;
  30. ButtonItems: TTaskDialogButtons;
  31. ButtonItem: TTaskDialogButtonItem;
  32. I: Integer;
  33. ActiveWindow: Windows.HWND;
  34. WindowList: Pointer;
  35. begin
  36. if Assigned(TaskDialogIndirectFunc) then begin
  37. ZeroMemory(@Config, Sizeof(Config));
  38. Config.cbSize := SizeOf(Config);
  39. if RightToLeft then
  40. Config.dwFlags := Config.dwFlags or TDF_RTL_LAYOUT;
  41. { If the application window isn't currently visible, show the task dialog
  42. with no owner window so it'll get a taskbar button }
  43. Config.hInstance := HInstance;
  44. if IsIconic(Application.Handle) or
  45. (GetWindowLong(Application.Handle, GWL_STYLE) and WS_VISIBLE = 0) or
  46. (GetWindowLong(Application.Handle, GWL_EXSTYLE) and WS_EX_TOOLWINDOW <> 0) then
  47. Config.hWndParent := 0
  48. else
  49. Config.hwndParent := hWnd;
  50. Config.dwCommonButtons := CommonButtons;
  51. Config.pszWindowTitle := Caption;
  52. Config.pszMainIcon := Icon;
  53. Config.pszMainInstruction := Instruction;
  54. Config.pszContent := Text;
  55. if VerificationText <> '' then
  56. Config.pszVerificationText := VerificationText;
  57. if ShieldButton <> 0 then begin
  58. Config.pfCallback := ShieldButtonCallback;
  59. Config.lpCallbackData := ShieldButton;
  60. end;
  61. ButtonItems := nil;
  62. try
  63. NButtonLabelsAvailable := Length(ButtonLabels);
  64. if NButtonLabelsAvailable <> 0 then begin
  65. ButtonItems := TTaskDialogButtons.Create(nil, TTaskDialogButtonItem);
  66. Config.dwFlags := Config.dwFlags or TDF_USE_COMMAND_LINKS;
  67. for I := 0 to NButtonLabelsAvailable-1 do begin
  68. ButtonItem := TTaskDialogButtonItem(ButtonItems.Add);
  69. ButtonItem.Caption := ButtonLabels[I];
  70. ButtonItem.ModalResult := ButtonIDs[I];
  71. end;
  72. Config.pButtons := ButtonItems.Buttons;
  73. Config.cButtons := ButtonItems.Count;
  74. end;
  75. TriggerMessageBoxCallbackFunc(TriggerMessageBoxCallbackFuncFlags, False);
  76. ActiveWindow := GetActiveWindow;
  77. WindowList := DisableTaskWindows(0);
  78. try
  79. Result := TaskDialogIndirectFunc(Config, @ModalResult, nil, pfVerificationFlagChecked) = S_OK;
  80. finally
  81. EnableTaskWindows(WindowList);
  82. SetActiveWindow(ActiveWindow);
  83. TriggerMessageBoxCallbackFunc(TriggerMessageBoxCallbackFuncFlags, True);
  84. end;
  85. finally
  86. ButtonItems.Free;
  87. end;
  88. end else
  89. Result := False;
  90. end;
  91. 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;
  92. var
  93. IconP: PChar;
  94. TDCommonButtons: Cardinal;
  95. NButtonLabelsAvailable: Integer;
  96. ButtonIDs: array of Integer;
  97. begin
  98. if Icon <> '' then
  99. IconP := PChar(Icon)
  100. else begin
  101. case Typ of
  102. mbInformation: IconP := TD_INFORMATION_ICON;
  103. mbError: IconP := TD_WARNING_ICON;
  104. mbCriticalError: IconP := TD_ERROR_ICON;
  105. else
  106. 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 }
  107. end;
  108. end;
  109. NButtonLabelsAvailable := Length(ButtonLabels);
  110. case Buttons of
  111. MB_OK, MB_OKCANCEL:
  112. begin
  113. if NButtonLabelsAvailable = 0 then
  114. TDCommonButtons := TDCBF_OK_BUTTON
  115. else begin
  116. TDCommonButtons := 0;
  117. ButtonIDs := [IDOK];
  118. end;
  119. if Buttons = MB_OKCANCEL then
  120. TDCommonButtons := TDCommonButtons or TDCBF_CANCEL_BUTTON;
  121. end;
  122. MB_YESNO, MB_YESNOCANCEL:
  123. begin
  124. if NButtonLabelsAvailable = 0 then
  125. TDCommonButtons := TDCBF_YES_BUTTON or TDCBF_NO_BUTTON
  126. else begin
  127. TDCommonButtons := 0;
  128. ButtonIDs := [IDYES, IDNO];
  129. end;
  130. if Buttons = MB_YESNOCANCEL then
  131. TDCommonButtons := TDCommonButtons or TDCBF_CANCEL_BUTTON;
  132. end;
  133. MB_RETRYCANCEL:
  134. begin
  135. if NButtonLabelsAvailable = 0 then
  136. TDCommonButtons := TDCBF_RETRY_BUTTON
  137. else begin
  138. TDCommonButtons := 0;
  139. ButtonIDs := [IDRETRY];
  140. end;
  141. TDCommonButtons := TDCommonButtons or TDCBF_CANCEL_BUTTON;
  142. end;
  143. MB_ABORTRETRYIGNORE:
  144. begin
  145. if NButtonLabelsAvailable = 0 then
  146. InternalError('TaskDialogMsgBox: Invalid ButtonLabels')
  147. else
  148. ButtonIDs := [IDRETRY, IDIGNORE, IDABORT]; { Notice the order, abort label must be last }
  149. TDCommonButtons := 0;
  150. end;
  151. else
  152. begin
  153. InternalError('TaskDialogMsgBox: Invalid Buttons');
  154. TDCommonButtons := 0; { Silence compiler }
  155. end;
  156. end;
  157. if Length(ButtonIDs) <> NButtonLabelsAvailable then
  158. InternalError('TaskDialogMsgBox: Invalid ButtonLabels');
  159. if not DoTaskDialog(Application.Handle, PChar(Instruction), PChar(Text),
  160. GetMessageBoxCaption(PChar(Caption), Typ), IconP, TDCommonButtons, ButtonLabels, ButtonIDs, ShieldButton,
  161. 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)
  162. Result := 0;
  163. end;
  164. procedure InitCommonControls; external comctl32 name 'InitCommonControls';
  165. initialization
  166. InitCommonControls;
  167. TaskDialogIndirectFunc := GetProcAddress(GetModuleHandle(comctl32), 'TaskDialogIndirect');
  168. end.