Tools.cpp 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763
  1. //---------------------------------------------------------------------------
  2. #include <WinPCH.h>
  3. #pragma hdrstop
  4. #include <shlobj.h>
  5. #include <stdio.h>
  6. #define INITGUID
  7. #include <propkey.h>
  8. #include <powrprof.h>
  9. #include <RemoteFiles.h>
  10. #include <PuttyTools.h>
  11. #include "Setup.h"
  12. #include <WinHelpViewer.hpp>
  13. #include <System.Win.ComObj.hpp>
  14. #include <ProgParams.h>
  15. //---------------------------------------------------------------------------
  16. // WORKAROUND
  17. // VCL includes wininet.h (even with NO_WIN32_LEAN_AND_MEAN)
  18. // and it cannot be combined with winhttp.h as of current Windows SDK.
  19. // This is hack to allow that.
  20. // https://web.archive.org/web/20140612011622/https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/8f468d9f-3f15-452c-803d-fc63ab3f684e/cannot-use-both-winineth-and-winhttph
  21. #undef BOOLAPI
  22. #undef SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
  23. #undef SECURITY_FLAG_IGNORE_CERT_CN_INVALID
  24. #define URL_COMPONENTS URL_COMPONENTS_ANOTHER
  25. #define URL_COMPONENTSW URL_COMPONENTSW_ANOTHER
  26. #define LPURL_COMPONENTS LPURL_COMPONENTS_ANOTHER
  27. #define LPURL_COMPONENTSW LPURL_COMPONENTS_ANOTHER
  28. #define INTERNET_SCHEME INTERNET_SCHEME_ANOTHER
  29. #define LPINTERNET_SCHEME LPINTERNET_SCHEME_ANOTHER
  30. #define HTTP_VERSION_INFO HTTP_VERSION_INFO_ANOTHER
  31. #define LPHTTP_VERSION_INFO LPHTTP_VERSION_INFO_ANOTHER
  32. #include <winhttp.h>
  33. #undef URL_COMPONENTS
  34. #undef URL_COMPONENTSA
  35. #undef URL_COMPONENTSW
  36. #undef LPURL_COMPONENTS
  37. #undef LPURL_COMPONENTSA
  38. #undef LPURL_COMPONENTSW
  39. #undef INTERNET_SCHEME
  40. #undef LPINTERNET_SCHEME
  41. #undef HTTP_VERSION_INFO
  42. #undef LPHTTP_VERSION_INFO
  43. //---------------------------------------------------------------------------
  44. TFontStyles __fastcall IntToFontStyles(int value)
  45. {
  46. TFontStyles Result;
  47. for (int i = fsBold; i <= fsStrikeOut; i++)
  48. {
  49. if (value & 1)
  50. {
  51. Result << (TFontStyle)i;
  52. }
  53. value >>= 1;
  54. }
  55. return Result;
  56. }
  57. //---------------------------------------------------------------------------
  58. int __fastcall FontStylesToInt(const TFontStyles value)
  59. {
  60. int Result = 0;
  61. for (int i = fsStrikeOut; i >= fsBold; i--)
  62. {
  63. Result <<= 1;
  64. if (value.Contains((TFontStyle)i))
  65. {
  66. Result |= 1;
  67. }
  68. }
  69. return Result;
  70. }
  71. //---------------------------------------------------------------------------
  72. bool __fastcall SameFont(TFont * Font1, TFont * Font2)
  73. {
  74. // keep in sync with TFontConfiguration::operator !=
  75. return
  76. SameText(Font1->Name, Font2->Name) && (Font1->Height == Font2->Height) &&
  77. (Font1->Charset == Font2->Charset) && (Font1->Style == Font2->Style);
  78. }
  79. //---------------------------------------------------------------------------
  80. TColor __fastcall GetWindowTextColor(TColor BackgroundColor, TColor Color)
  81. {
  82. if (Color == TColor(0))
  83. {
  84. // Could use current theme TMT_TEXTCOLOR - see https://github.com/ysc3839/win32-darkmode
  85. Color = (IsDarkColor(BackgroundColor) ? clWhite : clWindowText);
  86. SetContrast(Color, BackgroundColor, 180);
  87. }
  88. return Color;
  89. }
  90. //---------------------------------------------------------------------------
  91. TColor __fastcall GetWindowColor(TColor Color)
  92. {
  93. if (Color == TColor(0))
  94. {
  95. // Could use current theme TMT_FILLCOLOR - see https://github.com/ysc3839/win32-darkmode
  96. Color = (WinConfiguration->UseDarkTheme() ? static_cast<TColor>(RGB(0x20, 0x20, 0x20)) : clWindow);
  97. }
  98. return Color;
  99. }
  100. //---------------------------------------------------------------------------
  101. TColor __fastcall GetBtnFaceColor()
  102. {
  103. return WinConfiguration->UseDarkTheme() ? TColor(RGB(43, 43, 43)) : clBtnFace;
  104. }
  105. //---------------------------------------------------------------------------
  106. TColor GetLightLinkColor()
  107. {
  108. return clBlue;
  109. }
  110. //---------------------------------------------------------------------------
  111. TColor GetLinkColor(TControl * Control)
  112. {
  113. // dark theme Windows 11 Settings app On/Off toggle color
  114. return UseDarkModeForControl(Control) ? TColor(RGB(0x4C, 0xC2, 0xFF)) : GetLightLinkColor();
  115. }
  116. //---------------------------------------------------------------------------
  117. TColor __fastcall GetNonZeroColor(TColor Color)
  118. {
  119. // 0,0,0 is "default color"
  120. if (Color == TColor(0))
  121. {
  122. // use near-black instead
  123. Color = TColor(RGB(1, 1, 1));
  124. }
  125. return Color;
  126. }
  127. //---------------------------------------------------------------------------
  128. UnicodeString __fastcall GetListViewStr(TCustomListView * ListView)
  129. {
  130. UnicodeString Result;
  131. TListView * AListView = static_cast<TListView *>(ListView);
  132. for (int Index = 0; Index < AListView->Columns->Count; Index++)
  133. {
  134. AddToList(Result, IntToStr(AListView->Column[Index]->Width), L",");
  135. }
  136. // WORKAROUND
  137. // Adding an additional comma after the list,
  138. // to ensure that old versions that did not expect the pixels-per-inch part,
  139. // stop at the comma, otherwise they try to parse the
  140. // "last-column-width;pixels-per-inch" as integer and throw.
  141. // For the other instance of this hack, see TCustomListViewColProperties.GetParamsStr
  142. Result += L",;" + SavePixelsPerInch(ListView);
  143. return Result;
  144. }
  145. //---------------------------------------------------------------------------
  146. void __fastcall LoadListViewStr(TCustomListView * ListView, UnicodeString ALayoutStr)
  147. {
  148. UnicodeString LayoutStr = CutToChar(ALayoutStr, L';', true);
  149. int PixelsPerInch = LoadPixelsPerInch(CutToChar(ALayoutStr, L';', true), ListView);
  150. int Index = 0;
  151. TListView * AListView = static_cast<TListView *>(ListView);
  152. while (!LayoutStr.IsEmpty() && (Index < AListView->Columns->Count))
  153. {
  154. int Width;
  155. if (TryStrToInt(CutToChar(LayoutStr, L',', true), Width))
  156. {
  157. AListView->Column[Index]->Width = LoadDimension(Width, PixelsPerInch, ListView);
  158. }
  159. Index++;
  160. }
  161. }
  162. //---------------------------------------------------------------------------
  163. static wchar_t FormDataSep = L';';
  164. //---------------------------------------------------------------------------
  165. void LoadFormDimensions(
  166. const UnicodeString & AData, int PixelsPerInch, Forms::TMonitor * Monitor, TForm * Form, TRect & Bounds, bool & DefaultPos)
  167. {
  168. UnicodeString Data = AData;
  169. UnicodeString LeftStr = CutToChar(Data, FormDataSep, true);
  170. UnicodeString TopStr = CutToChar(Data, FormDataSep, true);
  171. UnicodeString RightStr = CutToChar(Data, FormDataSep, true);
  172. UnicodeString BottomStr = CutToChar(Data, FormDataSep, true);
  173. DefaultPos = (StrToIntDef(LeftStr, 0) == -1) && (StrToIntDef(TopStr, 0) == -1);
  174. if (!DefaultPos)
  175. {
  176. Bounds.Left = StrToDimensionDef(LeftStr, PixelsPerInch, Form, Bounds.Left);
  177. Bounds.Top = StrToDimensionDef(TopStr, PixelsPerInch, Form, Bounds.Top);
  178. }
  179. else
  180. {
  181. Bounds.Left = 0;
  182. Bounds.Top = 0;
  183. }
  184. Bounds.Right = StrToDimensionDef(RightStr, PixelsPerInch, Form, Bounds.Right);
  185. Bounds.Bottom = StrToDimensionDef(BottomStr, PixelsPerInch, Form, Bounds.Bottom);
  186. // move to the target monitor
  187. OffsetRect(Bounds, Monitor->Left, Monitor->Top);
  188. // reduce window size to that of monitor size
  189. // (this does not cut window into monitor!)
  190. if (Bounds.Width() > Monitor->WorkareaRect.Width())
  191. {
  192. Bounds.Right -= (Bounds.Width() - Monitor->WorkareaRect.Width());
  193. }
  194. if (Bounds.Height() > Monitor->WorkareaRect.Height())
  195. {
  196. Bounds.Bottom -= (Bounds.Height() - Monitor->WorkareaRect.Height());
  197. }
  198. }
  199. //---------------------------------------------------------------------------
  200. static void CenterOnMonitor(TForm * Form, Forms::TMonitor * Monitor, const TRect & Bounds)
  201. {
  202. Form->BoundsRect = Monitor->BoundsRect.CenteredRect(Bounds);
  203. }
  204. //---------------------------------------------------------------------------
  205. void RestoreForm(const UnicodeString & AData, TForm * Form, bool PositionOnly, const UnicodeString & DefaultData)
  206. {
  207. DebugAssert(Form);
  208. UnicodeString Data = AData;
  209. if (!Data.IsEmpty())
  210. {
  211. Forms::TMonitor * Monitor = FormMonitor(Form);
  212. UnicodeString BoundsStr;
  213. for (int Index = 0; Index < 4; Index++)
  214. {
  215. CutToChar(Data, FormDataSep, true);
  216. }
  217. TWindowState State = (TWindowState)StrToIntDef(CutToChar(Data, FormDataSep, true), (int)wsNormal);
  218. int PixelsPerInch = LoadPixelsPerInch(CutToChar(Data, FormDataSep, true), Form);
  219. TRect OriginalBounds = Form->BoundsRect;
  220. int OriginalPixelsPerInch = Form->PixelsPerInch;
  221. Form->WindowState = State;
  222. if (State == wsNormal)
  223. {
  224. bool DefaultPos;
  225. TRect Bounds = OriginalBounds;
  226. LoadFormDimensions(AData, PixelsPerInch, Monitor, Form, Bounds, DefaultPos);
  227. int Padding = ScaleByPixelsPerInch(20, Monitor);
  228. if (DefaultPos ||
  229. ((Bounds.Left < Monitor->Left - Padding) ||
  230. (Bounds.Left > Monitor->Left + Monitor->WorkareaRect.Width() - Padding) ||
  231. (Bounds.Top < Monitor->Top - Padding) ||
  232. (Bounds.Top > Monitor->Top + Monitor->WorkareaRect.Height() - Padding)))
  233. {
  234. bool ExplicitPlacing = !Monitor->Primary;
  235. if (!ExplicitPlacing)
  236. {
  237. TPosition Position;
  238. if ((Application->MainForm == NULL) || (Application->MainForm == Form))
  239. {
  240. Position = poDefaultPosOnly;
  241. }
  242. else
  243. {
  244. Position = poOwnerFormCenter;
  245. }
  246. // If handle is allocated already, changing Position reallocates it, what brings lot of nasty side effects.
  247. if (Form->HandleAllocated() && (Form->Position != Position))
  248. {
  249. ExplicitPlacing = true;
  250. }
  251. else
  252. {
  253. Form->Width = Bounds.Width();
  254. Form->Height = Bounds.Height();
  255. }
  256. }
  257. if (ExplicitPlacing)
  258. {
  259. // when positioning on non-primary monitor, we need
  260. // to handle that ourselves, so place window to center
  261. if (!PositionOnly)
  262. {
  263. CenterOnMonitor(Form, Monitor, Bounds);
  264. }
  265. if (!Form->HandleAllocated())
  266. {
  267. Form->Position = poDesigned;
  268. }
  269. }
  270. }
  271. else
  272. {
  273. Form->Position = poDesigned;
  274. if (!PositionOnly)
  275. {
  276. Form->BoundsRect = Bounds;
  277. // If setting bounds moved the form to another monitor with non-default DPI,
  278. // recalculate the size to avoid rounding issues
  279. // (as the form was very likely moved to the monitor, where the sizes were saved)
  280. // See also TCustomScpExplorerForm::DoShow()
  281. if (OriginalPixelsPerInch != Form->PixelsPerInch)
  282. {
  283. TRect Bounds2 = OriginalBounds;
  284. LoadFormDimensions(AData, PixelsPerInch, Monitor, Form, Bounds2, DefaultPos);
  285. DebugAssert(!DefaultPos);
  286. Form->BoundsRect = Bounds2;
  287. }
  288. }
  289. }
  290. }
  291. else if (State == wsMaximized)
  292. {
  293. Form->Position = poDesigned;
  294. if (!PositionOnly)
  295. {
  296. TRect Bounds2 = OriginalBounds;
  297. bool DefaultPos;
  298. // This gives the form after unmaximization somewhat reasonable size,
  299. // but it does not really set exact dimensions, let only with scaling
  300. LoadFormDimensions(DefaultData, USER_DEFAULT_SCREEN_DPI, Monitor, Form, Bounds2, DefaultPos);
  301. DebugAssert(DefaultPos);
  302. CenterOnMonitor(Form, Monitor, Bounds2);
  303. }
  304. }
  305. }
  306. else if (Form->Position == poDesigned)
  307. {
  308. Form->Position = poDefaultPosOnly;
  309. }
  310. }
  311. //---------------------------------------------------------------------------
  312. UnicodeString __fastcall StoreForm(TForm * Form)
  313. {
  314. DebugAssert(Form);
  315. TRect Bounds = Form->BoundsRect;
  316. OffsetRect(Bounds, -Form->Monitor->Left, -Form->Monitor->Top);
  317. UnicodeString Result =
  318. FORMAT(L"%d;%d;%d;%d;%d;%s", (SaveDimension(Bounds.Left), SaveDimension(Bounds.Top),
  319. SaveDimension(Bounds.Right), SaveDimension(Bounds.Bottom),
  320. // we do not want WinSCP to start minimized next time (we cannot handle that anyway).
  321. (int)(Form->WindowState == wsMinimized ? GetWindowStateBeforeMimimize(Form) : Form->WindowState),
  322. SavePixelsPerInch(Form)));
  323. return Result;
  324. }
  325. //---------------------------------------------------------------------------
  326. void __fastcall RestoreFormSize(UnicodeString Data, TForm * Form)
  327. {
  328. // This has to be called only after DoFormWindowProc(CM_SHOWINGCHANGED).
  329. // See comment in ResizeForm.
  330. UnicodeString WidthStr = CutToChar(Data, L',', true);
  331. UnicodeString HeightStr = CutToChar(Data, L',', true);
  332. int PixelsPerInch = LoadPixelsPerInch(CutToChar(Data, L',', true), Form);
  333. int Width = StrToDimensionDef(WidthStr, PixelsPerInch, Form, Form->Width);
  334. int Height = StrToDimensionDef(HeightStr, PixelsPerInch, Form, Form->Height);
  335. ResizeForm(Form, Width, Height);
  336. }
  337. //---------------------------------------------------------------------------
  338. UnicodeString __fastcall StoreFormSize(TForm * Form)
  339. {
  340. return FORMAT(L"%d,%d,%s", (Form->Width, Form->Height, SavePixelsPerInch(Form)));
  341. }
  342. //---------------------------------------------------------------------------
  343. void ExecuteProcessAndReadOutput(const UnicodeString & Command, UnicodeString & Output, DWORD & ExitCode, bool ReadStdErr)
  344. {
  345. SECURITY_ATTRIBUTES SecurityAttributes;
  346. ZeroMemory(&SecurityAttributes, sizeof(SecurityAttributes));
  347. SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  348. SecurityAttributes.bInheritHandle = TRUE;
  349. SecurityAttributes.lpSecurityDescriptor = NULL;
  350. HANDLE PipeRead = INVALID_HANDLE_VALUE;
  351. HANDLE PipeWrite = INVALID_HANDLE_VALUE;
  352. if (!CreatePipe(&PipeRead, &PipeWrite, &SecurityAttributes, 0) ||
  353. !SetHandleInformation(PipeRead, HANDLE_FLAG_INHERIT, 0))
  354. {
  355. throw EOSExtException(FMTLOAD(EXECUTE_APP_ERROR, (Command)));
  356. }
  357. PROCESS_INFORMATION ProcessInformation;
  358. ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
  359. try
  360. {
  361. try
  362. {
  363. STARTUPINFO StartupInfo;
  364. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  365. StartupInfo.cb = sizeof(STARTUPINFO);
  366. if (ReadStdErr)
  367. {
  368. StartupInfo.hStdError = PipeWrite;
  369. }
  370. StartupInfo.hStdOutput = PipeWrite;
  371. StartupInfo.wShowWindow = SW_HIDE;
  372. StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  373. if (!CreateProcess(NULL, Command.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
  374. {
  375. throw EOSExtException(FMTLOAD(EXECUTE_APP_ERROR, (Command)));
  376. }
  377. }
  378. __finally
  379. {
  380. // If we do not close the handle here, the ReadFile below would get stuck once the app finishes writting,
  381. // as it still sees that someone "can" write to the pipe.
  382. CloseHandle(PipeWrite);
  383. }
  384. DWORD BytesAvail;
  385. while (PeekNamedPipe(PipeRead, NULL, 0, NULL, &BytesAvail, NULL))
  386. {
  387. if (BytesAvail > 0)
  388. {
  389. char Buffer[4096];
  390. DWORD BytesToRead = std::min(BytesAvail, static_cast<unsigned long>(sizeof(Buffer)));
  391. DWORD BytesRead;
  392. if (ReadFile(PipeRead, Buffer, BytesToRead, &BytesRead, NULL))
  393. {
  394. Output += UnicodeString(UTF8String(Buffer, BytesRead));
  395. }
  396. }
  397. // Same as in ExecuteShellCheckedAndWait
  398. Sleep(50);
  399. Application->ProcessMessages();
  400. }
  401. DebugCheck(GetExitCodeProcess(ProcessInformation.hProcess, &ExitCode));
  402. }
  403. __finally
  404. {
  405. CloseHandle(ProcessInformation.hProcess);
  406. CloseHandle(ProcessInformation.hThread);
  407. CloseHandle(PipeRead);
  408. }
  409. }
  410. //---------------------------------------------------------------------------
  411. static void __fastcall DoExecuteProcessAndReadOutput(
  412. const UnicodeString & Command, const UnicodeString & HelpKeyword, UnicodeString & Output)
  413. {
  414. if (!CopyCommandToClipboard(Command))
  415. {
  416. DWORD ExitCode;
  417. ExecuteProcessAndReadOutput(Command, Output, ExitCode, true);
  418. if (ExitCode != 0)
  419. {
  420. UnicodeString Buf = Output;
  421. UnicodeString Buf2;
  422. if (ExtractMainInstructions(Buf, Buf2))
  423. {
  424. throw ExtException(Output, EmptyStr, HelpKeyword);
  425. }
  426. else
  427. {
  428. UnicodeString Message = MainInstructions(FMTLOAD(COMMAND_FAILED_CODEONLY, (static_cast<int>(ExitCode))));
  429. throw ExtException(Message, Output, HelpKeyword);
  430. }
  431. }
  432. }
  433. }
  434. //---------------------------------------------------------------------------
  435. void __fastcall ExecuteProcessChecked(
  436. const UnicodeString & Command, const UnicodeString & HelpKeyword, UnicodeString * Output)
  437. {
  438. if (Output == NULL)
  439. {
  440. ExecuteShellChecked(Command);
  441. }
  442. else
  443. {
  444. DoExecuteProcessAndReadOutput(Command, HelpKeyword, *Output);
  445. }
  446. }
  447. //---------------------------------------------------------------------------
  448. void __fastcall ExecuteProcessCheckedAndWait(
  449. const UnicodeString & Command, const UnicodeString & HelpKeyword, UnicodeString * Output)
  450. {
  451. if (Output == NULL)
  452. {
  453. ExecuteShellCheckedAndWait(Command, &Application->ProcessMessages);
  454. }
  455. else
  456. {
  457. DoExecuteProcessAndReadOutput(Command, HelpKeyword, *Output);
  458. }
  459. }
  460. //---------------------------------------------------------------------------
  461. bool __fastcall IsKeyPressed(int VirtualKey)
  462. {
  463. return FLAGSET(GetAsyncKeyState(VirtualKey), 0x8000);
  464. }
  465. //---------------------------------------------------------------------------
  466. bool __fastcall UseAlternativeFunction()
  467. {
  468. return IsKeyPressed(VK_SHIFT);
  469. }
  470. //---------------------------------------------------------------------------
  471. bool __fastcall OpenInNewWindow()
  472. {
  473. return UseAlternativeFunction();
  474. }
  475. //---------------------------------------------------------------------------
  476. void ExecuteSelf(const UnicodeString & Params)
  477. {
  478. ExecuteShellChecked(Application->ExeName, Params);
  479. }
  480. //---------------------------------------------------------------------------
  481. void __fastcall ExecuteNewInstance(const UnicodeString & Param, const UnicodeString & AdditionalParams)
  482. {
  483. UnicodeString Arg;
  484. if (!Param.IsEmpty())
  485. {
  486. Arg = FORMAT(L"\"%s\"", (Param));
  487. }
  488. UnicodeString Space(L" ");
  489. AddToList(Arg, GetIniFileParam(), Space);
  490. AddToList(Arg, TProgramParams::FormatSwitch(NEWINSTANCE_SWICH), Space);
  491. AddToList(Arg, AdditionalParams, Space);
  492. ExecuteSelf(Arg);
  493. }
  494. //---------------------------------------------------------------------------
  495. IShellLink * __fastcall CreateDesktopShortCut(const UnicodeString & Name,
  496. const UnicodeString &File, const UnicodeString & Params, const UnicodeString & Description,
  497. int SpecialFolder, int IconIndex, bool Return)
  498. {
  499. IShellLink* pLink = NULL;
  500. if (SpecialFolder < 0)
  501. {
  502. SpecialFolder = CSIDL_DESKTOPDIRECTORY;
  503. }
  504. try
  505. {
  506. OleCheck(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **) &pLink));
  507. try
  508. {
  509. pLink->SetPath(File.c_str());
  510. pLink->SetDescription(Description.c_str());
  511. pLink->SetArguments(Params.c_str());
  512. pLink->SetShowCmd(SW_SHOW);
  513. // Explicitly setting icon file,
  514. // without this icons are not shown at least in Windows 7 jumplist
  515. pLink->SetIconLocation(File.c_str(), IconIndex);
  516. IPersistFile* pPersistFile;
  517. if (!Return &&
  518. SUCCEEDED(pLink->QueryInterface(IID_IPersistFile, (void **)&pPersistFile)))
  519. {
  520. try
  521. {
  522. LPMALLOC ShellMalloc;
  523. LPITEMIDLIST DesktopPidl;
  524. wchar_t DesktopDir[MAX_PATH];
  525. OleCheck(SHGetMalloc(&ShellMalloc));
  526. try
  527. {
  528. OleCheck(SHGetSpecialFolderLocation(NULL, SpecialFolder, &DesktopPidl));
  529. OleCheck(SHGetPathFromIDList(DesktopPidl, DesktopDir));
  530. }
  531. __finally
  532. {
  533. ShellMalloc->Free(DesktopPidl);
  534. ShellMalloc->Release();
  535. }
  536. WideString strShortCutLocation(DesktopDir);
  537. // Name can contain even path (e.g. to create quick launch icon)
  538. strShortCutLocation += UnicodeString(L"\\") + Name + L".lnk";
  539. OleCheck(pPersistFile->Save(strShortCutLocation.c_bstr(), TRUE));
  540. }
  541. __finally
  542. {
  543. pPersistFile->Release();
  544. }
  545. }
  546. // this is necessary for Windows 7 taskbar jump list links
  547. IPropertyStore * PropertyStore;
  548. if (SUCCEEDED(pLink->QueryInterface(IID_IPropertyStore, (void**)&PropertyStore)))
  549. {
  550. PROPVARIANT Prop;
  551. Prop.vt = VT_LPWSTR;
  552. Prop.pwszVal = Name.c_str();
  553. PropertyStore->SetValue(PKEY_Title, Prop);
  554. PropertyStore->Commit();
  555. PropertyStore->Release();
  556. }
  557. }
  558. catch(...)
  559. {
  560. pLink->Release();
  561. throw;
  562. }
  563. if (!Return)
  564. {
  565. pLink->Release();
  566. pLink = NULL;
  567. }
  568. }
  569. catch(Exception & E)
  570. {
  571. throw ExtException(&E, LoadStr(CREATE_SHORTCUT_ERROR));
  572. }
  573. return pLink;
  574. }
  575. //---------------------------------------------------------------------------
  576. UnicodeString GetIniFileParam()
  577. {
  578. UnicodeString ParamValue = Configuration->GetIniFileParamValue();
  579. UnicodeString Result;
  580. if (!ParamValue.IsEmpty())
  581. {
  582. Result = TProgramParams::FormatSwitch(INI_SWITCH) + L"=" + AddQuotes(ParamValue);
  583. }
  584. return Result;
  585. }
  586. //---------------------------------------------------------------------------
  587. IShellLink * __fastcall CreateAppDesktopShortCut(
  588. const UnicodeString & Name, const UnicodeString & AParams, const UnicodeString & Description,
  589. int SpecialFolder, int IconIndex, bool Return)
  590. {
  591. UnicodeString Params = GetIniFileParam();
  592. AddToList(Params, AParams, L" ");
  593. return CreateDesktopShortCut(Name, Application->ExeName, Params, Description, SpecialFolder, IconIndex, Return);
  594. }
  595. //---------------------------------------------------------------------------
  596. IShellLink * __fastcall CreateDesktopSessionShortCut(
  597. const UnicodeString & SessionName, UnicodeString Name,
  598. const UnicodeString & AdditionalParams, int SpecialFolder, int IconIndex,
  599. bool Return)
  600. {
  601. bool DefaultsOnly;
  602. UnicodeString InfoTip;
  603. bool IsFolder = StoredSessions->IsFolder(SessionName);
  604. bool IsWorkspace = StoredSessions->IsWorkspace(SessionName);
  605. if (IsFolder || IsWorkspace)
  606. {
  607. InfoTip = FMTLOAD(
  608. (IsFolder ? SHORTCUT_INFO_TIP_FOLDER : SHORTCUT_INFO_TIP_WORKSPACE),
  609. (SessionName));
  610. if (Name.IsEmpty())
  611. {
  612. // no slashes in filename
  613. Name = UnixExtractFileName(SessionName);
  614. }
  615. }
  616. else
  617. {
  618. // this should not be done for workspaces and folders
  619. TSessionData * SessionData =
  620. StoredSessions->ParseUrl(EncodeUrlString(SessionName), NULL, DefaultsOnly);
  621. InfoTip =
  622. FMTLOAD(SHORTCUT_INFO_TIP, (SessionName, SessionData->InfoTip));
  623. if (Name.IsEmpty())
  624. {
  625. // no slashes in filename
  626. Name = SessionData->LocalName;
  627. }
  628. delete SessionData;
  629. }
  630. return
  631. CreateAppDesktopShortCut(ValidLocalFileName(Name),
  632. FORMAT(L"\"%s\"%s%s", (EncodeUrlString(SessionName), (AdditionalParams.IsEmpty() ? L"" : L" "), AdditionalParams)),
  633. InfoTip, SpecialFolder, IconIndex, Return);
  634. }
  635. //---------------------------------------------------------------------------
  636. void ValidateMask(const UnicodeString & Mask, int ForceDirectoryMasks)
  637. {
  638. TFileMasks Masks(ForceDirectoryMasks);
  639. Masks = Mask;
  640. }
  641. //---------------------------------------------------------------------------
  642. template<class TEditControl>
  643. void __fastcall ValidateMaskEditT(const UnicodeString & Mask, TEditControl * Edit, int ForceDirectoryMasks)
  644. {
  645. DebugAssert(Edit != NULL);
  646. if (!IsCancelButtonBeingClicked(Edit))
  647. {
  648. try
  649. {
  650. ValidateMask(Mask, ForceDirectoryMasks);
  651. }
  652. catch(EFileMasksException & E)
  653. {
  654. ShowExtendedException(&E);
  655. Edit->SetFocus();
  656. // This does not work for TEdit and TMemo (descendants of TCustomEdit) anymore,
  657. // as it re-selects whole text on exception in TCustomEdit.CMExit
  658. Edit->SelStart = E.ErrorStart - 1;
  659. Edit->SelLength = E.ErrorLen;
  660. Abort();
  661. }
  662. }
  663. }
  664. //---------------------------------------------------------------------------
  665. void __fastcall ValidateMaskEdit(TComboBox * Edit)
  666. {
  667. ValidateMaskEditT(Edit->Text, Edit, -1);
  668. }
  669. //---------------------------------------------------------------------------
  670. void __fastcall ValidateMaskEdit(TEdit * Edit)
  671. {
  672. ValidateMaskEditT(Edit->Text, Edit, -1);
  673. }
  674. //---------------------------------------------------------------------------
  675. void __fastcall ValidateMaskEdit(TMemo * Edit, bool Directory)
  676. {
  677. if (!IsCancelButtonBeingClicked(Edit))
  678. {
  679. UnicodeString Mask = TFileMasks::ComposeMaskStr(GetUnwrappedMemoLines(Edit), Directory);
  680. ValidateMaskEditT(Mask, Edit, Directory ? 1 : 0);
  681. }
  682. }
  683. //---------------------------------------------------------------------------
  684. TStrings * __fastcall GetUnwrappedMemoLines(TMemo * Memo)
  685. {
  686. // This removes soft linebreaks when text in memo wraps
  687. // (Memo->Lines includes soft linebreaks, while Memo->Text does not)
  688. return TextToStringList(Memo->Text);
  689. }
  690. //---------------------------------------------------------------------------
  691. void __fastcall ExitActiveControl(TForm * Form)
  692. {
  693. if (Form->ActiveControl != NULL)
  694. {
  695. TNotifyEvent OnExit = ((TEdit*)Form->ActiveControl)->OnExit;
  696. if (OnExit != NULL)
  697. {
  698. OnExit(Form->ActiveControl);
  699. }
  700. }
  701. }
  702. //---------------------------------------------------------------------------
  703. bool __fastcall IsWinSCPUrl(const UnicodeString & Url)
  704. {
  705. UnicodeString HomePageUrl = LoadStr(HOMEPAGE_URL);
  706. UnicodeString HttpHomePageUrl = ChangeUrlProtocol(HomePageUrl, HttpProtocol);
  707. DebugAssert(HomePageUrl != HttpHomePageUrl);
  708. return
  709. StartsText(HomePageUrl, Url) ||
  710. StartsText(HttpHomePageUrl, Url);
  711. }
  712. //---------------------------------------------------------------------------
  713. UnicodeString __fastcall SecureUrl(const UnicodeString & Url)
  714. {
  715. UnicodeString Result = Url;
  716. if (IsWinSCPUrl(Url) && IsHttpUrl(Url))
  717. {
  718. Result = ChangeUrlProtocol(Result, HttpsProtocol);
  719. }
  720. return Result;
  721. }
  722. //---------------------------------------------------------------------------
  723. void ShellOpen(const UnicodeString & Param)
  724. {
  725. if (!CopyCommandToClipboard(Param))
  726. {
  727. ShellExecute(Application->Handle, L"open", Param.c_str(), NULL, NULL, SW_SHOWNORMAL);
  728. }
  729. }
  730. //---------------------------------------------------------------------------
  731. void __fastcall OpenBrowser(UnicodeString URL)
  732. {
  733. if (IsWinSCPUrl(URL))
  734. {
  735. DebugAssert(!IsHttpUrl(URL));
  736. URL = CampaignUrl(URL);
  737. }
  738. // Rather arbitrary limit. Opening a URL over approx. 5 KB fails in Chrome, Firefox and Edge.
  739. const int URLLimit = 4*1024;
  740. if (URL.Length() > URLLimit)
  741. {
  742. URL.SetLength(URLLimit);
  743. }
  744. ShellOpen(URL);
  745. }
  746. //---------------------------------------------------------------------------
  747. void __fastcall OpenFolderInExplorer(const UnicodeString & Path)
  748. {
  749. if ((int)ShellExecute(Application->Handle, L"explore",
  750. static_cast<const wchar_t*>(Path.data()), NULL, NULL, SW_SHOWNORMAL) <= 32)
  751. {
  752. throw Exception(FMTLOAD(EXPLORE_LOCAL_DIR_ERROR, (Path)));
  753. }
  754. }
  755. //---------------------------------------------------------------------------
  756. void __fastcall OpenFileInExplorer(const UnicodeString & Path)
  757. {
  758. PCIDLIST_ABSOLUTE Folder = ILCreateFromPathW(ApiPath(Path).c_str());
  759. SHOpenFolderAndSelectItems(Folder, 0, NULL, 0);
  760. }
  761. //---------------------------------------------------------------------------
  762. void __fastcall ShowHelp(const UnicodeString & AHelpKeyword)
  763. {
  764. // see also AppendUrlParams
  765. UnicodeString HelpKeyword = AHelpKeyword;
  766. const wchar_t FragmentSeparator = L'#';
  767. UnicodeString HelpPath = CutToChar(HelpKeyword, FragmentSeparator, false);
  768. UnicodeString HelpUrl = FMTLOAD(DOCUMENTATION_KEYWORD_URL2, (HelpPath, Configuration->ProductVersion, GUIConfiguration->AppliedLocaleHex));
  769. AddToList(HelpUrl, HelpKeyword, FragmentSeparator);
  770. OpenBrowser(HelpUrl);
  771. }
  772. //---------------------------------------------------------------------------
  773. bool __fastcall IsFormatInClipboard(unsigned int Format)
  774. {
  775. bool Result = OpenClipboard(0);
  776. if (Result)
  777. {
  778. Result = IsClipboardFormatAvailable(Format);
  779. CloseClipboard();
  780. }
  781. return Result;
  782. }
  783. //---------------------------------------------------------------------------
  784. HANDLE __fastcall OpenTextFromClipboard(const wchar_t *& Text)
  785. {
  786. UnicodeString ErrorContext;
  787. try
  788. {
  789. HANDLE Result = NULL;
  790. ErrorContext = L"open";
  791. if (OpenClipboard(0))
  792. {
  793. // Check also for CF_TEXT?
  794. ErrorContext = L"getdata";
  795. Result = GetClipboardData(CF_UNICODETEXT);
  796. if (Result != NULL)
  797. {
  798. ErrorContext = L"lock";
  799. Text = static_cast<const wchar_t*>(GlobalLock(Result));
  800. }
  801. else
  802. {
  803. ErrorContext = L"close";
  804. CloseClipboard();
  805. }
  806. }
  807. return Result;
  808. }
  809. catch (EAccessViolation & E)
  810. {
  811. throw EAccessViolation(AddContextToExceptionMessage(E, ErrorContext));
  812. }
  813. }
  814. //---------------------------------------------------------------------------
  815. void __fastcall CloseTextFromClipboard(HANDLE Handle)
  816. {
  817. UnicodeString ErrorContext;
  818. try
  819. {
  820. if (Handle != NULL)
  821. {
  822. ErrorContext = "unlock";
  823. GlobalUnlock(Handle);
  824. }
  825. ErrorContext = "close";
  826. CloseClipboard();
  827. }
  828. catch (EAccessViolation & E)
  829. {
  830. throw EAccessViolation(AddContextToExceptionMessage(E, ErrorContext));
  831. }
  832. }
  833. //---------------------------------------------------------------------------
  834. bool __fastcall TextFromClipboard(UnicodeString & Text, bool Trim)
  835. {
  836. UnicodeString ErrorContext;
  837. try
  838. {
  839. const wchar_t * AText = NULL;
  840. ErrorContext = L"open";
  841. HANDLE Handle = OpenTextFromClipboard(AText);
  842. bool Result = (Handle != NULL);
  843. if (Result)
  844. {
  845. // For all current uses (URL pasting, key/fingerprint pasting, known_hosts pasting, "more messages" copying,
  846. // permissions pasting), 64KB is large enough.
  847. const int Limit = 64*1024;
  848. ErrorContext = L"size";
  849. size_t Size = GlobalSize(Handle);
  850. int Len = (Size / sizeof(*AText)) - 1;
  851. if (Len > Limit)
  852. {
  853. ErrorContext = FORMAT(L"substring(%d,%d)", (int(Size), Len));
  854. Text = UnicodeString(AText, Limit);
  855. }
  856. else
  857. {
  858. ErrorContext = FORMAT(L"string(%d,%d)", (int(Size), Len));
  859. Text = AText;
  860. }
  861. if (Trim)
  862. {
  863. ErrorContext = L"trim";
  864. Text = Text.Trim();
  865. }
  866. ErrorContext = L"close";
  867. CloseTextFromClipboard(Handle);
  868. }
  869. return Result;
  870. }
  871. catch (EAccessViolation & E)
  872. {
  873. throw EAccessViolation(AddContextToExceptionMessage(E, ErrorContext));
  874. }
  875. }
  876. //---------------------------------------------------------------------------
  877. bool __fastcall NonEmptyTextFromClipboard(UnicodeString & Text)
  878. {
  879. return
  880. TextFromClipboard(Text, true) &&
  881. !Text.IsEmpty();
  882. }
  883. //---------------------------------------------------------------------------
  884. static bool __fastcall GetResource(
  885. const UnicodeString ResName, void *& Content, unsigned long & Size)
  886. {
  887. HRSRC Resource = FindResourceEx(HInstance, RT_RCDATA, ResName.c_str(),
  888. MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
  889. bool Result = (Resource != NULL);
  890. if (Result)
  891. {
  892. Size = SizeofResource(HInstance, Resource);
  893. if (!Size)
  894. {
  895. throw Exception(FORMAT(L"Cannot get size of resource %s", (ResName)));
  896. }
  897. Content = LoadResource(HInstance, Resource);
  898. if (!Content)
  899. {
  900. throw Exception(FORMAT(L"Cannot read resource %s", (ResName)));
  901. }
  902. Content = LockResource(Content);
  903. if (!Content)
  904. {
  905. throw Exception(FORMAT(L"Cannot lock resource %s", (ResName)));
  906. }
  907. }
  908. return Result;
  909. }
  910. //---------------------------------------------------------------------------
  911. bool __fastcall DumpResourceToFile(const UnicodeString ResName,
  912. const UnicodeString FileName)
  913. {
  914. void * Content;
  915. unsigned long Size;
  916. bool Result = GetResource(ResName, Content, Size);
  917. if (Result)
  918. {
  919. FILE * f = _wfopen(ApiPath(FileName).c_str(), L"wb");
  920. if (!f)
  921. {
  922. throw Exception(FORMAT(L"Cannot create file %s", (FileName)));
  923. }
  924. if (fwrite(Content, 1, Size, f) != Size)
  925. {
  926. throw Exception(FORMAT(L"Cannot write to file %s", (FileName)));
  927. }
  928. fclose(f);
  929. }
  930. return Result;
  931. }
  932. //---------------------------------------------------------------------------
  933. UnicodeString __fastcall ReadResource(const UnicodeString ResName)
  934. {
  935. void * Content;
  936. unsigned long Size;
  937. UnicodeString Result;
  938. if (GetResource(ResName, Content, Size))
  939. {
  940. Result = UnicodeString(UTF8String(static_cast<char*>(Content), Size));
  941. }
  942. return Result;
  943. }
  944. //---------------------------------------------------------------------------
  945. template <class T>
  946. void __fastcall BrowseForExecutableT(T * Control, UnicodeString Title,
  947. UnicodeString Filter, bool FileNameCommand, bool Escape)
  948. {
  949. UnicodeString Executable, Program, Params, Dir;
  950. Executable = Control->Text;
  951. if (FileNameCommand)
  952. {
  953. ReformatFileNameCommand(Executable);
  954. }
  955. SplitCommand(Executable, Program, Params, Dir);
  956. TOpenDialog * FileDialog = new TOpenDialog(Application);
  957. try
  958. {
  959. if (Escape)
  960. {
  961. Program = ReplaceStr(Program, L"\\\\", L"\\");
  962. }
  963. UnicodeString ExpandedProgram = ExpandEnvironmentVariables(Program);
  964. FileDialog->FileName = ExpandedProgram;
  965. UnicodeString InitialDir = ExtractFilePath(ExpandedProgram);
  966. if (!InitialDir.IsEmpty())
  967. {
  968. FileDialog->InitialDir = InitialDir;
  969. }
  970. FileDialog->Filter = Filter;
  971. FileDialog->Title = Title;
  972. if (FileDialog->Execute())
  973. {
  974. TNotifyEvent PrevOnChange = Control->OnChange;
  975. Control->OnChange = NULL;
  976. try
  977. {
  978. // preserve unexpanded file, if the destination has not changed actually
  979. if (!IsPathToSameFile(ExpandedProgram, FileDialog->FileName))
  980. {
  981. Program = FileDialog->FileName;
  982. if (Escape)
  983. {
  984. Program = ReplaceStr(Program, L"\\", L"\\\\");
  985. }
  986. }
  987. Control->Text = FormatCommand(Program, Params);
  988. }
  989. __finally
  990. {
  991. Control->OnChange = PrevOnChange;
  992. }
  993. if (Control->OnExit != NULL)
  994. {
  995. Control->OnExit(Control);
  996. }
  997. }
  998. }
  999. __finally
  1000. {
  1001. delete FileDialog;
  1002. }
  1003. }
  1004. //---------------------------------------------------------------------------
  1005. void __fastcall BrowseForExecutable(TEdit * Control, UnicodeString Title,
  1006. UnicodeString Filter, bool FileNameCommand, bool Escape)
  1007. {
  1008. BrowseForExecutableT(Control, Title, Filter, FileNameCommand, Escape);
  1009. }
  1010. //---------------------------------------------------------------------------
  1011. void __fastcall BrowseForExecutable(TComboBox * Control, UnicodeString Title,
  1012. UnicodeString Filter, bool FileNameCommand, bool Escape)
  1013. {
  1014. BrowseForExecutableT(Control, Title, Filter, FileNameCommand, Escape);
  1015. }
  1016. //---------------------------------------------------------------------------
  1017. bool __fastcall FontDialog(TFont * Font)
  1018. {
  1019. bool Result;
  1020. TFontDialog * Dialog = new TFontDialog(Application);
  1021. try
  1022. {
  1023. Dialog->Device = fdScreen;
  1024. Dialog->Options = TFontDialogOptions() << fdForceFontExist;
  1025. Dialog->Font = Font;
  1026. Result = Dialog->Execute();
  1027. if (Result)
  1028. {
  1029. Font->Assign(Dialog->Font);
  1030. }
  1031. }
  1032. __finally
  1033. {
  1034. delete Dialog;
  1035. }
  1036. return Result;
  1037. }
  1038. //---------------------------------------------------------------------------
  1039. bool __fastcall SaveDialog(UnicodeString Title, UnicodeString Filter,
  1040. UnicodeString DefaultExt, UnicodeString & FileName)
  1041. {
  1042. bool Result;
  1043. #if 0
  1044. TFileSaveDialog * Dialog = new TFileSaveDialog(Application);
  1045. try
  1046. {
  1047. Dialog->Title = Title;
  1048. FilterToFileTypes(Filter, Dialog->FileTypes);
  1049. Dialog->DefaultExtension = DefaultExt;
  1050. Dialog->FileName = FileName;
  1051. UnicodeString DefaultFolder = ExtractFilePath(FileName);
  1052. if (!DefaultFolder.IsEmpty())
  1053. {
  1054. Dialog->DefaultFolder = DefaultFolder;
  1055. }
  1056. Dialog->Options = Dialog->Options << fdoOverWritePrompt << fdoForceFileSystem <<
  1057. fdoPathMustExist << fdoNoReadOnlyReturn;
  1058. Result = Dialog->Execute();
  1059. if (Result)
  1060. {
  1061. FileName = Dialog->FileName;
  1062. }
  1063. }
  1064. __finally
  1065. {
  1066. delete Dialog;
  1067. }
  1068. #else
  1069. TSaveDialog * Dialog = new TSaveDialog(Application);
  1070. try
  1071. {
  1072. Dialog->Title = Title;
  1073. Dialog->Filter = Filter;
  1074. Dialog->DefaultExt = DefaultExt;
  1075. Dialog->FileName = FileName;
  1076. UnicodeString InitialDir = ExtractFilePath(FileName);
  1077. if (!InitialDir.IsEmpty())
  1078. {
  1079. Dialog->InitialDir = InitialDir;
  1080. }
  1081. Dialog->Options = Dialog->Options << ofOverwritePrompt << ofPathMustExist <<
  1082. ofNoReadOnlyReturn;
  1083. Result = Dialog->Execute();
  1084. if (Result)
  1085. {
  1086. FileName = Dialog->FileName;
  1087. }
  1088. }
  1089. __finally
  1090. {
  1091. delete Dialog;
  1092. }
  1093. #endif
  1094. return Result;
  1095. }
  1096. //---------------------------------------------------------------------------
  1097. void __fastcall CopyToClipboard(UnicodeString Text)
  1098. {
  1099. HANDLE Data;
  1100. void * DataPtr;
  1101. AppLogFmt(L"Copying text to clipboard [%d]", (Text.Length()));
  1102. if (OpenClipboard(0))
  1103. {
  1104. try
  1105. {
  1106. size_t Size = (Text.Length() + 1) * sizeof(wchar_t);
  1107. Data = GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, Size);
  1108. try
  1109. {
  1110. DataPtr = GlobalLock(Data);
  1111. try
  1112. {
  1113. memcpy(DataPtr, Text.c_str(), Size);
  1114. EmptyClipboard();
  1115. SetClipboardData(CF_UNICODETEXT, Data);
  1116. }
  1117. __finally
  1118. {
  1119. GlobalUnlock(Data);
  1120. }
  1121. }
  1122. catch(...)
  1123. {
  1124. GlobalFree(Data);
  1125. throw;
  1126. }
  1127. }
  1128. __finally
  1129. {
  1130. CloseClipboard();
  1131. }
  1132. AppLogFmt(L"Copied text to clipboard [%d]", (Text.Length()));
  1133. }
  1134. else
  1135. {
  1136. throw Exception(Vcl_Consts_SCannotOpenClipboard);
  1137. }
  1138. }
  1139. //---------------------------------------------------------------------------
  1140. void __fastcall CopyToClipboard(TStrings * Strings)
  1141. {
  1142. if (Strings->Count > 0)
  1143. {
  1144. CopyToClipboard(StringsToText(Strings));
  1145. }
  1146. }
  1147. //---------------------------------------------------------------------------
  1148. static void __fastcall AcquireShutDownPrivileges()
  1149. {
  1150. HANDLE Token;
  1151. // Get a token for this process.
  1152. Win32Check(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token));
  1153. TOKEN_PRIVILEGES Priv;
  1154. ZeroMemory(&Priv, sizeof(Priv));
  1155. // Get the LUID for the shutdown privilege.
  1156. // For hibernate/suspend, you need the same:
  1157. // https://stackoverflow.com/q/959589/850848
  1158. Win32Check(LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &Priv.Privileges[0].Luid));
  1159. Priv.PrivilegeCount = 1; // one privilege to set
  1160. Priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  1161. // Get the shutdown privilege for this process.
  1162. Win32Check(AdjustTokenPrivileges(Token, FALSE, &Priv, 0, (PTOKEN_PRIVILEGES)NULL, 0));
  1163. }
  1164. //---------------------------------------------------------------------------
  1165. void __fastcall ShutDownWindows()
  1166. {
  1167. AcquireShutDownPrivileges();
  1168. // Shut down the system and force all applications to close.
  1169. Win32Check(ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF,
  1170. SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED));
  1171. }
  1172. //---------------------------------------------------------------------------
  1173. void __fastcall SuspendWindows()
  1174. {
  1175. AcquireShutDownPrivileges();
  1176. // https://learn.microsoft.com/en-us/windows/win32/api/powrprof/nf-powrprof-setsuspendstate
  1177. Win32Check(SetSuspendState(false, false, false));
  1178. }
  1179. //---------------------------------------------------------------------------
  1180. void __fastcall EditSelectBaseName(HWND Edit)
  1181. {
  1182. UnicodeString Text;
  1183. Text.SetLength(GetWindowTextLength(Edit) + 1);
  1184. GetWindowText(Edit, Text.c_str(), Text.Length());
  1185. int P = Text.LastDelimiter(L".");
  1186. if (P > 0)
  1187. {
  1188. // SendMessage does not work, if edit control is not fully
  1189. // initialized yet
  1190. PostMessage(Edit, EM_SETSEL, 0, P - 1);
  1191. }
  1192. }
  1193. //---------------------------------------------------------------------------
  1194. UnicodeString GetConvertedKeyFileName(const UnicodeString & FileName)
  1195. {
  1196. return ChangeFileExt(FileName, FORMAT(L".%s", (PuttyKeyExt)));
  1197. }
  1198. //---------------------------------------------------------------------------
  1199. static bool TryAddMatchingKeyCertificate(
  1200. TPrivateKey * PrivateKey, const UnicodeString & CertificateFileName, UnicodeString & Result)
  1201. {
  1202. if (FileExists(CertificateFileName))
  1203. {
  1204. try
  1205. {
  1206. AddCertificateToKey(PrivateKey, CertificateFileName);
  1207. Result = CertificateFileName;
  1208. }
  1209. catch (Exception & E)
  1210. {
  1211. AppLogFmt(L"Cannot add certificate from auto-detected \"%s\": %s", (CertificateFileName, E.Message));
  1212. }
  1213. }
  1214. return !Result.IsEmpty();
  1215. }
  1216. //---------------------------------------------------------------------------
  1217. UnicodeString AddMatchingKeyCertificate(TPrivateKey * PrivateKey, const UnicodeString & FileName)
  1218. {
  1219. UnicodeString CertificateFileName = FileName;
  1220. UnicodeString S = FORMAT(L".%s", (PuttyKeyExt));
  1221. if (EndsText(S, CertificateFileName))
  1222. {
  1223. CertificateFileName.SetLength(CertificateFileName.Length() - S.Length());
  1224. }
  1225. UnicodeString Result;
  1226. TryAddMatchingKeyCertificate(PrivateKey, CertificateFileName + L"-cert.pub", Result) ||
  1227. TryAddMatchingKeyCertificate(PrivateKey, CertificateFileName + L".pub-aadcert.pub", Result);
  1228. return Result;
  1229. }
  1230. //---------------------------------------------------------------------------
  1231. static void __fastcall ConvertKey(UnicodeString & FileName, TKeyType Type)
  1232. {
  1233. UnicodeString Passphrase;
  1234. UnicodeString Comment;
  1235. if (IsKeyEncrypted(Type, FileName, Comment))
  1236. {
  1237. if (!InputDialog(
  1238. LoadStr(PASSPHRASE_TITLE),
  1239. FORMAT(LoadStr(PROMPT_KEY_PASSPHRASE), (Comment)),
  1240. Passphrase, HELP_NONE, NULL, false, NULL, false))
  1241. {
  1242. Abort();
  1243. }
  1244. }
  1245. TPrivateKey * PrivateKey = LoadKey(Type, FileName, Passphrase);
  1246. try
  1247. {
  1248. AppLogFmt(L"Loaded key from \"%s\".", (FileName));
  1249. UnicodeString CertificateMessage;
  1250. UnicodeString CertificateFileName = AddMatchingKeyCertificate(PrivateKey, FileName);
  1251. if (!CertificateFileName.IsEmpty())
  1252. {
  1253. AppLogFmt(L"Added certificate from auto-detected \"%s\".", (CertificateFileName));
  1254. CertificateMessage = L"\n" + FMTLOAD(CERTIFICATE_ADDED, (CertificateFileName));
  1255. }
  1256. FileName = GetConvertedKeyFileName(FileName);
  1257. if (!SaveDialog(LoadStr(CONVERTKEY_SAVE_TITLE), LoadStr(CONVERTKEY_SAVE_FILTER), PuttyKeyExt, FileName))
  1258. {
  1259. Abort();
  1260. }
  1261. SaveKey(ktSSH2, FileName, Passphrase, PrivateKey);
  1262. AppLogFmt(L"Saved converted key to \"%s\".", (FileName));
  1263. UnicodeString Message =
  1264. MainInstructions(FMTLOAD(CONVERTKEY_SAVED, (FileName))) +
  1265. CertificateMessage;
  1266. MessageDialog(Message, qtInformation, qaOK);
  1267. }
  1268. __finally
  1269. {
  1270. FreeKey(PrivateKey);
  1271. }
  1272. }
  1273. //---------------------------------------------------------------------------
  1274. void DoVerifyKey(UnicodeString & FileName, bool Convert, UnicodeString & Message, TStrings *& MoreMessages, UnicodeString & HelpKeyword)
  1275. {
  1276. std::unique_ptr<TStrings> AMoreMessages;
  1277. if (!FileName.Trim().IsEmpty())
  1278. {
  1279. FileName = ExpandEnvironmentVariables(FileName);
  1280. TKeyType Type = KeyType(FileName);
  1281. // reason _wfopen failed
  1282. int Error = errno;
  1283. HelpKeyword = HELP_LOGIN_KEY_TYPE;
  1284. UnicodeString PuttygenPath;
  1285. switch (Type)
  1286. {
  1287. case ktOpenSSHPEM:
  1288. case ktOpenSSHNew:
  1289. case ktSSHCom:
  1290. {
  1291. UnicodeString TypeName = ((Type == ktOpenSSHPEM) || (Type == ktOpenSSHNew)) ? L"OpenSSH" : L"ssh.com";
  1292. Message = FMTLOAD(KEY_TYPE_UNSUPPORTED2, (FileName, TypeName));
  1293. if (Convert)
  1294. {
  1295. Configuration->Usage->Inc(L"PrivateKeyConvertSuggestionsNative");
  1296. UnicodeString ConvertMessage = FMTLOAD(KEY_TYPE_CONVERT4, (TypeName, RemoveMainInstructionsTag(Message)));
  1297. Message = EmptyStr;
  1298. if (MoreMessageDialog(ConvertMessage, NULL, qtConfirmation, qaOK | qaCancel, HelpKeyword) == qaOK)
  1299. {
  1300. ConvertKey(FileName, Type);
  1301. Configuration->Usage->Inc(L"PrivateKeyConverted");
  1302. }
  1303. else
  1304. {
  1305. Abort();
  1306. }
  1307. }
  1308. else
  1309. {
  1310. HelpKeyword = HELP_KEY_TYPE_UNSUPPORTED;
  1311. }
  1312. }
  1313. break;
  1314. case ktSSH1:
  1315. Message = MainInstructions(FMTLOAD(KEY_TYPE_SSH1, (FileName)));
  1316. break;
  1317. case ktSSH2:
  1318. Message = TestKey(Type, FileName);
  1319. break;
  1320. case ktSSH1Public:
  1321. case ktSSH2PublicRFC4716:
  1322. case ktSSH2PublicOpenSSH:
  1323. // noop
  1324. break;
  1325. case ktUnopenable:
  1326. Message = MainInstructions(FMTLOAD(KEY_TYPE_UNOPENABLE, (FileName)));
  1327. if (Error != ERROR_SUCCESS)
  1328. {
  1329. AMoreMessages.reset(TextToStringList(SysErrorMessageForError(Error)));
  1330. }
  1331. break;
  1332. default:
  1333. DebugFail();
  1334. // fallthru
  1335. case ktUnknown:
  1336. Message = MainInstructions(FMTLOAD(KEY_TYPE_UNKNOWN2, (FileName)));
  1337. break;
  1338. }
  1339. }
  1340. MoreMessages = AMoreMessages.release();
  1341. }
  1342. //---------------------------------------------------------------------------
  1343. static void __fastcall DoVerifyKey(UnicodeString & FileName, bool Convert, bool CanIgnore)
  1344. {
  1345. TStrings * AMoreMessages;
  1346. UnicodeString Message;
  1347. UnicodeString HelpKeyword;
  1348. DoVerifyKey(FileName, Convert, Message, AMoreMessages, HelpKeyword);
  1349. std::unique_ptr<TStrings> MoreMessages(AMoreMessages);
  1350. if (!Message.IsEmpty())
  1351. {
  1352. Configuration->Usage->Inc(L"PrivateKeySelectErrors");
  1353. unsigned int Answers = (CanIgnore ? (qaIgnore | qaAbort) : qaOK);
  1354. if (MoreMessageDialog(Message, MoreMessages.get(), qtWarning, Answers, HelpKeyword) != qaIgnore)
  1355. {
  1356. Abort();
  1357. }
  1358. }
  1359. }
  1360. //---------------------------------------------------------------------------
  1361. void __fastcall VerifyAndConvertKey(UnicodeString & FileName, bool CanIgnore)
  1362. {
  1363. DoVerifyKey(FileName, true, CanIgnore);
  1364. }
  1365. //---------------------------------------------------------------------------
  1366. void __fastcall VerifyKey(const UnicodeString & FileName)
  1367. {
  1368. UnicodeString AFileName(FileName);
  1369. DoVerifyKey(AFileName, false, true);
  1370. }
  1371. //---------------------------------------------------------------------------
  1372. void __fastcall VerifyCertificate(const UnicodeString & FileName)
  1373. {
  1374. if (!FileName.Trim().IsEmpty())
  1375. {
  1376. try
  1377. {
  1378. CheckCertificate(FileName);
  1379. }
  1380. catch (Exception & E)
  1381. {
  1382. if (ExceptionMessageDialog(&E, qtWarning, L"", qaIgnore | qaAbort) == qaAbort)
  1383. {
  1384. Abort();
  1385. }
  1386. }
  1387. }
  1388. }
  1389. //---------------------------------------------------------------------------
  1390. bool __fastcall DetectSystemExternalEditor(
  1391. bool AllowDefaultEditor,
  1392. UnicodeString & Executable, UnicodeString & ExecutableDescription,
  1393. UnicodeString & UsageState, bool & TryNextTime)
  1394. {
  1395. bool Result = false;
  1396. UnicodeString TempName = ExcludeTrailingBackslash(WinConfiguration->TemporaryDir()) + L".txt";
  1397. if (FileExists(ApiPath(TempName)))
  1398. {
  1399. TryNextTime = true;
  1400. UsageState = "F";
  1401. }
  1402. else
  1403. {
  1404. unsigned int File = FileCreate(ApiPath(TempName));
  1405. if (File == (unsigned int)INVALID_HANDLE_VALUE)
  1406. {
  1407. TryNextTime = true;
  1408. UsageState = "F";
  1409. }
  1410. else
  1411. {
  1412. FileClose(File);
  1413. try
  1414. {
  1415. wchar_t ExecutableBuf[MAX_PATH];
  1416. if (!SUCCEEDED(FindExecutable(TempName.c_str(), NULL, ExecutableBuf)))
  1417. {
  1418. UsageState = "N";
  1419. }
  1420. else
  1421. {
  1422. Executable = ExecutableBuf;
  1423. if (Executable.IsEmpty() ||
  1424. !FileExists(ApiPath(Executable)))
  1425. {
  1426. UsageState = "N";
  1427. }
  1428. else
  1429. {
  1430. UnicodeString ExecutableName = ExtractFileName(Executable);
  1431. if (!AllowDefaultEditor &&
  1432. SameText(ExecutableName, TEditorPreferences::GetDefaultExternalEditor()))
  1433. {
  1434. UsageState = "P";
  1435. Executable = L"";
  1436. }
  1437. else if (SameText(ExecutableName, "openwith.exe"))
  1438. {
  1439. UsageState = "W";
  1440. Executable = L"";
  1441. }
  1442. else
  1443. {
  1444. try
  1445. {
  1446. ExecutableDescription = Configuration->GetFileDescription(Executable);
  1447. }
  1448. catch(...)
  1449. {
  1450. }
  1451. if (ExecutableDescription.IsEmpty())
  1452. {
  1453. ExecutableDescription = ExecutableName;
  1454. }
  1455. Result = true;
  1456. }
  1457. }
  1458. }
  1459. }
  1460. __finally
  1461. {
  1462. DeleteFile(ApiPath(TempName));
  1463. }
  1464. }
  1465. }
  1466. return Result;
  1467. }
  1468. //---------------------------------------------------------------------------
  1469. // this was moved to global scope in past in some attempt to fix crashes,
  1470. // not sure it really helped
  1471. static WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IEProxyInfo;
  1472. //---------------------------------------------------------------------------
  1473. static bool __fastcall GetProxyUrlFromIE(UnicodeString & Proxy)
  1474. {
  1475. bool Result = false;
  1476. memset(&IEProxyInfo, 0, sizeof(IEProxyInfo));
  1477. if (WinHttpGetIEProxyConfigForCurrentUser(&IEProxyInfo))
  1478. {
  1479. if (IEProxyInfo.lpszProxy != NULL)
  1480. {
  1481. UnicodeString IEProxy = IEProxyInfo.lpszProxy;
  1482. Proxy = L"";
  1483. while (Proxy.IsEmpty() && !IEProxy.IsEmpty())
  1484. {
  1485. UnicodeString Str = CutToChar(IEProxy, L';', true);
  1486. if (Str.Pos(L"=") == 0)
  1487. {
  1488. Proxy = Str;
  1489. }
  1490. else
  1491. {
  1492. UnicodeString Protocol = CutToChar(Str, L'=', true);
  1493. if (SameText(Protocol, L"http"))
  1494. {
  1495. Proxy = Str;
  1496. }
  1497. }
  1498. }
  1499. GlobalFree(IEProxyInfo.lpszProxy);
  1500. Result = true;
  1501. }
  1502. if (IEProxyInfo.lpszAutoConfigUrl != NULL)
  1503. {
  1504. GlobalFree(IEProxyInfo.lpszAutoConfigUrl);
  1505. }
  1506. if (IEProxyInfo.lpszProxyBypass != NULL)
  1507. {
  1508. GlobalFree(IEProxyInfo.lpszProxyBypass);
  1509. }
  1510. }
  1511. return Result;
  1512. }
  1513. //---------------------------------------------------------------------------
  1514. bool __fastcall AutodetectProxy(UnicodeString & HostName, int & PortNumber)
  1515. {
  1516. bool Result = false;
  1517. /* First we try for proxy info direct from the registry if
  1518. it's available. */
  1519. UnicodeString Proxy;
  1520. WINHTTP_PROXY_INFO ProxyInfo;
  1521. memset(&ProxyInfo, 0, sizeof(ProxyInfo));
  1522. if (WinHttpGetDefaultProxyConfiguration(&ProxyInfo))
  1523. {
  1524. if (ProxyInfo.lpszProxy != NULL)
  1525. {
  1526. Proxy = ProxyInfo.lpszProxy;
  1527. GlobalFree(ProxyInfo.lpszProxy);
  1528. Result = true;
  1529. }
  1530. if (ProxyInfo.lpszProxyBypass != NULL)
  1531. {
  1532. GlobalFree(ProxyInfo.lpszProxyBypass);
  1533. }
  1534. }
  1535. /* The next fallback is to get the proxy info from MSIE. This is also
  1536. usually much quicker than WinHttpGetProxyForUrl(), although sometimes
  1537. it seems to fall back to that, based on the longish delay involved.
  1538. Another issue with this is that it won't work in a service process
  1539. that isn't impersonating an interactive user (since there isn't a
  1540. current user), but in that case we just fall back to
  1541. WinHttpGetProxyForUrl() */
  1542. if (!Result)
  1543. {
  1544. Result = GetProxyUrlFromIE(Proxy);
  1545. }
  1546. if (Result)
  1547. {
  1548. if (Proxy.Trim().IsEmpty())
  1549. {
  1550. Result = false;
  1551. }
  1552. else
  1553. {
  1554. HostName = CutToChar(Proxy, L':', true);
  1555. PortNumber = StrToIntDef(Proxy, ProxyPortNumber);
  1556. AppLogFmt("Proxy autodetected: %s:%d", (HostName, PortNumber));
  1557. }
  1558. }
  1559. // We can also use WinHttpGetProxyForUrl, but it is lengthy
  1560. // See the source address of the code for example
  1561. return Result;
  1562. }
  1563. //---------------------------------------------------------------------------
  1564. //---------------------------------------------------------------------------
  1565. class TWinHelpTester : public TInterfacedObject, public IWinHelpTester
  1566. {
  1567. public:
  1568. virtual bool __fastcall CanShowALink(const UnicodeString ALink, const UnicodeString FileName);
  1569. virtual bool __fastcall CanShowTopic(const UnicodeString Topic, const UnicodeString FileName);
  1570. virtual bool __fastcall CanShowContext(const int Context, const UnicodeString FileName);
  1571. virtual TStringList * __fastcall GetHelpStrings(const UnicodeString ALink);
  1572. virtual UnicodeString __fastcall GetHelpPath();
  1573. virtual UnicodeString __fastcall GetDefaultHelpFile();
  1574. IUNKNOWN
  1575. };
  1576. //---------------------------------------------------------------------------
  1577. class TCustomHelpSelector : public TInterfacedObject, public IHelpSelector
  1578. {
  1579. public:
  1580. __fastcall TCustomHelpSelector(const UnicodeString & Name);
  1581. virtual int __fastcall SelectKeyword(TStrings * Keywords);
  1582. virtual int __fastcall TableOfContents(TStrings * Contents);
  1583. IUNKNOWN
  1584. private:
  1585. UnicodeString FName;
  1586. };
  1587. //---------------------------------------------------------------------------
  1588. void __fastcall AssignHelpSelector(IHelpSelector * HelpSelector)
  1589. {
  1590. _di_IHelpSystem HelpSystem;
  1591. if (GetHelpSystem(HelpSystem))
  1592. {
  1593. HelpSystem->AssignHelpSelector(HelpSelector);
  1594. }
  1595. }
  1596. //---------------------------------------------------------------------------
  1597. void __fastcall InitializeCustomHelp(ICustomHelpViewer * HelpViewer)
  1598. {
  1599. _di_IHelpManager HelpManager;
  1600. RegisterViewer(HelpViewer, HelpManager);
  1601. // Register dummy tester that disables win help
  1602. WinHelpTester = new TWinHelpTester();
  1603. AssignHelpSelector(new TCustomHelpSelector(HelpViewer->GetViewerName()));
  1604. }
  1605. //---------------------------------------------------------------------------
  1606. void __fastcall FinalizeCustomHelp()
  1607. {
  1608. AssignHelpSelector(NULL);
  1609. }
  1610. //---------------------------------------------------------------------------
  1611. //---------------------------------------------------------------------------
  1612. bool __fastcall TWinHelpTester::CanShowALink(
  1613. const UnicodeString DebugUsedArg(ALink), const UnicodeString DebugUsedArg(FileName))
  1614. {
  1615. return !Application->HelpFile.IsEmpty();
  1616. }
  1617. //---------------------------------------------------------------------------
  1618. bool __fastcall TWinHelpTester::CanShowTopic(
  1619. const UnicodeString DebugUsedArg(Topic), const UnicodeString DebugUsedArg(FileName))
  1620. {
  1621. DebugFail();
  1622. return !Application->HelpFile.IsEmpty();
  1623. }
  1624. //---------------------------------------------------------------------------
  1625. bool __fastcall TWinHelpTester::CanShowContext(
  1626. const int DebugUsedArg(Context), const UnicodeString DebugUsedArg(FileName))
  1627. {
  1628. DebugFail();
  1629. return !Application->HelpFile.IsEmpty();
  1630. }
  1631. //---------------------------------------------------------------------------
  1632. TStringList * __fastcall TWinHelpTester::GetHelpStrings(const UnicodeString ALink)
  1633. {
  1634. DebugFail();
  1635. TStringList * Result = new TStringList();
  1636. Result->Add(ViewerName + L": " + ALink);
  1637. return Result;
  1638. }
  1639. //---------------------------------------------------------------------------
  1640. UnicodeString __fastcall TWinHelpTester::GetHelpPath()
  1641. {
  1642. // never called on windows anyway
  1643. return ExtractFilePath(Application->HelpFile);
  1644. }
  1645. //---------------------------------------------------------------------------
  1646. UnicodeString __fastcall TWinHelpTester::GetDefaultHelpFile()
  1647. {
  1648. return Application->HelpFile;
  1649. }
  1650. //---------------------------------------------------------------------------
  1651. //---------------------------------------------------------------------------
  1652. __fastcall TCustomHelpSelector::TCustomHelpSelector(const UnicodeString & Name) :
  1653. FName(Name)
  1654. {
  1655. }
  1656. //---------------------------------------------------------------------------
  1657. int __fastcall TCustomHelpSelector::SelectKeyword(TStrings * /*Keywords*/)
  1658. {
  1659. DebugFail();
  1660. return 0;
  1661. }
  1662. //---------------------------------------------------------------------------
  1663. int __fastcall TCustomHelpSelector::TableOfContents(TStrings * Contents)
  1664. {
  1665. return Contents->IndexOf(FName);
  1666. }