WinInterface.cpp 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <shlwapi.h>
  5. #include <Common.h>
  6. #include <Queue.h>
  7. #include <Exceptions.h>
  8. #include <CoreMain.h>
  9. #include <TextsCore.h>
  10. #include <TextsWin.h>
  11. #include <HelpWin.h>
  12. #include <HelpCore.h>
  13. #include <Interface.h>
  14. #include <VCLCommon.h>
  15. #include <Glyphs.h>
  16. #include <PasTools.hpp>
  17. #include <DateUtils.hpp>
  18. #include <Custom.h>
  19. #include <HistoryComboBox.hpp>
  20. #include "WinInterface.h"
  21. #include "GUITools.h"
  22. #include "JclDebug.hpp"
  23. #include "JclHookExcept.hpp"
  24. #include <System.IOUtils.hpp>
  25. #include <StrUtils.hpp>
  26. #include <WinApi.h>
  27. #include "Tools.h"
  28. #include <Vcl.AppEvnts.hpp>
  29. //---------------------------------------------------------------------------
  30. #pragma package(smart_init)
  31. //---------------------------------------------------------------------------
  32. #define WM_TRAY_ICON (WM_WINSCP_USER + 5)
  33. //---------------------------------------------------------------------
  34. TNotifyEvent GlobalOnMinimize = NULL;
  35. //---------------------------------------------------------------------
  36. const IID IID_IListView_Win7 = {0xE5B16AF2, 0x3990, 0x4681, {0xA6, 0x09, 0x1F, 0x06, 0x0C, 0xD1, 0x42, 0x69}};
  37. //---------------------------------------------------------------------
  38. void __fastcall FormHelp(TCustomForm * Form)
  39. {
  40. InvokeHelp(Form->ActiveControl != NULL ? Form->ActiveControl : Form);
  41. }
  42. //---------------------------------------------------------------------------
  43. TMessageParams::TMessageParams(unsigned int AParams)
  44. {
  45. Reset();
  46. Params = AParams;
  47. }
  48. //---------------------------------------------------------------------------
  49. TMessageParams::TMessageParams(const TQueryParams * AParams)
  50. {
  51. Reset();
  52. if (AParams != NULL)
  53. {
  54. Aliases = AParams->Aliases;
  55. AliasesCount = AParams->AliasesCount;
  56. Timer = AParams->Timer;
  57. TimerEvent = AParams->TimerEvent;
  58. TimerMessage = AParams->TimerMessage;
  59. TimerAnswers = AParams->TimerAnswers;
  60. TimerQueryType = AParams->TimerQueryType;
  61. Timeout = AParams->Timeout;
  62. TimeoutAnswer = AParams->TimeoutAnswer;
  63. if (FLAGSET(AParams->Params, qpNeverAskAgainCheck))
  64. {
  65. Params |= mpNeverAskAgainCheck;
  66. }
  67. if (FLAGSET(AParams->Params, qpAllowContinueOnError))
  68. {
  69. Params |= mpAllowContinueOnError;
  70. }
  71. }
  72. }
  73. //---------------------------------------------------------------------------
  74. inline void TMessageParams::Reset()
  75. {
  76. Params = 0;
  77. Aliases = NULL;
  78. AliasesCount = 0;
  79. Timer = 0;
  80. TimerEvent = NULL;
  81. TimerMessage = L"";
  82. TimerAnswers = 0;
  83. TimerQueryType = static_cast<TQueryType>(-1);
  84. Timeout = 0;
  85. TimeoutAnswer = 0;
  86. NeverAskAgainTitle = L"";
  87. NeverAskAgainAnswer = 0;
  88. NeverAskAgainCheckedInitially = false;
  89. AllowHelp = true;
  90. ImageName = L"";
  91. MoreMessagesUrl = L"";
  92. MoreMessagesSize = TSize();
  93. CustomCaption = L"";
  94. }
  95. //---------------------------------------------------------------------------
  96. static bool __fastcall IsPositiveAnswer(unsigned int Answer)
  97. {
  98. return (Answer == qaYes) || (Answer == qaOK) || (Answer == qaYesToAll);
  99. }
  100. //---------------------------------------------------------------------------
  101. static void __fastcall NeverAskAgainCheckClick(void * /*Data*/, TObject * Sender)
  102. {
  103. TCheckBox * CheckBox = dynamic_cast<TCheckBox *>(Sender);
  104. DebugAssert(CheckBox != NULL);
  105. TForm * Dialog = dynamic_cast<TForm *>(CheckBox->Owner);
  106. DebugAssert(Dialog != NULL);
  107. unsigned int PositiveAnswer = 0;
  108. if (CheckBox->Checked)
  109. {
  110. if (CheckBox->Tag > 0)
  111. {
  112. PositiveAnswer = CheckBox->Tag;
  113. }
  114. else
  115. {
  116. for (int ii = 0; ii < Dialog->ControlCount; ii++)
  117. {
  118. TButton * Button = dynamic_cast<TButton *>(Dialog->Controls[ii]);
  119. if (Button != NULL)
  120. {
  121. if (IsPositiveAnswer(Button->ModalResult))
  122. {
  123. PositiveAnswer = Button->ModalResult;
  124. break;
  125. }
  126. }
  127. }
  128. }
  129. DebugAssert(PositiveAnswer != 0);
  130. }
  131. for (int ii = 0; ii < Dialog->ControlCount; ii++)
  132. {
  133. TButton * Button = dynamic_cast<TButton *>(Dialog->Controls[ii]);
  134. if (Button != NULL)
  135. {
  136. if ((Button->ModalResult != 0) && (Button->ModalResult != static_cast<int>(qaCancel)))
  137. {
  138. Button->Enabled = !CheckBox->Checked || (Button->ModalResult == static_cast<int>(PositiveAnswer));
  139. }
  140. if (Button->DropDownMenu != NULL)
  141. {
  142. for (int iii = 0; iii < Button->DropDownMenu->Items->Count; iii++)
  143. {
  144. TMenuItem * Item = Button->DropDownMenu->Items->Items[iii];
  145. Item->Enabled = Item->Default || !CheckBox->Checked;
  146. }
  147. }
  148. }
  149. }
  150. }
  151. //---------------------------------------------------------------------------
  152. static TCheckBox * __fastcall FindNeverAskAgainCheck(TForm * Dialog)
  153. {
  154. return DebugNotNull(dynamic_cast<TCheckBox *>(Dialog->FindComponent(L"NeverAskAgainCheck")));
  155. }
  156. //---------------------------------------------------------------------------
  157. TForm * __fastcall CreateMessageDialogEx(const UnicodeString Msg,
  158. TStrings * MoreMessages, TQueryType Type, unsigned int Answers, UnicodeString HelpKeyword,
  159. const TMessageParams * Params, TButton *& TimeoutButton)
  160. {
  161. TMsgDlgType DlgType;
  162. switch (Type) {
  163. case qtConfirmation: DlgType = mtConfirmation; break;
  164. case qtInformation: DlgType = mtInformation; break;
  165. case qtError: DlgType = mtError; break;
  166. case qtWarning: DlgType = mtWarning; break;
  167. default: DebugFail();
  168. }
  169. unsigned int TimeoutAnswer = (Params != NULL) ? Params->TimeoutAnswer : 0;
  170. unsigned int ActualAnswers = Answers;
  171. if ((Params == NULL) || Params->AllowHelp)
  172. {
  173. Answers = Answers | qaHelp;
  174. }
  175. if (IsInternalErrorHelpKeyword(HelpKeyword))
  176. {
  177. Answers = Answers | qaReport;
  178. }
  179. if ((MoreMessages != NULL) && (MoreMessages->Count == 0))
  180. {
  181. MoreMessages = NULL;
  182. }
  183. UnicodeString ImageName;
  184. UnicodeString MoreMessagesUrl;
  185. TSize MoreMessagesSize;
  186. UnicodeString CustomCaption;
  187. if (Params != NULL)
  188. {
  189. ImageName = Params->ImageName;
  190. MoreMessagesUrl = Params->MoreMessagesUrl;
  191. MoreMessagesSize = Params->MoreMessagesSize;
  192. CustomCaption = Params->CustomCaption;
  193. }
  194. const TQueryButtonAlias * Aliases = (Params != NULL) ? Params->Aliases : NULL;
  195. unsigned int AliasesCount = (Params != NULL) ? Params->AliasesCount : 0;
  196. UnicodeString NeverAskAgainCaption;
  197. bool HasNeverAskAgain = (Params != NULL) && FLAGSET(Params->Params, mpNeverAskAgainCheck);
  198. if (HasNeverAskAgain)
  199. {
  200. NeverAskAgainCaption =
  201. !Params->NeverAskAgainTitle.IsEmpty() ?
  202. (UnicodeString)Params->NeverAskAgainTitle :
  203. // qaOK | qaIgnore is used, when custom "non-answer" button is required
  204. LoadStr(((ActualAnswers == qaOK) || (ActualAnswers == (qaOK | qaIgnore))) ?
  205. NEVER_SHOW_AGAIN : NEVER_ASK_AGAIN);
  206. }
  207. TForm * Dialog = CreateMoreMessageDialog(Msg, MoreMessages, DlgType, Answers,
  208. Aliases, AliasesCount, TimeoutAnswer, &TimeoutButton, ImageName, NeverAskAgainCaption,
  209. MoreMessagesUrl, MoreMessagesSize, CustomCaption);
  210. try
  211. {
  212. if (HasNeverAskAgain && DebugAlwaysTrue(Params != NULL))
  213. {
  214. TCheckBox * NeverAskAgainCheck = FindNeverAskAgainCheck(Dialog);
  215. NeverAskAgainCheck->Checked = Params->NeverAskAgainCheckedInitially;
  216. if (Params->NeverAskAgainAnswer > 0)
  217. {
  218. NeverAskAgainCheck->Tag = Params->NeverAskAgainAnswer;
  219. }
  220. TNotifyEvent OnClick;
  221. ((TMethod*)&OnClick)->Code = NeverAskAgainCheckClick;
  222. NeverAskAgainCheck->OnClick = OnClick;
  223. }
  224. Dialog->HelpKeyword = HelpKeyword;
  225. if (FLAGSET(Answers, qaHelp))
  226. {
  227. Dialog->BorderIcons = Dialog->BorderIcons << biHelp;
  228. }
  229. ResetSystemSettings(Dialog);
  230. }
  231. catch(...)
  232. {
  233. delete Dialog;
  234. throw;
  235. }
  236. return Dialog;
  237. }
  238. //---------------------------------------------------------------------------
  239. unsigned int __fastcall ExecuteMessageDialog(TForm * Dialog, unsigned int Answers, const TMessageParams * Params)
  240. {
  241. FlashOnBackground();
  242. unsigned int Answer = Dialog->ShowModal();
  243. // mrCancel is returned always when X button is pressed, despite
  244. // no Cancel button was on the dialog. Find valid "cancel" answer.
  245. // mrNone is returned when Windows session is closing (log off)
  246. if ((Answer == mrCancel) || (Answer == mrNone))
  247. {
  248. Answer = CancelAnswer(Answers);
  249. }
  250. if ((Params != NULL) && (Params->Params & mpNeverAskAgainCheck))
  251. {
  252. TCheckBox * NeverAskAgainCheck = FindNeverAskAgainCheck(Dialog);
  253. if (NeverAskAgainCheck->Checked)
  254. {
  255. bool PositiveAnswer =
  256. (Params->NeverAskAgainAnswer > 0) ?
  257. (Answer == Params->NeverAskAgainAnswer) :
  258. IsPositiveAnswer(Answer);
  259. if (PositiveAnswer)
  260. {
  261. Answer = qaNeverAskAgain;
  262. }
  263. }
  264. }
  265. return Answer;
  266. }
  267. //---------------------------------------------------------------------------
  268. class TMessageTimer : public TTimer
  269. {
  270. public:
  271. TQueryParamsTimerEvent Event;
  272. TForm * Dialog;
  273. __fastcall TMessageTimer(TComponent * AOwner);
  274. protected:
  275. void __fastcall DoTimer(TObject * Sender);
  276. };
  277. //---------------------------------------------------------------------------
  278. __fastcall TMessageTimer::TMessageTimer(TComponent * AOwner) : TTimer(AOwner)
  279. {
  280. Event = NULL;
  281. OnTimer = DoTimer;
  282. Dialog = NULL;
  283. }
  284. //---------------------------------------------------------------------------
  285. void __fastcall TMessageTimer::DoTimer(TObject * /*Sender*/)
  286. {
  287. if (Event != NULL)
  288. {
  289. unsigned int Result = 0;
  290. Event(Result);
  291. if (Result != 0)
  292. {
  293. Dialog->ModalResult = Result;
  294. }
  295. }
  296. }
  297. //---------------------------------------------------------------------------
  298. class TMessageTimeout : public TTimer
  299. {
  300. public:
  301. __fastcall TMessageTimeout(TComponent * AOwner, unsigned int Timeout, TButton * Button);
  302. protected:
  303. unsigned int FOrigTimeout;
  304. unsigned int FTimeout;
  305. TButton * FButton;
  306. UnicodeString FOrigCaption;
  307. TPoint FOrigCursorPos;
  308. std::unique_ptr<TApplicationEvents> FApplicationEvents;
  309. void __fastcall DoTimer(TObject * Sender);
  310. void __fastcall UpdateButton();
  311. void __fastcall ApplicationMessage(TMsg & Msg, bool & Handled);
  312. void __fastcall MouseMove();
  313. void __fastcall Cancel();
  314. };
  315. //---------------------------------------------------------------------------
  316. __fastcall TMessageTimeout::TMessageTimeout(TComponent * AOwner,
  317. unsigned int Timeout, TButton * Button) :
  318. TTimer(AOwner), FOrigTimeout(Timeout), FTimeout(Timeout), FButton(Button)
  319. {
  320. OnTimer = DoTimer;
  321. Interval = MSecsPerSec;
  322. FOrigCaption = FButton->Caption;
  323. FOrigCursorPos = Mouse->CursorPos;
  324. FApplicationEvents.reset(new TApplicationEvents(Application));
  325. FApplicationEvents->OnMessage = ApplicationMessage;
  326. UpdateButton();
  327. }
  328. //---------------------------------------------------------------------------
  329. void __fastcall TMessageTimeout::ApplicationMessage(TMsg & Msg, bool & DebugUsedArg(Handled))
  330. {
  331. if (Msg.message == WM_MOUSEMOVE)
  332. {
  333. MouseMove();
  334. }
  335. else if ((Msg.message == WM_LBUTTONDOWN) || (Msg.message == WM_RBUTTONDOWN) ||
  336. (Msg.message == WM_KEYDOWN) || (Msg.message == WM_SYSKEYDOWN))
  337. {
  338. Cancel();
  339. }
  340. }
  341. //---------------------------------------------------------------------------
  342. void __fastcall TMessageTimeout::MouseMove()
  343. {
  344. TPoint CursorPos = Mouse->CursorPos;
  345. int Delta = std::max(std::abs(FOrigCursorPos.X - CursorPos.X), std::abs(FOrigCursorPos.Y - CursorPos.Y));
  346. int Threshold = 8;
  347. if (DebugAlwaysTrue(FButton != NULL))
  348. {
  349. Threshold = ScaleByTextHeight(FButton, Threshold);
  350. }
  351. if (Delta > Threshold)
  352. {
  353. FOrigCursorPos = CursorPos;
  354. const unsigned int SuspendTime = 30 * MSecsPerSec;
  355. FTimeout = std::max(FOrigTimeout, SuspendTime);
  356. UpdateButton();
  357. }
  358. }
  359. //---------------------------------------------------------------------------
  360. void __fastcall TMessageTimeout::Cancel()
  361. {
  362. Enabled = false;
  363. UpdateButton();
  364. }
  365. //---------------------------------------------------------------------------
  366. void __fastcall TMessageTimeout::UpdateButton()
  367. {
  368. DebugAssert(FButton != NULL);
  369. FButton->Caption =
  370. !Enabled ? FOrigCaption : FMTLOAD(TIMEOUT_BUTTON, (FOrigCaption, int(FTimeout / MSecsPerSec)));
  371. }
  372. //---------------------------------------------------------------------------
  373. void __fastcall TMessageTimeout::DoTimer(TObject * /*Sender*/)
  374. {
  375. if (FTimeout <= Interval)
  376. {
  377. DebugAssert(FButton != NULL);
  378. // Needed particularly for "keep up to date" dialog, which does not close on the button click
  379. Enabled = false;
  380. FButton->Click();
  381. }
  382. else
  383. {
  384. FTimeout -= Interval;
  385. UpdateButton();
  386. }
  387. }
  388. //---------------------------------------------------------------------------
  389. void InitiateDialogTimeout(TForm * Dialog, unsigned int Timeout, TButton * Button)
  390. {
  391. TMessageTimeout * MessageTimeout = new TMessageTimeout(Application, Timeout, Button);
  392. MessageTimeout->Name = L"MessageTimeout";
  393. Dialog->InsertComponent(MessageTimeout);
  394. }
  395. //---------------------------------------------------------------------
  396. class TPublicControl : public TControl
  397. {
  398. friend void __fastcall MenuPopup(TObject * Sender, const TPoint & MousePos, bool & Handled);
  399. };
  400. //---------------------------------------------------------------------------
  401. // Merge with CreateMessageDialogEx
  402. TForm * __fastcall CreateMoreMessageDialogEx(const UnicodeString Message, TStrings * MoreMessages,
  403. TQueryType Type, unsigned int Answers, UnicodeString HelpKeyword, const TMessageParams * Params)
  404. {
  405. std::unique_ptr<TForm> Dialog;
  406. UnicodeString AMessage = Message;
  407. TMessageTimer * Timer = NULL;
  408. if ((Params != NULL) && (Params->Timer > 0))
  409. {
  410. Timer = new TMessageTimer(Application);
  411. Timer->Interval = Params->Timer;
  412. Timer->Event = Params->TimerEvent;
  413. if (Params->TimerAnswers > 0)
  414. {
  415. Answers = Params->TimerAnswers;
  416. }
  417. if (Params->TimerQueryType >= 0)
  418. {
  419. Type = Params->TimerQueryType;
  420. }
  421. if (!Params->TimerMessage.IsEmpty())
  422. {
  423. AMessage = Params->TimerMessage;
  424. }
  425. Timer->Name = L"MessageTimer";
  426. }
  427. TButton * TimeoutButton = NULL;
  428. Dialog.reset(
  429. CreateMessageDialogEx(
  430. AMessage, MoreMessages, Type, Answers, HelpKeyword, Params, TimeoutButton));
  431. if (Timer != NULL)
  432. {
  433. Timer->Dialog = Dialog.get();
  434. Dialog->InsertComponent(Timer);
  435. }
  436. if (Params != NULL)
  437. {
  438. if (Params->Timeout > 0)
  439. {
  440. InitiateDialogTimeout(Dialog.get(), Params->Timeout, TimeoutButton);
  441. }
  442. }
  443. return Dialog.release();
  444. }
  445. //---------------------------------------------------------------------------
  446. unsigned int __fastcall MoreMessageDialog(const UnicodeString Message, TStrings * MoreMessages,
  447. TQueryType Type, unsigned int Answers, UnicodeString HelpKeyword, const TMessageParams * Params)
  448. {
  449. std::unique_ptr<TForm> Dialog(CreateMoreMessageDialogEx(Message, MoreMessages, Type, Answers, HelpKeyword, Params));
  450. unsigned int Result = ExecuteMessageDialog(Dialog.get(), Answers, Params);
  451. return Result;
  452. }
  453. //---------------------------------------------------------------------------
  454. unsigned int __fastcall MessageDialog(const UnicodeString Msg, TQueryType Type,
  455. unsigned int Answers, UnicodeString HelpKeyword, const TMessageParams * Params)
  456. {
  457. return MoreMessageDialog(Msg, NULL, Type, Answers, HelpKeyword, Params);
  458. }
  459. //---------------------------------------------------------------------------
  460. unsigned int __fastcall SimpleErrorDialog(const UnicodeString Msg, const UnicodeString MoreMessages)
  461. {
  462. unsigned int Result;
  463. TStrings * More = NULL;
  464. try
  465. {
  466. if (!MoreMessages.IsEmpty())
  467. {
  468. More = TextToStringList(MoreMessages);
  469. }
  470. Result = MoreMessageDialog(Msg, More, qtError, qaOK, HELP_NONE);
  471. }
  472. __finally
  473. {
  474. delete More;
  475. }
  476. return Result;
  477. }
  478. //---------------------------------------------------------------------------
  479. static TStrings * __fastcall StackInfoListToStrings(
  480. TJclStackInfoList * StackInfoList)
  481. {
  482. std::unique_ptr<TStrings> StackTrace(new TStringList());
  483. StackInfoList->AddToStrings(StackTrace.get(), true, false, true, true);
  484. for (int Index = 0; Index < StackTrace->Count; Index++)
  485. {
  486. UnicodeString Frame = StackTrace->Strings[Index];
  487. // get rid of declarations "flags" that are included in .map
  488. Frame = ReplaceStr(Frame, L"__fastcall ", L"");
  489. Frame = ReplaceStr(Frame, L"__linkproc__ ", L"");
  490. if (DebugAlwaysTrue(!Frame.IsEmpty() && (Frame[1] == L'(')))
  491. {
  492. int Start = Frame.Pos(L"[");
  493. int End = Frame.Pos(L"]");
  494. if (DebugAlwaysTrue((Start > 1) && (End > Start) && (Frame[Start - 1] == L' ')))
  495. {
  496. // remove absolute address
  497. Frame.Delete(Start - 1, End - Start + 2);
  498. }
  499. }
  500. StackTrace->Strings[Index] = Frame;
  501. }
  502. return StackTrace.release();
  503. }
  504. //---------------------------------------------------------------------------
  505. static std::unique_ptr<TCriticalSection> StackTraceCriticalSection(TraceInitPtr(new TCriticalSection()));
  506. typedef std::map<DWORD, TStrings *> TStackTraceMap;
  507. static TStackTraceMap StackTraceMap;
  508. //---------------------------------------------------------------------------
  509. UnicodeString __fastcall GetExceptionDebugInfo()
  510. {
  511. UnicodeString Result;
  512. TGuard Guard(StackTraceCriticalSection.get());
  513. TStackTraceMap::iterator Iterator = StackTraceMap.find(GetCurrentThreadId());
  514. if (Iterator != StackTraceMap.end())
  515. {
  516. TStrings * StackTrace = Iterator->second;
  517. for (int Index = 0; Index < StackTrace->Count; Index++)
  518. {
  519. UnicodeString Frame = StackTrace->Strings[Index];
  520. // The last line might be empty
  521. if (!Frame.IsEmpty())
  522. {
  523. int P = Frame.Pos(L")");
  524. if (DebugAlwaysTrue(P > 0))
  525. {
  526. UnicodeString Symbol = Frame.SubString(P + 1, Frame.Length() - P).Trim();
  527. if ((Symbol != L"KERNELBASE.dll.RaiseException") &&
  528. (Symbol != L"Jclhookexcept::JclAddExceptNotifier") &&
  529. (Symbol != L"_ReThrowException") &&
  530. (Symbol != L"____ExceptionHandler") &&
  531. (Symbol != L"__ExceptionHandler") &&
  532. (Symbol != L"___doGlobalUnwind") &&
  533. (Symbol != L"_ThrowExceptionLDTC"))
  534. {
  535. AddToList(Result, Symbol, L";");
  536. }
  537. }
  538. }
  539. }
  540. }
  541. return Result;
  542. }
  543. //---------------------------------------------------------------------------
  544. bool __fastcall AppendExceptionStackTraceAndForget(TStrings *& MoreMessages)
  545. {
  546. bool Result = false;
  547. TGuard Guard(StackTraceCriticalSection.get());
  548. TStackTraceMap::iterator Iterator = StackTraceMap.find(GetCurrentThreadId());
  549. if (Iterator != StackTraceMap.end())
  550. {
  551. std::unique_ptr<TStrings> OwnedMoreMessages;
  552. if (MoreMessages == NULL)
  553. {
  554. OwnedMoreMessages.reset(new TStringList());
  555. MoreMessages = OwnedMoreMessages.get();
  556. Result = true;
  557. }
  558. if (!MoreMessages->Text.IsEmpty())
  559. {
  560. MoreMessages->Text = MoreMessages->Text + "\n";
  561. }
  562. MoreMessages->Text = MoreMessages->Text + LoadStr(STACK_TRACE) + "\n";
  563. MoreMessages->AddStrings(Iterator->second);
  564. delete Iterator->second;
  565. StackTraceMap.erase(Iterator);
  566. OwnedMoreMessages.release();
  567. }
  568. return Result;
  569. }
  570. //---------------------------------------------------------------------------
  571. unsigned int __fastcall ExceptionMessageDialog(Exception * E, TQueryType Type,
  572. const UnicodeString MessageFormat, unsigned int Answers, UnicodeString HelpKeyword,
  573. const TMessageParams * Params)
  574. {
  575. TStrings * MoreMessages = NULL;
  576. ExtException * EE = dynamic_cast<ExtException *>(E);
  577. if (EE != NULL)
  578. {
  579. MoreMessages = EE->MoreMessages;
  580. }
  581. UnicodeString Message;
  582. // this is always called from within ExceptionMessage check,
  583. // so it should never fail here
  584. DebugCheck(ExceptionMessageFormatted(E, Message));
  585. if (!MessageFormat.IsEmpty())
  586. {
  587. Message = FORMAT(MessageFormat, (UnformatMessage(Message)));
  588. }
  589. HelpKeyword = MergeHelpKeyword(HelpKeyword, GetExceptionHelpKeyword(E));
  590. std::unique_ptr<TStrings> OwnedMoreMessages;
  591. if (AppendExceptionStackTraceAndForget(MoreMessages))
  592. {
  593. OwnedMoreMessages.reset(MoreMessages);
  594. }
  595. return MoreMessageDialog(
  596. Message, MoreMessages, Type, Answers, HelpKeyword, Params);
  597. }
  598. //---------------------------------------------------------------------------
  599. unsigned int __fastcall FatalExceptionMessageDialog(Exception * E, TQueryType Type,
  600. int SessionReopenTimeout, const UnicodeString MessageFormat, unsigned int Answers,
  601. UnicodeString HelpKeyword, const TMessageParams * Params)
  602. {
  603. DebugAssert(FLAGCLEAR(Answers, qaRetry));
  604. Answers |= qaRetry;
  605. TQueryButtonAlias Aliases[1];
  606. Aliases[0].Button = qaRetry;
  607. Aliases[0].Alias = LoadStr(RECONNECT_BUTTON);
  608. TMessageParams AParams;
  609. if (Params != NULL)
  610. {
  611. AParams = *Params;
  612. }
  613. DebugAssert(AParams.Timeout == 0);
  614. // the condition is de facto excess
  615. if (SessionReopenTimeout > 0)
  616. {
  617. AParams.Timeout = SessionReopenTimeout;
  618. AParams.TimeoutAnswer = qaRetry;
  619. }
  620. DebugAssert(AParams.Aliases == NULL);
  621. AParams.Aliases = Aliases;
  622. AParams.AliasesCount = LENOF(Aliases);
  623. return ExceptionMessageDialog(E, Type, MessageFormat, Answers, HelpKeyword, &AParams);
  624. }
  625. //---------------------------------------------------------------------------
  626. //---------------------------------------------------------------------------
  627. static void __fastcall DoExceptNotify(TObject * ExceptObj, void * ExceptAddr,
  628. bool OSException, void * BaseOfStack)
  629. {
  630. if (ExceptObj != NULL)
  631. {
  632. Exception * E = dynamic_cast<Exception *>(ExceptObj);
  633. if ((E != NULL) && IsInternalException(E))
  634. {
  635. DoExceptionStackTrace(ExceptObj, ExceptAddr, OSException, BaseOfStack);
  636. TJclStackInfoList * StackInfoList = JclLastExceptStackList();
  637. if (DebugAlwaysTrue(StackInfoList != NULL))
  638. {
  639. std::unique_ptr<TStrings> StackTrace(StackInfoListToStrings(StackInfoList));
  640. DWORD ThreadID = GetCurrentThreadId();
  641. TGuard Guard(StackTraceCriticalSection.get());
  642. TStackTraceMap::iterator Iterator = StackTraceMap.find(ThreadID);
  643. if (Iterator != StackTraceMap.end())
  644. {
  645. Iterator->second->Add(L"");
  646. Iterator->second->AddStrings(StackTrace.get());
  647. }
  648. else
  649. {
  650. StackTraceMap.insert(std::make_pair(ThreadID, StackTrace.release()));
  651. }
  652. // this chains so that JclLastExceptStackList() returns NULL the next time
  653. // for the current thread
  654. delete StackInfoList;
  655. }
  656. }
  657. }
  658. }
  659. //---------------------------------------------------------------------------
  660. void * __fastcall BusyStart()
  661. {
  662. void * Token = reinterpret_cast<void *>(Screen->Cursor);
  663. Screen->Cursor = crHourGlass;
  664. return Token;
  665. }
  666. //---------------------------------------------------------------------------
  667. void __fastcall BusyEnd(void * Token)
  668. {
  669. Screen->Cursor = reinterpret_cast<TCursor>(Token);
  670. }
  671. //---------------------------------------------------------------------------
  672. //---------------------------------------------------------------------------
  673. static DWORD MainThread = 0;
  674. static TDateTime LastGUIUpdate = 0;
  675. static double GUIUpdateIntervalFrac = static_cast<double>(OneSecond/1000*GUIUpdateInterval); // 1/5 sec
  676. static bool NoGUI = false;
  677. //---------------------------------------------------------------------------
  678. void __fastcall SetNoGUI()
  679. {
  680. NoGUI = true;
  681. }
  682. //---------------------------------------------------------------------------
  683. bool __fastcall ProcessGUI(bool Force)
  684. {
  685. DebugAssert(MainThread != 0);
  686. bool Result = false;
  687. // Calling ProcessMessages in Azure WebJob causes access violation in VCL.
  688. // As we do not really need to call it in scripting/.NET, just skip it.
  689. if ((MainThread == GetCurrentThreadId()) && !NoGUI)
  690. {
  691. TDateTime N = Now();
  692. if (Force ||
  693. (double(N) - double(LastGUIUpdate) > GUIUpdateIntervalFrac))
  694. {
  695. LastGUIUpdate = N;
  696. Application->ProcessMessages();
  697. Result = true;
  698. }
  699. }
  700. return Result;
  701. }
  702. //---------------------------------------------------------------------------
  703. void __fastcall CopyParamListButton(TButton * Button)
  704. {
  705. if (!SupportsSplitButton())
  706. {
  707. MenuButton(Button);
  708. }
  709. }
  710. //---------------------------------------------------------------------------
  711. const int cpiDefault = -1;
  712. const int cpiConfigure = -2;
  713. const int cpiCustom = -3;
  714. const int cpiSaveSettings = -4;
  715. const int cpiGenerateCode = -5;
  716. const int cpiSavePreset = -6;
  717. //---------------------------------------------------------------------------
  718. void __fastcall CopyParamListPopup(TRect Rect, TPopupMenu * Menu,
  719. const TCopyParamType & Param, UnicodeString Preset, TNotifyEvent OnClick,
  720. int Options, int CopyParamAttrs, bool SaveSettings)
  721. {
  722. Menu->Items->Clear();
  723. TMenuItem * Item;
  724. Item = new TMenuItem(Menu);
  725. Item->Caption = LoadStr(COPY_PARAM_CUSTOM);
  726. Item->Tag = cpiCustom;
  727. Item->Default = FLAGSET(Options, cplCustomizeDefault);
  728. Item->OnClick = OnClick;
  729. Menu->Items->Add(Item);
  730. TMenuItem * CustomizeItem = Item;
  731. if (FLAGSET(Options, cplSaveSettings))
  732. {
  733. Item = new TMenuItem(Menu);
  734. Item->Caption = LoadStr(COPY_PARAM_SAVE_SETTINGS);
  735. Item->Tag = cpiSaveSettings;
  736. Item->Checked = SaveSettings;
  737. Item->OnClick = OnClick;
  738. Menu->Items->Add(Item);
  739. }
  740. Item = new TMenuItem(Menu);
  741. Item->Caption = LoadStr(COPY_PARAM_SAVE_PRESET);
  742. Item->Tag = cpiSavePreset;
  743. Item->OnClick = OnClick;
  744. Menu->Items->Add(Item);
  745. Item = new TMenuItem(Menu);
  746. Item->Caption = LoadStr(COPY_PARAM_PRESET_HEADER);
  747. Item->Visible = false;
  748. Item->Enabled = false;
  749. Menu->Items->Add(Item);
  750. bool AnyChecked = false;
  751. Item = new TMenuItem(Menu);
  752. Item->Caption = LoadStr(COPY_PARAM_DEFAULT);
  753. Item->Tag = cpiDefault;
  754. Item->Checked =
  755. Preset.IsEmpty() && (GUIConfiguration->CopyParamPreset[L""] == Param);
  756. AnyChecked = AnyChecked || Item->Checked;
  757. Item->OnClick = OnClick;
  758. Menu->Items->Add(Item);
  759. TCopyParamType DefaultParam;
  760. const TCopyParamList * CopyParamList = GUIConfiguration->CopyParamList;
  761. for (int i = 0; i < CopyParamList->Count; i++)
  762. {
  763. UnicodeString Name = CopyParamList->Names[i];
  764. TCopyParamType AParam = GUIConfiguration->CopyParamPreset[Name];
  765. if (AParam.AnyUsableCopyParam(CopyParamAttrs) ||
  766. // This makes "Binary" preset visible,
  767. // as long as we care about transfer mode
  768. ((AParam == DefaultParam) &&
  769. FLAGCLEAR(CopyParamAttrs, cpaIncludeMaskOnly) &&
  770. FLAGCLEAR(CopyParamAttrs, cpaNoTransferMode)))
  771. {
  772. Item = new TMenuItem(Menu);
  773. Item->Caption = Name;
  774. Item->Tag = i;
  775. Item->Checked =
  776. (Preset == Name) && (AParam == Param);
  777. AnyChecked = AnyChecked || Item->Checked;
  778. Item->OnClick = OnClick;
  779. Menu->Items->Add(Item);
  780. }
  781. }
  782. CustomizeItem->Checked = !AnyChecked;
  783. Item = new TMenuItem(Menu);
  784. Item->Caption = L"-";
  785. Menu->Items->Add(Item);
  786. Item = new TMenuItem(Menu);
  787. Item->Caption = LoadStr(COPY_PARAM_CONFIGURE);
  788. Item->Tag = cpiConfigure;
  789. Item->OnClick = OnClick;
  790. Menu->Items->Add(Item);
  791. if (FLAGSET(Options, cplGenerateCode))
  792. {
  793. Item = new TMenuItem(Menu);
  794. Item->Caption = L"-";
  795. Menu->Items->Add(Item);
  796. Item = new TMenuItem(Menu);
  797. Item->Caption = LoadStr(COPY_PARAM_GENERATE_CODE);
  798. Item->Tag = cpiGenerateCode;
  799. Item->OnClick = OnClick;
  800. Menu->Items->Add(Item);
  801. }
  802. MenuPopup(Menu, Rect, NULL);
  803. }
  804. //---------------------------------------------------------------------------
  805. int __fastcall CopyParamListPopupClick(TObject * Sender,
  806. TCopyParamType & Param, UnicodeString & Preset, int CopyParamAttrs,
  807. bool * SaveSettings)
  808. {
  809. TComponent * Item = dynamic_cast<TComponent *>(Sender);
  810. DebugAssert(Item != NULL);
  811. DebugAssert((Item->Tag >= cpiSavePreset) && (Item->Tag < GUIConfiguration->CopyParamList->Count));
  812. int Result;
  813. if (Item->Tag == cpiConfigure)
  814. {
  815. bool MatchedPreset = (GUIConfiguration->CopyParamPreset[Preset] == Param);
  816. DoPreferencesDialog(pmPresets);
  817. Result = (MatchedPreset && GUIConfiguration->HasCopyParamPreset[Preset]) ? 1 : 0;
  818. if (Result > 0)
  819. {
  820. // For cast, see a comment below
  821. Param = TCopyParamType(GUIConfiguration->CopyParamPreset[Preset]);
  822. }
  823. }
  824. else if (Item->Tag == cpiCustom)
  825. {
  826. Result = DoCopyParamCustomDialog(Param, CopyParamAttrs) ? 1 : 0;
  827. }
  828. else if (Item->Tag == cpiSaveSettings)
  829. {
  830. if (DebugAlwaysTrue(SaveSettings != NULL))
  831. {
  832. *SaveSettings = !*SaveSettings;
  833. }
  834. Result = 0;
  835. }
  836. else if (Item->Tag == cpiSavePreset)
  837. {
  838. std::unique_ptr<TCopyParamList> CopyParamList(new TCopyParamList());
  839. *CopyParamList = *GUIConfiguration->CopyParamList;
  840. int Index = -1;
  841. if (DoCopyParamPresetDialog(CopyParamList.get(), Index, cpmAdd, NULL, Param))
  842. {
  843. GUIConfiguration->CopyParamList = CopyParamList.get();
  844. // If saved unmodified, then make this the selected preset
  845. if (*CopyParamList->CopyParams[Index] == Param)
  846. {
  847. Preset = CopyParamList->Names[Index];
  848. }
  849. }
  850. Result = 0;
  851. }
  852. else if (Item->Tag == cpiGenerateCode)
  853. {
  854. Result = -cplGenerateCode;
  855. }
  856. else
  857. {
  858. Preset = (Item->Tag >= 0) ?
  859. GUIConfiguration->CopyParamList->Names[Item->Tag] : UnicodeString();
  860. // The cast strips away the "queue" properties of the TGUICopyParamType
  861. // that are not configurable in presets
  862. Param = TCopyParamType(GUIConfiguration->CopyParamPreset[Preset]);
  863. Result = 1;
  864. }
  865. return Result;
  866. }
  867. //---------------------------------------------------------------------------
  868. //---------------------------------------------------------------------------
  869. class TCustomCommandPromptsDialog : public TCustomDialog
  870. {
  871. public:
  872. __fastcall TCustomCommandPromptsDialog(
  873. const UnicodeString & CustomCommandName, const UnicodeString & HelpKeyword,
  874. const TUnicodeStringVector & Prompts, const TUnicodeStringVector & Defaults);
  875. bool __fastcall Execute(TUnicodeStringVector & Values);
  876. private:
  877. UnicodeString __fastcall HistoryKey(int Index);
  878. std::vector<THistoryComboBox *> FEdits;
  879. TUnicodeStringVector FPrompts;
  880. UnicodeString FCustomCommandName;
  881. };
  882. //---------------------------------------------------------------------------
  883. __fastcall TCustomCommandPromptsDialog::TCustomCommandPromptsDialog(
  884. const UnicodeString & CustomCommandName, const UnicodeString & HelpKeyword,
  885. const TUnicodeStringVector & Prompts, const TUnicodeStringVector & Defaults) :
  886. TCustomDialog(HelpKeyword)
  887. {
  888. FCustomCommandName = CustomCommandName;
  889. Caption = FMTLOAD(CUSTOM_COMMANDS_PARAMS_TITLE, (FCustomCommandName));
  890. FPrompts = Prompts;
  891. DebugAssert(FPrompts.size() == Defaults.size());
  892. for (size_t Index = 0; Index < FPrompts.size(); Index++)
  893. {
  894. UnicodeString Prompt = FPrompts[Index];
  895. if (Prompt.IsEmpty())
  896. {
  897. Prompt = LoadStr(CUSTOM_COMMANDS_PARAM_PROMPT2);
  898. }
  899. THistoryComboBox * ComboBox = new THistoryComboBox(this);
  900. ComboBox->AutoComplete = false;
  901. AddComboBox(ComboBox, CreateLabel(Prompt));
  902. ComboBox->Items = CustomWinConfiguration->History[HistoryKey(Index)];
  903. ComboBox->Text = Defaults[Index];
  904. FEdits.push_back(ComboBox);
  905. }
  906. }
  907. //---------------------------------------------------------------------------
  908. UnicodeString __fastcall TCustomCommandPromptsDialog::HistoryKey(int Index)
  909. {
  910. UnicodeString Result = FPrompts[Index];
  911. if (Result.IsEmpty())
  912. {
  913. Result = IntToStr(Index);
  914. }
  915. Result = FORMAT(L"%s_%s", (FCustomCommandName, Result));
  916. Result = CustomWinConfiguration->GetValidHistoryKey(Result);
  917. return L"CustomCommandParam_" + Result;
  918. }
  919. //---------------------------------------------------------------------------
  920. bool __fastcall TCustomCommandPromptsDialog::Execute(TUnicodeStringVector & Values)
  921. {
  922. bool Result = TCustomDialog::Execute();
  923. if (Result)
  924. {
  925. for (size_t Index = 0; Index < FEdits.size(); Index++)
  926. {
  927. Values.push_back(FEdits[Index]->Text);
  928. FEdits[Index]->SaveToHistory();
  929. CustomWinConfiguration->History[HistoryKey(Index)] = FEdits[Index]->Items;
  930. }
  931. }
  932. return Result;
  933. }
  934. //---------------------------------------------------------------------------
  935. //---------------------------------------------------------------------------
  936. TWinInteractiveCustomCommand::TWinInteractiveCustomCommand(
  937. TCustomCommand * ChildCustomCommand, const UnicodeString CustomCommandName, const UnicodeString HelpKeyword) :
  938. TInteractiveCustomCommand(ChildCustomCommand)
  939. {
  940. FCustomCommandName = StripEllipsis(StripHotkey(CustomCommandName));
  941. FHelpKeyword = HelpKeyword;
  942. }
  943. //---------------------------------------------------------------------------
  944. void __fastcall TWinInteractiveCustomCommand::PatternHint(int Index, const UnicodeString & Pattern)
  945. {
  946. if (IsPromptPattern(Pattern))
  947. {
  948. UnicodeString Prompt;
  949. UnicodeString Default;
  950. bool Delimit = false;
  951. ParsePromptPattern(Pattern, Prompt, Default, Delimit);
  952. FIndexes.insert(std::make_pair(Index, FPrompts.size()));
  953. FPrompts.push_back(Prompt);
  954. FDefaults.push_back(Default);
  955. }
  956. }
  957. //---------------------------------------------------------------------------
  958. void __fastcall TWinInteractiveCustomCommand::Prompt(
  959. int Index, const UnicodeString & /*Prompt*/, UnicodeString & Value)
  960. {
  961. if (DebugAlwaysTrue(FIndexes.find(Index) != FIndexes.end()))
  962. {
  963. size_t PromptIndex = FIndexes[Index];
  964. if (FValues.empty())
  965. {
  966. UnicodeString HelpKeyword = FHelpKeyword;
  967. if (HelpKeyword.IsEmpty())
  968. {
  969. HelpKeyword = HELP_CUSTOM_COMMAND_PARAM;
  970. }
  971. std::unique_ptr<TCustomCommandPromptsDialog> Dialog(
  972. new TCustomCommandPromptsDialog(FCustomCommandName, HelpKeyword, FPrompts, FDefaults));
  973. if (!Dialog->Execute(FValues))
  974. {
  975. Abort();
  976. }
  977. }
  978. if (DebugAlwaysTrue(FValues.size() == FPrompts.size()) &&
  979. DebugAlwaysTrue(PromptIndex < FValues.size()))
  980. {
  981. Value = FValues[PromptIndex];
  982. }
  983. }
  984. }
  985. //---------------------------------------------------------------------------
  986. void __fastcall TWinInteractiveCustomCommand::Execute(
  987. const UnicodeString & Command, UnicodeString & Value)
  988. {
  989. // inspired by
  990. // http://forum.codecall.net/topic/72472-execute-a-console-program-and-capture-its-output/
  991. HANDLE StdOutOutput;
  992. HANDLE StdOutInput;
  993. HANDLE StdInOutput;
  994. HANDLE StdInInput;
  995. SECURITY_ATTRIBUTES SecurityAttributes;
  996. SecurityAttributes.nLength = sizeof(SecurityAttributes);
  997. SecurityAttributes.lpSecurityDescriptor = NULL;
  998. SecurityAttributes.bInheritHandle = TRUE;
  999. try
  1000. {
  1001. if (!CreatePipe(&StdOutOutput, &StdOutInput, &SecurityAttributes, 0))
  1002. {
  1003. throw Exception(FMTLOAD(SHELL_PATTERN_ERROR, (Command, L"out")));
  1004. }
  1005. else if (!CreatePipe(&StdInOutput, &StdInInput, &SecurityAttributes, 0))
  1006. {
  1007. throw Exception(FMTLOAD(SHELL_PATTERN_ERROR, (Command, L"in")));
  1008. }
  1009. else
  1010. {
  1011. STARTUPINFO StartupInfo;
  1012. PROCESS_INFORMATION ProcessInformation;
  1013. FillMemory(&StartupInfo, sizeof(StartupInfo), 0);
  1014. StartupInfo.cb = sizeof(StartupInfo);
  1015. StartupInfo.wShowWindow = SW_HIDE;
  1016. StartupInfo.hStdInput = StdInOutput;
  1017. StartupInfo.hStdOutput = StdOutInput;
  1018. StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  1019. if (!CreateProcess(NULL, Command.c_str(), &SecurityAttributes, &SecurityAttributes,
  1020. TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &StartupInfo, &ProcessInformation))
  1021. {
  1022. throw Exception(FMTLOAD(SHELL_PATTERN_ERROR, (Command, L"process")));
  1023. }
  1024. else
  1025. {
  1026. try
  1027. {
  1028. // wait until the console program terminated
  1029. bool Running = true;
  1030. while (Running)
  1031. {
  1032. switch (WaitForSingleObject(ProcessInformation.hProcess, 200))
  1033. {
  1034. case WAIT_TIMEOUT:
  1035. Application->ProcessMessages();
  1036. break;
  1037. case WAIT_OBJECT_0:
  1038. Running = false;
  1039. break;
  1040. default:
  1041. throw Exception(FMTLOAD(SHELL_PATTERN_ERROR, (Command, L"wait")));
  1042. }
  1043. }
  1044. char Buffer[1024];
  1045. unsigned long Read;
  1046. while (PeekNamedPipe(StdOutOutput, NULL, 0, NULL, &Read, NULL) &&
  1047. (Read > 0))
  1048. {
  1049. if (!ReadFile(StdOutOutput, &Buffer, Read, &Read, NULL))
  1050. {
  1051. throw Exception(FMTLOAD(SHELL_PATTERN_ERROR, (Command, L"read")));
  1052. }
  1053. else if (Read > 0)
  1054. {
  1055. Value += AnsiToString(Buffer, Read);
  1056. }
  1057. }
  1058. // trim trailing cr/lf
  1059. Value = TrimRight(Value);
  1060. }
  1061. __finally
  1062. {
  1063. CloseHandle(ProcessInformation.hProcess);
  1064. CloseHandle(ProcessInformation.hThread);
  1065. }
  1066. }
  1067. }
  1068. }
  1069. __finally
  1070. {
  1071. if (StdOutOutput != INVALID_HANDLE_VALUE)
  1072. {
  1073. CloseHandle(StdOutOutput);
  1074. }
  1075. if (StdOutInput != INVALID_HANDLE_VALUE)
  1076. {
  1077. CloseHandle(StdOutInput);
  1078. }
  1079. if (StdInOutput != INVALID_HANDLE_VALUE)
  1080. {
  1081. CloseHandle(StdInOutput);
  1082. }
  1083. if (StdInInput != INVALID_HANDLE_VALUE)
  1084. {
  1085. CloseHandle(StdInInput);
  1086. }
  1087. }
  1088. }
  1089. //---------------------------------------------------------------------------
  1090. void __fastcall MenuPopup(TPopupMenu * Menu, TButton * Button)
  1091. {
  1092. MenuPopup(Menu, CalculatePopupRect(Button), Button);
  1093. }
  1094. //---------------------------------------------------------------------------
  1095. void __fastcall MenuPopup(TObject * Sender, const TPoint & MousePos, bool & Handled)
  1096. {
  1097. TControl * Control = dynamic_cast<TControl *>(Sender);
  1098. DebugAssert(Control != NULL);
  1099. TPoint Point;
  1100. if ((MousePos.x == -1) && (MousePos.y == -1))
  1101. {
  1102. Point = Control->ClientToScreen(TPoint(0, 0));
  1103. }
  1104. else
  1105. {
  1106. Point = Control->ClientToScreen(MousePos);
  1107. }
  1108. TPopupMenu * PopupMenu = (reinterpret_cast<TPublicControl *>(Control))->PopupMenu;
  1109. DebugAssert(PopupMenu != NULL);
  1110. TRect Rect(Point, Point);
  1111. MenuPopup(PopupMenu, Rect, Control);
  1112. Handled = true;
  1113. }
  1114. //---------------------------------------------------------------------------
  1115. TComponent * __fastcall GetPopupComponent(TObject * Sender)
  1116. {
  1117. TComponent * Item = dynamic_cast<TComponent *>(Sender);
  1118. DebugAssert(Item != NULL);
  1119. TPopupMenu * PopupMenu = dynamic_cast<TPopupMenu *>(Item->Owner);
  1120. DebugAssert(PopupMenu != NULL);
  1121. DebugAssert(PopupMenu->PopupComponent != NULL);
  1122. return PopupMenu->PopupComponent;
  1123. }
  1124. //---------------------------------------------------------------------------
  1125. static void __fastcall SetMenuButtonImages(TButton * Button)
  1126. {
  1127. Button->Images = GetButtonImages(Button);
  1128. }
  1129. //---------------------------------------------------------------------------
  1130. static void __fastcall MenuButtonRescale(TComponent * Sender, TObject * /*Token*/)
  1131. {
  1132. TButton * Button = DebugNotNull(dynamic_cast<TButton *>(Sender));
  1133. SetMenuButtonImages(Button);
  1134. }
  1135. //---------------------------------------------------------------------------
  1136. void __fastcall MenuButton(TButton * Button)
  1137. {
  1138. SetMenuButtonImages(Button);
  1139. Button->ImageIndex = 0;
  1140. Button->DisabledImageIndex = 1;
  1141. Button->ImageAlignment = iaRight;
  1142. SetRescaleFunction(Button, MenuButtonRescale);
  1143. }
  1144. //---------------------------------------------------------------------------
  1145. TRect __fastcall CalculatePopupRect(TButton * Button)
  1146. {
  1147. TPoint UpPoint = Button->ClientToScreen(TPoint(0, 0));
  1148. TPoint DownPoint = Button->ClientToScreen(TPoint(Button->Width, Button->Height));
  1149. TRect Rect(UpPoint, DownPoint);
  1150. // With themes enabled, button are rendered 1 pixel smaller than their actual size
  1151. int Offset = UseThemes() ? -1 : 0;
  1152. Rect.Inflate(Offset, Offset);
  1153. return Rect;
  1154. }
  1155. //---------------------------------------------------------------------------
  1156. TRect __fastcall CalculatePopupRect(TControl * Control, TPoint MousePos)
  1157. {
  1158. MousePos = Control->ClientToScreen(MousePos);
  1159. TRect Rect(MousePos, MousePos);
  1160. return Rect;
  1161. }
  1162. //---------------------------------------------------------------------------
  1163. void __fastcall FixButtonImage(TButton * Button)
  1164. {
  1165. // with themes enabled, button image is by default drawn too high
  1166. if (UseThemes())
  1167. {
  1168. Button->ImageMargins->Top = 1;
  1169. }
  1170. }
  1171. //---------------------------------------------------------------------------
  1172. void __fastcall CenterButtonImage(TButton * Button)
  1173. {
  1174. // with themes disabled, the text seems to be drawn over the icon,
  1175. // so that the padding spaces hide away most of the icon
  1176. if (UseThemes())
  1177. {
  1178. Button->ImageAlignment = iaCenter;
  1179. int ImageWidth = Button->Images->Width;
  1180. std::unique_ptr<TCanvas> Canvas(CreateControlCanvas(Button));
  1181. UnicodeString Caption;
  1182. // Centering unlinks the caption from the action
  1183. TAction * Action = dynamic_cast<TAction *>(Button->Action);
  1184. if (Action != NULL)
  1185. {
  1186. Caption = Action->Caption;
  1187. }
  1188. else
  1189. {
  1190. Caption = Button->Caption.Trim();
  1191. }
  1192. UnicodeString Padding;
  1193. while (Canvas->TextWidth(Padding) < ImageWidth)
  1194. {
  1195. Padding += L" ";
  1196. }
  1197. if (Button->IsRightToLeft())
  1198. {
  1199. Caption = Caption + Padding;
  1200. }
  1201. else
  1202. {
  1203. Caption = Padding + Caption;
  1204. }
  1205. Button->Caption = Caption;
  1206. int CaptionWidth = Canvas->TextWidth(Caption);
  1207. // The margins seem to extend the area over which the image is centered,
  1208. // so we have to set it to a double of desired padding.
  1209. // The original formula is - 2 * ((CaptionWidth / 2) - (ImageWidth / 2) + ScaleByTextHeight(Button, 2))
  1210. // the one below is equivalent, but with reduced rouding.
  1211. // Without the change, the rounding caused the space between icon and caption too
  1212. // small on 200% zoom.
  1213. // Note that (CaptionWidth / 2) - (ImageWidth / 2)
  1214. // is approximatelly same as half of caption width before padding.
  1215. Button->ImageMargins->Left = -(CaptionWidth - ImageWidth + ScaleByTextHeight(Button, 4));
  1216. }
  1217. else
  1218. {
  1219. // at least do not draw it so near to the edge
  1220. Button->ImageMargins->Left = 1;
  1221. }
  1222. }
  1223. //---------------------------------------------------------------------------
  1224. int __fastcall AdjustLocaleFlag(const UnicodeString & S, TLocaleFlagOverride LocaleFlagOverride, bool Recommended, int On, int Off)
  1225. {
  1226. int Result = !S.IsEmpty() && StrToInt(S);
  1227. switch (LocaleFlagOverride)
  1228. {
  1229. default:
  1230. case lfoLanguageIfRecommended:
  1231. if (!Recommended)
  1232. {
  1233. Result = Off;
  1234. }
  1235. break;
  1236. case lfoLanguage:
  1237. // noop = as configured in locale
  1238. break;
  1239. case lfoAlways:
  1240. Result = On;
  1241. break;
  1242. case lfoNever:
  1243. Result = Off;
  1244. break;
  1245. }
  1246. return Result;
  1247. }
  1248. //---------------------------------------------------------------------------
  1249. void __fastcall SetGlobalMinimizeHandler(TCustomForm * /*Form*/, TNotifyEvent OnMinimize)
  1250. {
  1251. if (GlobalOnMinimize == NULL)
  1252. {
  1253. GlobalOnMinimize = OnMinimize;
  1254. }
  1255. }
  1256. //---------------------------------------------------------------------------
  1257. void __fastcall ClearGlobalMinimizeHandler(TNotifyEvent OnMinimize)
  1258. {
  1259. if (GlobalOnMinimize == OnMinimize)
  1260. {
  1261. GlobalOnMinimize = NULL;
  1262. }
  1263. }
  1264. //---------------------------------------------------------------------------
  1265. void __fastcall CallGlobalMinimizeHandler(TObject * Sender)
  1266. {
  1267. Configuration->Usage->Inc(L"OperationMinimizations");
  1268. if (DebugAlwaysTrue(GlobalOnMinimize != NULL))
  1269. {
  1270. GlobalOnMinimize(Sender);
  1271. }
  1272. }
  1273. //---------------------------------------------------------------------------
  1274. bool MinimizedToTray = false;
  1275. //---------------------------------------------------------------------------
  1276. static void __fastcall DoApplicationMinimizeRestore(bool Minimize)
  1277. {
  1278. TForm * MainForm = Application->MainForm;
  1279. TForm * MainLikeForm = GetMainForm();
  1280. bool MinimizeToTray;
  1281. if (Minimize)
  1282. {
  1283. MinimizeToTray = WinConfiguration->MinimizeToTray;
  1284. }
  1285. else
  1286. {
  1287. // Use tray restore code, even if minimization to tray is not on anymore when restoring.
  1288. // This is particularly used for ad-hoc minimize to tray from "keep up to date" dialog.
  1289. MinimizeToTray = MinimizedToTray;
  1290. }
  1291. MinimizedToTray = false; // reset in any case, even if we somehow got restored from tray without invoking this code
  1292. if ((MainLikeForm != MainForm) && !MinimizeToTray)
  1293. {
  1294. static TWindowState PreviousWindowState = wsNormal;
  1295. if (Minimize)
  1296. {
  1297. PreviousWindowState = MainLikeForm->WindowState;
  1298. // This works correctly with child windows thanks to Application->OnGetMainFormHandle
  1299. MainLikeForm->WindowState = wsMinimized;
  1300. }
  1301. else
  1302. {
  1303. MainLikeForm->WindowState = PreviousWindowState;
  1304. }
  1305. }
  1306. else
  1307. {
  1308. // What is described below should not ever happen, except when minimizing to tray,
  1309. // as we capture command-line operation above.
  1310. // Had we called TApplication::Minimize, it would hide all non-MainForm windows, including MainLineForm,
  1311. // so it actually also hides taskbar button, what we do not want.
  1312. // WORKAROUND
  1313. // When main window is hidden (command-line operation),
  1314. // we do not want it to be shown by TApplication.Restore,
  1315. // so we temporarily detach it from an application.
  1316. // Probably not really necessary for minimizing phase,
  1317. // but we do it for consistency anyway.
  1318. bool RestoreMainForm = false;
  1319. if (DebugAlwaysTrue(MainForm != NULL) &&
  1320. !MainForm->Visible)
  1321. {
  1322. SetAppMainForm(NULL);
  1323. RestoreMainForm = true;
  1324. }
  1325. try
  1326. {
  1327. if (Minimize)
  1328. {
  1329. Application->Minimize();
  1330. MinimizedToTray = WinConfiguration->MinimizeToTray;
  1331. }
  1332. else
  1333. {
  1334. Application->Restore();
  1335. }
  1336. }
  1337. __finally
  1338. {
  1339. if (RestoreMainForm)
  1340. {
  1341. SetAppMainForm(MainForm);
  1342. }
  1343. }
  1344. }
  1345. }
  1346. //---------------------------------------------------------------------------
  1347. void __fastcall ApplicationMinimize()
  1348. {
  1349. DoApplicationMinimizeRestore(true);
  1350. }
  1351. //---------------------------------------------------------------------------
  1352. void __fastcall ApplicationRestore()
  1353. {
  1354. DoApplicationMinimizeRestore(false);
  1355. }
  1356. //---------------------------------------------------------------------------
  1357. bool __fastcall IsApplicationMinimized()
  1358. {
  1359. // VCL help recommends handling Application->OnMinimize/OnRestore
  1360. // for tracking state, but OnRestore is actually not called
  1361. // (OnMinimize is), when app is minimized from e.g. Progress window
  1362. bool AppMinimized = IsIconic(Application->Handle);
  1363. bool MainFormMinimized = IsIconic(Application->MainFormHandle);
  1364. return AppMinimized || MainFormMinimized;
  1365. }
  1366. //---------------------------------------------------------------------------
  1367. bool __fastcall HandleMinimizeSysCommand(TMessage & Message)
  1368. {
  1369. TWMSysCommand & SysCommand = reinterpret_cast<TWMSysCommand &>(Message);
  1370. unsigned int Cmd = (SysCommand.CmdType & 0xFFF0);
  1371. bool Result = (Cmd == SC_MINIMIZE);
  1372. if (Result)
  1373. {
  1374. ApplicationMinimize();
  1375. SysCommand.Result = 1;
  1376. }
  1377. return Result;
  1378. }
  1379. //---------------------------------------------------------------------------
  1380. void __fastcall ClickToolbarItem(TTBCustomItem * Item, bool PositionCursor)
  1381. {
  1382. TTBCustomItem * TopItem = Item;
  1383. while (TopItem->Parent != NULL)
  1384. {
  1385. TopItem = TopItem->Parent;
  1386. }
  1387. TTBCustomToolbar * Toolbar = dynamic_cast<TTBCustomToolbar *>(TopItem->ParentComponent);
  1388. DebugAssert(Toolbar != NULL);
  1389. TTBItemViewer * Viewer = Toolbar->View->Find(Item);
  1390. DebugAssert(Viewer != NULL);
  1391. int X = Viewer->BoundsRect.Left + (Viewer->BoundsRect.Width() / 2);
  1392. int Y = Viewer->BoundsRect.Top + (Viewer->BoundsRect.Height() / 2);
  1393. if (PositionCursor)
  1394. {
  1395. Mouse->CursorPos = Toolbar->ClientToScreen(TPoint(X, Y));
  1396. }
  1397. PostMessage(Toolbar->Handle, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(X, Y));
  1398. }
  1399. //---------------------------------------------------------------------------
  1400. //---------------------------------------------------------------------------
  1401. UnicodeString DumpCallstackEventName(int ProcessId)
  1402. {
  1403. return FORMAT(DUMPCALLSTACK_EVENT, (ProcessId));
  1404. }
  1405. //---------------------------------------------------------------------------
  1406. UnicodeString DumpCallstackFileName(int ProcessId)
  1407. {
  1408. UnicodeString FileName = FORMAT(L"%s.txt", (DumpCallstackEventName(ProcessId)));
  1409. UnicodeString Result = TPath::Combine(SystemTemporaryDirectory(), FileName);
  1410. return Result;
  1411. }
  1412. //---------------------------------------------------------------------------
  1413. void CheckConfigurationForceSave()
  1414. {
  1415. if (UseAlternativeFunction() && Configuration->Persistent &&
  1416. (Configuration->Storage == stIniFile) && Sysutils::FileExists(ApiPath(Configuration->IniFileStorageName)) &&
  1417. !Configuration->ForceSave)
  1418. {
  1419. int Attr = GetFileAttributes(ApiPath(Configuration->IniFileStorageName).c_str());
  1420. if (FLAGSET(Attr, FILE_ATTRIBUTE_READONLY))
  1421. {
  1422. UnicodeString Message = FMTLOAD(READONLY_INI_FILE_OVERWRITE, (Configuration->IniFileStorageName));
  1423. if (MessageDialog(Message, qtConfirmation, qaOK | qaCancel, HELP_READONLY_INI_FILE) == qaOK)
  1424. {
  1425. Configuration->ForceSave = true;
  1426. }
  1427. }
  1428. }
  1429. }
  1430. //---------------------------------------------------------------------------
  1431. class TCallstackThread : public TSignalThread
  1432. {
  1433. public:
  1434. __fastcall TCallstackThread();
  1435. protected:
  1436. virtual void __fastcall ProcessEvent();
  1437. private:
  1438. static HANDLE DoCreateEvent();
  1439. };
  1440. //---------------------------------------------------------------------------
  1441. __fastcall TCallstackThread::TCallstackThread() :
  1442. TSignalThread(true, DoCreateEvent())
  1443. {
  1444. }
  1445. //---------------------------------------------------------------------------
  1446. void __fastcall TCallstackThread::ProcessEvent()
  1447. {
  1448. try
  1449. {
  1450. UnicodeString Path = DumpCallstackFileName(GetCurrentProcessId());
  1451. std::unique_ptr<TStrings> StackStrings;
  1452. HANDLE MainThreadHandle = reinterpret_cast<HANDLE>(MainThreadID);
  1453. if (SuspendThread(MainThreadHandle) < 0)
  1454. {
  1455. RaiseLastOSError();
  1456. }
  1457. try
  1458. {
  1459. TJclStackInfoList * StackInfoList = JclCreateThreadStackTraceFromID(true, MainThreadID);
  1460. if (StackInfoList == NULL)
  1461. {
  1462. RaiseLastOSError();
  1463. }
  1464. StackStrings.reset(StackInfoListToStrings(StackInfoList));
  1465. }
  1466. __finally
  1467. {
  1468. if (ResumeThread(MainThreadHandle) < 0)
  1469. {
  1470. RaiseLastOSError();
  1471. }
  1472. }
  1473. TFile::WriteAllText(Path, StackStrings->Text);
  1474. }
  1475. catch (...)
  1476. {
  1477. }
  1478. }
  1479. //---------------------------------------------------------------------------
  1480. HANDLE TCallstackThread::DoCreateEvent()
  1481. {
  1482. UnicodeString Name = DumpCallstackEventName(GetCurrentProcessId());
  1483. return CreateEvent(NULL, false, false, Name.c_str());
  1484. }
  1485. //---------------------------------------------------------------------------
  1486. //---------------------------------------------------------------------------
  1487. std::unique_ptr<TCallstackThread> CallstackThread;
  1488. //---------------------------------------------------------------------------
  1489. static void __fastcall AppGetMainFormHandle(void * /*Data*/, HWND & Handle)
  1490. {
  1491. TForm * MainForm = GetMainForm();
  1492. // This, among other, causes minimizing of the top-level non-MainForm minimize other child windows.
  1493. // Like clicking "Minimize" on Progress window over Synchronization progress window over Synchronization checklist window.
  1494. // Would also have a lot of other effects (hopefully possitive) and may render lot of existing MainFormLike code obsolete.
  1495. if ((MainForm != NULL) && IsMainFormLike(MainForm) && MainForm->HandleAllocated())
  1496. {
  1497. Handle = MainForm->Handle;
  1498. }
  1499. }
  1500. //---------------------------------------------------------------------------
  1501. void __fastcall WinInitialize()
  1502. {
  1503. if (JclHookExceptions())
  1504. {
  1505. JclStackTrackingOptions << stAllModules;
  1506. JclAddExceptNotifier(DoExceptNotify, npFirstChain);
  1507. CallstackThread.reset(new TCallstackThread());
  1508. CallstackThread->Start();
  1509. }
  1510. SetErrorMode(SEM_FAILCRITICALERRORS);
  1511. OnApiPath = ApiPath;
  1512. MainThread = GetCurrentThreadId();
  1513. Application->OnGetMainFormHandle = MakeMethod<TGetHandleEvent>(NULL, AppGetMainFormHandle);
  1514. #pragma warn -8111
  1515. #pragma warn .8111
  1516. }
  1517. //---------------------------------------------------------------------------
  1518. void __fastcall WinFinalize()
  1519. {
  1520. CallstackThread.reset(NULL);
  1521. JclRemoveExceptNotifier(DoExceptNotify);
  1522. }
  1523. //---------------------------------------------------------------------------
  1524. __fastcall ::TTrayIcon::TTrayIcon(unsigned int Id)
  1525. {
  1526. FVisible = false;
  1527. FOnClick = NULL;
  1528. FOnBalloonClick = NULL;
  1529. FBalloonUserData = NULL;
  1530. FTrayIcon = new NOTIFYICONDATA;
  1531. memset(FTrayIcon, 0, sizeof(*FTrayIcon));
  1532. FTrayIcon->cbSize = sizeof(*FTrayIcon);
  1533. FTrayIcon->uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  1534. // LoadIconMetric is available from Windows Vista only
  1535. HMODULE ComCtl32Dll = GetModuleHandle(comctl32);
  1536. if (DebugAlwaysTrue(ComCtl32Dll))
  1537. {
  1538. typedef HRESULT WINAPI (* TLoadIconMetric)(HINSTANCE hinst, PCWSTR pszName, int lims, __out HICON *phico);
  1539. TLoadIconMetric LoadIconMetric = (TLoadIconMetric)GetProcAddress(ComCtl32Dll, "LoadIconMetric");
  1540. if (LoadIconMetric != NULL)
  1541. {
  1542. // Prefer not to use Application->Icon->Handle as that shows 32x32 scaled down to 16x16 for some reason
  1543. LoadIconMetric(MainInstance, L"MAINICON", LIM_SMALL, &FTrayIcon->hIcon);
  1544. }
  1545. }
  1546. if (FTrayIcon->hIcon == 0)
  1547. {
  1548. FTrayIcon->hIcon = Application->Icon->Handle;
  1549. }
  1550. FTrayIcon->uID = Id;
  1551. FTrayIcon->hWnd = AllocateHWnd(WndProc);
  1552. FTrayIcon->uCallbackMessage = WM_TRAY_ICON;
  1553. FTaskbarCreatedMsg = RegisterWindowMessage(L"TaskbarCreated");
  1554. }
  1555. //---------------------------------------------------------------------------
  1556. __fastcall ::TTrayIcon::~TTrayIcon()
  1557. {
  1558. // make sure we hide icon even in case it was shown just to pop up the balloon
  1559. // (in which case Visible == false)
  1560. CancelBalloon();
  1561. Visible = false;
  1562. DeallocateHWnd(FTrayIcon->hWnd);
  1563. delete FTrayIcon;
  1564. }
  1565. //---------------------------------------------------------------------------
  1566. void __fastcall ::TTrayIcon::PopupBalloon(UnicodeString Title,
  1567. const UnicodeString & Str, TQueryType QueryType, unsigned int Timeout,
  1568. TNotifyEvent OnBalloonClick, TObject * BalloonUserData)
  1569. {
  1570. if (Timeout > 30000)
  1571. {
  1572. // this is probably system limit, do not try more, especially for
  1573. // the timeout-driven hiding of the tray icon (for Win2k)
  1574. Timeout = 30000;
  1575. }
  1576. FTrayIcon->uFlags |= NIF_INFO;
  1577. Title = Title + TitleSeparator + AppNameString();
  1578. StrPLCopy(FTrayIcon->szInfoTitle, Title, LENOF(FTrayIcon->szInfoTitle) - 1);
  1579. UnicodeString Info = Str;
  1580. // When szInfo is empty, balloon is not shown
  1581. // (or actually it means the balloon should be deleted, if any)
  1582. if (Info.IsEmpty())
  1583. {
  1584. Info = L" ";
  1585. }
  1586. StrPLCopy(FTrayIcon->szInfo, Info, LENOF(FTrayIcon->szInfo) - 1);
  1587. FTrayIcon->uTimeout = Timeout;
  1588. switch (QueryType)
  1589. {
  1590. case qtError:
  1591. FTrayIcon->dwInfoFlags = NIIF_ERROR;
  1592. break;
  1593. case qtInformation:
  1594. case qtConfirmation:
  1595. FTrayIcon->dwInfoFlags = NIIF_INFO;
  1596. break;
  1597. case qtWarning:
  1598. default:
  1599. FTrayIcon->dwInfoFlags = NIIF_WARNING;
  1600. break;
  1601. }
  1602. KillTimer(FTrayIcon->hWnd, 1);
  1603. if (Visible)
  1604. {
  1605. Update();
  1606. }
  1607. else
  1608. {
  1609. Notify(NIM_ADD);
  1610. }
  1611. FOnBalloonClick = OnBalloonClick;
  1612. delete FBalloonUserData;
  1613. FBalloonUserData = BalloonUserData;
  1614. // Clearing the flag ensures that subsequent updates does not hide the baloon
  1615. // unless CancelBalloon is called explicitly
  1616. FTrayIcon->uFlags = FTrayIcon->uFlags & ~NIF_INFO;
  1617. }
  1618. //---------------------------------------------------------------------------
  1619. void __fastcall ::TTrayIcon::BalloonCancelled()
  1620. {
  1621. FOnBalloonClick = NULL;
  1622. delete FBalloonUserData;
  1623. FBalloonUserData = NULL;
  1624. }
  1625. //---------------------------------------------------------------------------
  1626. void __fastcall ::TTrayIcon::CancelBalloon()
  1627. {
  1628. KillTimer(FTrayIcon->hWnd, 1);
  1629. if (Visible)
  1630. {
  1631. FTrayIcon->uFlags |= NIF_INFO;
  1632. FTrayIcon->szInfo[0] = L'\0';
  1633. Update();
  1634. FTrayIcon->uFlags = FTrayIcon->uFlags & ~NIF_INFO;
  1635. }
  1636. else
  1637. {
  1638. Notify(NIM_DELETE);
  1639. }
  1640. BalloonCancelled();
  1641. }
  1642. //---------------------------------------------------------------------------
  1643. bool __fastcall ::TTrayIcon::Notify(unsigned int Message)
  1644. {
  1645. bool Result = SUCCEEDED(Shell_NotifyIcon(Message, (NOTIFYICONDATA*)FTrayIcon));
  1646. if (Result && (Message == NIM_ADD))
  1647. {
  1648. UINT Timeout = FTrayIcon->uTimeout;
  1649. try
  1650. {
  1651. FTrayIcon->uVersion = NOTIFYICON_VERSION;
  1652. Result = SUCCEEDED(Shell_NotifyIcon(NIM_SETVERSION, (NOTIFYICONDATA*)FTrayIcon));
  1653. }
  1654. __finally
  1655. {
  1656. FTrayIcon->uTimeout = Timeout;
  1657. }
  1658. }
  1659. return Result;
  1660. }
  1661. //---------------------------------------------------------------------------
  1662. void __fastcall ::TTrayIcon::Update()
  1663. {
  1664. if (Visible)
  1665. {
  1666. Notify(NIM_MODIFY);
  1667. }
  1668. }
  1669. //---------------------------------------------------------------------------
  1670. void __fastcall ::TTrayIcon::SetVisible(bool value)
  1671. {
  1672. if (Visible != value)
  1673. {
  1674. if (value)
  1675. {
  1676. FVisible = Notify(NIM_ADD);
  1677. }
  1678. else
  1679. {
  1680. FVisible = false;
  1681. KillTimer(FTrayIcon->hWnd, 1);
  1682. Notify(NIM_DELETE);
  1683. BalloonCancelled();
  1684. }
  1685. }
  1686. }
  1687. //---------------------------------------------------------------------------
  1688. void __fastcall ::TTrayIcon::WndProc(TMessage & Message)
  1689. {
  1690. try
  1691. {
  1692. if (Message.Msg == WM_TRAY_ICON)
  1693. {
  1694. DebugAssert(Message.WParam == 0);
  1695. switch (Message.LParam)
  1696. {
  1697. // old shell32
  1698. case WM_LBUTTONUP:
  1699. case WM_RBUTTONUP:
  1700. // new shell32:
  1701. case WM_CONTEXTMENU:
  1702. if (OnClick != NULL)
  1703. {
  1704. OnClick(NULL);
  1705. }
  1706. Message.Result = true;
  1707. break;
  1708. }
  1709. if (Message.LParam == NIN_BALLOONUSERCLICK)
  1710. {
  1711. if (FOnBalloonClick != NULL)
  1712. {
  1713. // prevent the user data from being freed by possible call
  1714. // to CancelBalloon or PopupBalloon during call to OnBalloonClick
  1715. std::unique_ptr<TObject> UserData(FBalloonUserData);
  1716. FBalloonUserData = NULL;
  1717. FOnBalloonClick(UserData.get());
  1718. }
  1719. else if (OnClick != NULL)
  1720. {
  1721. OnClick(NULL);
  1722. }
  1723. }
  1724. switch (Message.LParam)
  1725. {
  1726. case NIN_BALLOONHIDE:
  1727. case NIN_BALLOONTIMEOUT:
  1728. case NIN_BALLOONUSERCLICK:
  1729. KillTimer(FTrayIcon->hWnd, 1);
  1730. // if icon was shown just to display balloon, hide it with the balloon
  1731. if (!Visible)
  1732. {
  1733. Notify(NIM_DELETE);
  1734. }
  1735. BalloonCancelled();
  1736. break;
  1737. }
  1738. }
  1739. else if (Message.Msg == WM_TIMER)
  1740. {
  1741. // sanity check
  1742. Notify(NIM_DELETE);
  1743. BalloonCancelled();
  1744. }
  1745. else if (Message.Msg == FTaskbarCreatedMsg)
  1746. {
  1747. if (Visible)
  1748. {
  1749. // force recreation
  1750. Visible = false;
  1751. Visible = true;
  1752. }
  1753. }
  1754. else
  1755. {
  1756. Message.Result = DefWindowProc(FTrayIcon->hWnd, Message.Msg, Message.WParam, Message.LParam);
  1757. }
  1758. }
  1759. catch(Exception & E)
  1760. {
  1761. Application->HandleException(&E);
  1762. }
  1763. }
  1764. //---------------------------------------------------------------------------
  1765. UnicodeString __fastcall ::TTrayIcon::GetHint()
  1766. {
  1767. return FTrayIcon->szTip;
  1768. }
  1769. //---------------------------------------------------------------------------
  1770. void __fastcall ::TTrayIcon::SetHint(UnicodeString value)
  1771. {
  1772. if (Hint != value)
  1773. {
  1774. unsigned int Max = LENOF(FTrayIcon->szTip);
  1775. StrPLCopy(FTrayIcon->szTip, value, Max - 1);
  1776. Update();
  1777. }
  1778. }
  1779. //---------------------------------------------------------------------------