PasTools.pas 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. unit PasTools;
  2. interface
  3. {$WARN SYMBOL_PLATFORM OFF}
  4. uses
  5. Windows, Types, Classes, ComCtrls, ExtCtrls, Controls, Dialogs, Forms, Messages, Graphics, SysUtils;
  6. function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
  7. function IsVistaHard: Boolean;
  8. function IsVista: Boolean;
  9. {$EXTERNALSYM IsWin7}
  10. function IsWin7: Boolean;
  11. {$EXTERNALSYM IsWin8}
  12. function IsWin8: Boolean;
  13. {$EXTERNALSYM CutToChar}
  14. function CutToChar(var Str: string; Ch: Char; Trim: Boolean): string;
  15. procedure FilterToFileTypes(Filter: string; FileTypes: TFileTypeItems);
  16. // Note that while we based our scaling on pixels-per-inch,
  17. // VCL actually scales based on font size
  18. const
  19. CM_DPICHANGED = WM_USER + $2000 + 10;
  20. function HasSystemParametersInfoForPixelsPerInch: Boolean;
  21. function SystemParametersInfoForPixelsPerInch(
  22. uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT; dpi: UINT): BOOL;
  23. function GetMonitorFromControl(Control: TControl): TMonitor;
  24. function GetMonitorPixelsPerInch(Monitor: TMonitor): Integer;
  25. function GetControlPixelsPerInch(Control: TControl): Integer;
  26. function GetComponentPixelsPerInch(Component: TComponent): Integer;
  27. function LoadDimension(Dimension: Integer; PixelsPerInch: Integer; Control: TControl): Integer;
  28. function StrToDimensionDef(Str: string; PixelsPerInch: Integer; Control: TControl; Default: Integer): Integer;
  29. function SaveDimension(Dimension: Integer): Integer;
  30. function DimensionToDefaultPixelsPerInch(Dimension: Integer): Integer;
  31. function ScaleByPixelsPerInch(Dimension: Integer; Monitor: TMonitor): Integer; overload;
  32. function ScaleByPixelsPerInch(Dimension: Integer; Control: TControl): Integer; overload;
  33. function ScaleByPixelsPerInchFromSystem(Dimension: Integer; Control: TControl): Integer;
  34. function LoadPixelsPerInch(S: string; Control: TControl): Integer;
  35. function SavePixelsPerInch(Control: TControl): string;
  36. function SaveDefaultPixelsPerInch: string;
  37. function CalculateTextHeight(Canvas: TCanvas): Integer;
  38. function ScaleByTextHeight(Control: TControl; Dimension: Integer): Integer;
  39. function ScaleByTextHeightRunTime(Control: TControl; Dimension: Integer): Integer;
  40. function ScaleByControlTextHeightRunTime(Canvas: TCanvas; Dimension: Integer): Integer;
  41. function GetSystemMetricsForControl(Control: TControl; nIndex: Integer): Integer;
  42. type
  43. TImageListSize = (ilsSmall, ilsLarge);
  44. procedure NeedShellImageLists;
  45. function ShellImageListForSize(Width: Integer): TImageList;
  46. function ShellImageListForControl(Control: TControl; Size: TImageListSize): TImageList;
  47. function ControlHasRecreationPersistenceData(Control: TControl): Boolean;
  48. function IsAppIconic: Boolean;
  49. procedure SetAppIconic(Value: Boolean);
  50. procedure SetAppMainForm(Value: TForm);
  51. procedure SetAppTerminated(Value: Boolean);
  52. procedure ForceColorChange(Control: TWinControl);
  53. function IsUncPath(Path: string): Boolean;
  54. function FileExistsFix(Path: string): Boolean;
  55. function DirectoryExistsFix(Path: string): Boolean;
  56. const
  57. FIND_FIRST_EX_LARGE_FETCH_PAS = 2; // VCLCOPY (actually should be part of Winapi)
  58. function FindFirstEx(
  59. const Path: string; Attr: Integer; var F: TSearchRec; AdditionalFlags: DWORD = 0;
  60. SearchOp: _FINDEX_SEARCH_OPS = FindExSearchNameMatch): Integer;
  61. function SupportsDarkMode: Boolean;
  62. procedure AllowDarkModeForWindow(Control: TWinControl; Allow: Boolean); overload;
  63. procedure AllowDarkModeForWindow(Handle: THandle; Allow: Boolean); overload;
  64. procedure RefreshColorMode;
  65. procedure ResetSysDarkTheme;
  66. function GetSysDarkTheme: Boolean;
  67. type
  68. TApiPathEvent = function(Path: string): string;
  69. var
  70. OnApiPath: TApiPathEvent = nil;
  71. {$EXTERNALSYM ApiPath}
  72. function ApiPath(Path: string): string;
  73. type
  74. TAppLogEvent = procedure(S: string);
  75. var
  76. OnAppLog: TAppLogEvent = nil;
  77. {$EXTERNALSYM AppLog}
  78. procedure AppLog(S: string);
  79. type
  80. TControlScrollBeforeUpdate = procedure(ObjectToValidate: TObject) of object;
  81. TControlScrollAfterUpdate = procedure of object;
  82. TCustomControlScrollOnDragOver = class
  83. private
  84. FOnBeforeUpdate: TControlScrollBeforeUpdate;
  85. FOnAfterUpdate: TControlScrollAfterUpdate;
  86. FDragOverTimer: TTimer;
  87. FControl: TControl;
  88. FDragOverTime: FILETIME;
  89. FLastVScrollTime: FILETIME;
  90. FVScrollCount: Integer;
  91. procedure DragOverTimer(Sender: TObject);
  92. procedure BeforeUpdate(ObjectToValidate: TObject);
  93. procedure AfterUpdate;
  94. public
  95. constructor Create(Control: TControl; ScheduleDragOver: Boolean);
  96. destructor Destroy; override;
  97. procedure StartDrag; virtual;
  98. procedure EndDrag; virtual;
  99. procedure DragOver(Point: TPoint); virtual; abstract;
  100. property OnBeforeUpdate: TControlScrollBeforeUpdate read FOnBeforeUpdate write FOnBeforeUpdate;
  101. property OnAfterUpdate: TControlScrollAfterUpdate read FOnAfterUpdate write FOnAfterUpdate;
  102. end;
  103. TTreeViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  104. private
  105. FLastDragNode: TTreeNode;
  106. FLastHScrollTime: FILETIME;
  107. public
  108. procedure StartDrag; override;
  109. procedure DragOver(Point: TPoint); override;
  110. end;
  111. TListViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  112. public
  113. procedure DragOver(Point: TPoint); override;
  114. end;
  115. TListBoxScrollOnDragOver = class(TCustomControlScrollOnDragOver)
  116. public
  117. procedure DragOver(Point: TPoint); override;
  118. end;
  119. implementation
  120. uses
  121. StdCtrls, MultiMon, ShellAPI, Generics.Collections, CommCtrl, ImgList, Registry;
  122. const
  123. DDExpandDelay = 15000000;
  124. DDMaxSlowCount = 3;
  125. DDVScrollDelay = 2000000;
  126. DDHScrollDelay = 2000000;
  127. DDDragStartDelay = 500000;
  128. function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
  129. begin
  130. Result := ComponentClass.Create(Owner);
  131. end;
  132. // detects vista, even in compatibility mode
  133. // (GetLocaleInfoEx is available since Vista only)
  134. function IsVistaHard: Boolean;
  135. begin
  136. Result := (GetProcAddress(GetModuleHandle(Kernel32), 'GetLocaleInfoEx') <> nil);
  137. end;
  138. function IsVista: Boolean;
  139. begin
  140. Result := CheckWin32Version(6, 0);
  141. end;
  142. function IsWin7: Boolean;
  143. begin
  144. Result := CheckWin32Version(6, 1);
  145. end;
  146. function IsWin8: Boolean;
  147. begin
  148. Result := CheckWin32Version(6, 2);
  149. end;
  150. function CutToChar(var Str: string; Ch: Char; Trim: Boolean): string;
  151. var
  152. P: Integer;
  153. begin
  154. P := Pos(Ch, Str);
  155. if P > 0 then
  156. begin
  157. Result := Copy(Str, 1, P-1);
  158. Delete(Str, 1, P);
  159. end
  160. else
  161. begin
  162. Result := Str;
  163. Str := '';
  164. end;
  165. if Trim then Result := SysUtils.Trim(Result);
  166. end;
  167. procedure FilterToFileTypes(Filter: string; FileTypes: TFileTypeItems);
  168. var
  169. Item: TFileTypeItem;
  170. begin
  171. while Filter <> '' do
  172. begin
  173. Item := FileTypes.Add();
  174. Item.DisplayName := CutToChar(Filter, '|', True);
  175. Item.FileMask := CutToChar(Filter, '|', True);
  176. end;
  177. end;
  178. type
  179. TGetDpiForMonitorFunc =
  180. function (hMonitor: HMONITOR; MonitorType: Integer; out DpiX, DpiY: Cardinal): HRESULT; stdcall;
  181. TGetSystemMetricsForDpiFunc =
  182. function (nIndex: Integer; Dpi: Cardinal): Integer; stdcall;
  183. TSystemParametersInfoForDpiFunc =
  184. function (uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT; dpi: UINT): BOOL; stdcall;
  185. const
  186. MDT_EFFECTIVE_DPI = 0;
  187. var
  188. GetDpiForMonitor: TGetDpiForMonitorFunc = nil;
  189. GetSystemMetricsForDpi: TGetSystemMetricsForDpiFunc = nil;
  190. SystemParametersInfoForDpi: TSystemParametersInfoForDpiFunc = nil;
  191. function HasSystemParametersInfoForPixelsPerInch: Boolean;
  192. begin
  193. Result := Assigned(SystemParametersInfoForDpi);
  194. end;
  195. function SystemParametersInfoForPixelsPerInch(
  196. uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT; dpi: UINT): BOOL;
  197. begin
  198. if HasSystemParametersInfoForPixelsPerInch then
  199. begin
  200. Result := SystemParametersInfoForDpi(uiAction, uiParam, pvParam, fWinIni, dpi);
  201. end
  202. else
  203. begin
  204. Result := SystemParametersInfo(uiAction, uiParam, pvParam, fWinIni);
  205. end;
  206. end;
  207. function GetMonitorPixelsPerInch(Monitor: TMonitor): Integer;
  208. var
  209. DpiX, DpiY: Cardinal;
  210. begin
  211. if Assigned(GetDpiForMonitor) and
  212. (GetDpiForMonitor(Monitor.Handle, MDT_EFFECTIVE_DPI, DpiX, DpiY) = S_OK) then
  213. begin
  214. Result := DpiX;
  215. end
  216. else
  217. begin
  218. Result := Screen.PixelsPerInch;
  219. end;
  220. end;
  221. function GetMonitorFromControl(Control: TControl): TMonitor;
  222. begin
  223. if Control.Parent <> nil then
  224. begin
  225. Result := GetMonitorFromControl(Control.Parent);
  226. end
  227. else
  228. if Control is TCustomForm then
  229. begin
  230. Result := TCustomForm(Control).Monitor;
  231. end
  232. else
  233. if (Control is TWinControl) and TWinControl(Control).HandleAllocated then
  234. begin
  235. Result := Screen.MonitorFromWindow(TWinControl(Control).Handle);
  236. end
  237. else
  238. begin
  239. Result := nil;
  240. end;
  241. end;
  242. function GetControlPixelsPerInch(Control: TControl): Integer;
  243. var
  244. Form: TCustomForm;
  245. Monitor: TMonitor;
  246. begin
  247. if Assigned(GetDpiForMonitor) then // optimization
  248. begin
  249. Form := GetParentForm(Control);
  250. if Assigned(Form) then
  251. begin
  252. // By default, scale according to what the form is so far rendered on.
  253. // If the monitor perceived DPI does not match its monitor DPI, it's because the WM_DPICHANGED is still pending.
  254. Result := TForm(Form).PixelsPerInch;
  255. end
  256. else
  257. begin
  258. Monitor := GetMonitorFromControl(Control);
  259. if Monitor = nil then
  260. begin
  261. Assert(False);
  262. Monitor := Screen.PrimaryMonitor;
  263. end;
  264. Result := GetMonitorPixelsPerInch(Monitor);
  265. end;
  266. end
  267. else
  268. begin
  269. Result := Screen.PixelsPerInch;
  270. end;
  271. end;
  272. function GetComponentPixelsPerInch(Component: TComponent): Integer;
  273. begin
  274. Result := GetControlPixelsPerInch(TControl(Component.Owner));
  275. end;
  276. function LoadDimension(Dimension: Integer; PixelsPerInch: Integer; Control: TControl): Integer;
  277. begin
  278. Result := MulDiv(Dimension, GetControlPixelsPerInch(Control), PixelsPerInch);
  279. end;
  280. function StrToDimensionDef(Str: string; PixelsPerInch: Integer; Control: TControl; Default: Integer): Integer;
  281. begin
  282. if TryStrToInt(Str, Result) then
  283. begin
  284. Result := LoadDimension(Result, PixelsPerInch, Control);
  285. end
  286. else
  287. begin
  288. Result := Default;
  289. end;
  290. end;
  291. function SaveDimension(Dimension: Integer): Integer;
  292. begin
  293. // noop
  294. Result := Dimension;
  295. end;
  296. function DimensionToDefaultPixelsPerInch(Dimension: Integer): Integer;
  297. begin
  298. Result := MulDiv(Dimension, USER_DEFAULT_SCREEN_DPI, Screen.PixelsPerInch);
  299. end;
  300. function ScaleByPixelsPerInch(Dimension: Integer; Monitor: TMonitor): Integer;
  301. begin
  302. Result := MulDiv(Dimension, GetMonitorPixelsPerInch(Monitor), USER_DEFAULT_SCREEN_DPI);
  303. end;
  304. function ScaleByPixelsPerInch(Dimension: Integer; Control: TControl): Integer;
  305. begin
  306. Result := MulDiv(Dimension, GetControlPixelsPerInch(Control), USER_DEFAULT_SCREEN_DPI);
  307. end;
  308. function ScaleByPixelsPerInchFromSystem(Dimension: Integer; Control: TControl): Integer;
  309. begin
  310. Result := MulDiv(Dimension, GetControlPixelsPerInch(Control), Screen.PixelsPerInch);
  311. end;
  312. function LoadPixelsPerInch(S: string; Control: TControl): Integer;
  313. begin
  314. // for backward compatibility with version that did not save the DPI,
  315. // make reasonable assumption that the configuration was saved with
  316. // the same DPI as we run now
  317. Result := StrToIntDef(S, GetControlPixelsPerInch(Control));
  318. end;
  319. function SavePixelsPerInch(Control: TControl): string;
  320. begin
  321. Result := IntToStr(GetControlPixelsPerInch(Control));
  322. end;
  323. function SaveDefaultPixelsPerInch: string;
  324. begin
  325. Result := IntToStr(USER_DEFAULT_SCREEN_DPI);
  326. end;
  327. // WORKAROUND
  328. // https://stackoverflow.com/q/9410485/850848
  329. type
  330. TFormHelper = class helper for TCustomForm
  331. public
  332. function RetrieveTextHeight: Integer;
  333. end;
  334. function TFormHelper.RetrieveTextHeight: Integer;
  335. begin
  336. Result := Self.GetInternalTextHeight;
  337. end;
  338. function CalculateTextHeight(Canvas: TCanvas): Integer;
  339. begin
  340. // RTL_COPY (TCustomForm.GetTextHeight)
  341. Result := Canvas.TextHeight('0');
  342. end;
  343. function ScaleByTextHeightImpl(Canvas: TCanvas; Dimension: Integer; TextHeight: Integer): Integer; overload;
  344. var
  345. NewTextHeight: Integer;
  346. begin
  347. // RTL_COPY (TCustomForm.ReadState)
  348. NewTextHeight := CalculateTextHeight(Canvas);
  349. if TextHeight <> NewTextHeight then
  350. begin
  351. Dimension := MulDiv(Dimension, NewTextHeight, TextHeight);
  352. end;
  353. Result := Dimension;
  354. end;
  355. function ScaleByTextHeightImpl(Control: TControl; Dimension: Integer; TextHeight: Integer): Integer; overload;
  356. var
  357. Form: TCustomForm;
  358. begin
  359. // RTL_COPY (TCustomForm.ReadState)
  360. Form := ValidParentForm(Control);
  361. Result := ScaleByTextHeightImpl(Form.Canvas, Dimension, TextHeight);
  362. end;
  363. const
  364. OurDesignTimeTextHeight = 13;
  365. function ScaleByTextHeight(Control: TControl; Dimension: Integer): Integer;
  366. var
  367. Form: TCustomForm;
  368. TextHeight: Integer;
  369. begin
  370. // RTL_COPY (TCustomForm.ReadState)
  371. Form := GetParentForm(Control);
  372. if Form = nil then
  373. begin
  374. // This should happen only for screen tip over dropped down menu.
  375. // The other condition is a temporary fix is for TCustomComboEdit on TCopyParamsFrame.
  376. Assert((Control.ClassName = 'TTBXPopupWindow') or (Control.ClassName = 'TTBXChevronPopupWindow') or ((Control.Parent <> nil) and (Control.Parent.ClassName = 'TCopyParamsFrame')) or ((Control.Parent <> nil) and (Control.Parent.Parent <> nil) and (Control.Parent.Parent.ClassName = 'TCopyParamsFrame')));
  377. Result := ScaleByPixelsPerInch(Dimension, Control);
  378. end
  379. else
  380. begin
  381. TextHeight := Form.RetrieveTextHeight;
  382. // runtime form (such as TTBFloatingWindowParent)
  383. if TextHeight = 0 then
  384. begin
  385. Result := ScaleByTextHeightRunTime(Control, Dimension);
  386. end
  387. else
  388. begin
  389. // that's our design text-size, we do not expect any other value
  390. Assert(TextHeight = OurDesignTimeTextHeight);
  391. Result := ScaleByTextHeightImpl(Control, Dimension, TextHeight);
  392. end;
  393. end;
  394. end;
  395. // this differs from ScaleByTextHeight only by enforcing
  396. // constant design-time text height
  397. function ScaleByTextHeightRunTime(Control: TControl; Dimension: Integer): Integer;
  398. begin
  399. Result := ScaleByTextHeightImpl(Control, Dimension, OurDesignTimeTextHeight);
  400. end;
  401. function ScaleByControlTextHeightRunTime(Canvas: TCanvas; Dimension: Integer): Integer;
  402. begin
  403. Result := ScaleByTextHeightImpl(Canvas, Dimension, OurDesignTimeTextHeight);
  404. end;
  405. function GetSystemMetricsForControl(Control: TControl; nIndex: Integer): Integer;
  406. begin
  407. if Assigned(GetSystemMetricsForDpi) then
  408. begin
  409. Result := GetSystemMetricsForDpi(nIndex, GetControlPixelsPerInch(Control))
  410. end
  411. else
  412. begin
  413. Result := GetSystemMetrics(nIndex);
  414. end;
  415. end;
  416. var
  417. ShellImageLists: TDictionary<Integer, TImageList> = nil;
  418. // This should be replaced with IShellItemImageFactory, as already used for thumbnails
  419. procedure InitializeShellImageLists;
  420. type
  421. TSHGetImageList = function (iImageList: integer; const riid: TGUID; var ppv: Pointer): hResult; stdcall;
  422. const
  423. IID_IImageList: TGUID = '{46EB5926-582E-4017-9FDF-E8998DAA0950}';
  424. var
  425. Lib: THandle;
  426. ImageList: Integer;
  427. Handle: THandle;
  428. Height, Width: Integer;
  429. ShellImageList: TImageList;
  430. SHGetImageList: TSHGetImageList;
  431. HR: HRESULT;
  432. begin
  433. Lib := LoadLibrary('shell32');
  434. SHGetImageList := GetProcAddress(Lib, 'SHGetImageList');
  435. ShellImageLists := TDictionary<Integer, TImageList>.Create;
  436. for ImageList := 0 to SHIL_LAST do
  437. begin
  438. // VCL have declaration for SHGetImageList in ShellAPI, but it does not link
  439. HR := SHGetImageList(ImageList, IID_IImageList, Pointer(Handle));
  440. if (HR = S_OK) and
  441. ImageList_GetIconSize(Handle, Width, Height) then
  442. begin
  443. // We could use AddOrSetValue instead, but to be on a safe side, we prefer e.g. SHIL_SMALL over SHIL_SYSSMALL,
  444. // while they actually can be the same
  445. if not ShellImageLists.ContainsKey(Width) then
  446. begin
  447. ShellImageList := TImageList.Create(Application);
  448. ShellImageList.Handle := Handle;
  449. ShellImageList.ShareImages := True;
  450. ShellImageList.DrawingStyle := dsTransparent;
  451. ShellImageLists.Add(Width, ShellImageList);
  452. end;
  453. end;
  454. end;
  455. end;
  456. procedure NeedShellImageLists;
  457. begin
  458. if ShellImageLists = nil then
  459. begin
  460. InitializeShellImageLists;
  461. end;
  462. end;
  463. function ShellImageListForSize(Width: Integer): TImageList;
  464. var
  465. ImageListPair: TPair<Integer, TImageList>;
  466. ImageListWidth: Integer;
  467. Diff, BestDiff: Integer;
  468. begin
  469. // Delay load image lists, not to waste resources in console/scripting mode
  470. NeedShellImageLists;
  471. Result := nil;
  472. BestDiff := -1;
  473. for ImageListPair in ShellImageLists do
  474. begin
  475. ImageListWidth := ImageListPair.Key;
  476. if ImageListWidth <= Width then
  477. begin
  478. Diff := Width - ImageListWidth;
  479. end
  480. else
  481. begin
  482. // Prefer smaller images over larger, so for 150%, we use 100% images, not 200%
  483. // (a larger icon would make the item row higher)
  484. Diff := ImageListWidth - Width + 1;
  485. end;
  486. if (BestDiff < 0) or (BestDiff > Diff) then
  487. begin
  488. BestDiff := Diff;
  489. Result := ImageListPair.Value;
  490. end;
  491. end;
  492. end;
  493. function ShellImageListForControl(Control: TControl; Size: TImageListSize): TImageList;
  494. var
  495. Width: Integer;
  496. begin
  497. case Size of
  498. ilsSmall: Width := 16;
  499. ilsLarge: Width := 32;
  500. else Width := 0; Assert(False);
  501. end;
  502. Width := ScaleByPixelsPerInch(Width, Control);
  503. Result := ShellImageListForSize(Width);
  504. end;
  505. type
  506. TListViewHelper = class helper for TCustomListView
  507. public
  508. function HasMemStream: Boolean;
  509. end;
  510. function TListViewHelper.HasMemStream: Boolean;
  511. begin
  512. with Self do
  513. Result := Assigned(FMemStream);
  514. end;
  515. type
  516. TTreeViewHelper = class helper for TCustomTreeView
  517. public
  518. function HasMemStream: Boolean;
  519. end;
  520. function TTreeViewHelper.HasMemStream: Boolean;
  521. begin
  522. with Self do
  523. Result := Assigned(FMemStream);
  524. end;
  525. type
  526. TRichEditHelper = class helper for TCustomRichEdit
  527. public
  528. function HasMemStream: Boolean;
  529. end;
  530. function TRichEditHelper.HasMemStream: Boolean;
  531. begin
  532. with Self do
  533. Result := Assigned(FMemStream);
  534. end;
  535. function ControlHasRecreationPersistenceData(Control: TControl): Boolean;
  536. begin
  537. // not implemented for this class as we do not use it as of now
  538. Assert(not (Control is TCustomComboBoxEx));
  539. Result :=
  540. ((Control is TCustomListView) and (Control as TCustomListView).HasMemStream) or
  541. ((Control is TCustomTreeView) and (Control as TCustomTreeView).HasMemStream) or
  542. ((Control is TCustomRichEdit) and (Control as TCustomRichEdit).HasMemStream);
  543. end;
  544. type
  545. TApplicationHelper = class helper for TApplication
  546. public
  547. function IsAppIconic: Boolean;
  548. procedure SetAppIconic(Value: Boolean);
  549. procedure SetMainForm(Value: TForm);
  550. procedure SetTerminated(Value: Boolean);
  551. end;
  552. function TApplicationHelper.IsAppIconic: Boolean;
  553. begin
  554. with Self do
  555. Result := FAppIconic;
  556. end;
  557. procedure TApplicationHelper.SetAppIconic(Value: Boolean);
  558. begin
  559. with Self do
  560. FAppIconic := Value;
  561. end;
  562. procedure TApplicationHelper.SetMainForm(Value: TForm);
  563. begin
  564. with Self do
  565. FMainForm := Value;
  566. end;
  567. procedure TApplicationHelper.SetTerminated(Value: Boolean);
  568. begin
  569. with Self do
  570. FTerminate := Value;
  571. end;
  572. function IsAppIconic: Boolean;
  573. begin
  574. Result := Application.IsAppIconic;
  575. end;
  576. procedure SetAppIconic(Value: Boolean);
  577. begin
  578. Application.SetAppIconic(Value);
  579. end;
  580. procedure SetAppMainForm(Value: TForm);
  581. begin
  582. Application.SetMainForm(Value);
  583. end;
  584. procedure SetAppTerminated(Value: Boolean);
  585. begin
  586. Application.SetTerminated(Value);
  587. end;
  588. function ApiPath(Path: string): string;
  589. begin
  590. Result := Path;
  591. if Assigned(OnApiPath) then
  592. begin
  593. Result := OnApiPath(Result);
  594. end;
  595. end;
  596. procedure ForceColorChange(Control: TWinControl);
  597. begin
  598. // particularly when changing color back to default (clWindow),
  599. // non-client area (border line) is not redrawn,
  600. // keeping previous color. force redraw here
  601. if Control.HandleAllocated then
  602. begin
  603. RedrawWindow(Control.Handle, nil, 0, RDW_INVALIDATE or RDW_FRAME);
  604. end;
  605. end;
  606. procedure AppLog(S: string);
  607. begin
  608. if Assigned(OnAppLog) then
  609. begin
  610. OnAppLog(S);
  611. end;
  612. end;
  613. { TCustomControlScrollOnDragOver }
  614. constructor TCustomControlScrollOnDragOver.Create(Control: TControl;
  615. ScheduleDragOver: Boolean);
  616. begin
  617. FControl := Control;
  618. FOnBeforeUpdate := nil;
  619. FOnAfterUpdate := nil;
  620. if ScheduleDragOver then
  621. begin
  622. FDragOverTimer := TTimer.Create(Control);
  623. FDragOverTimer.Enabled := False;
  624. FDragOverTimer.Interval := 50;
  625. FDragOverTimer.OnTimer := DragOverTimer;
  626. end
  627. else FDragOverTimer := nil;
  628. end;
  629. destructor TCustomControlScrollOnDragOver.Destroy;
  630. begin
  631. FreeAndNil(FDragOverTimer);
  632. end;
  633. procedure TCustomControlScrollOnDragOver.DragOverTimer(Sender: TObject);
  634. var
  635. P: TPoint;
  636. begin
  637. P := FControl.ScreenToClient(Mouse.CursorPos);
  638. if (P.X >= 0) and (P.X < FControl.Width) and
  639. (P.Y >= 0) and (P.Y < FControl.Height) then
  640. begin
  641. DragOver(P);
  642. end;
  643. end;
  644. procedure TCustomControlScrollOnDragOver.StartDrag;
  645. begin
  646. GetSystemTimeAsFileTime(FDragOverTime);
  647. GetSystemTimeAsFileTime(FLastVScrollTime);
  648. FVScrollCount := 0;
  649. if Assigned(FDragOverTimer) then
  650. FDragOverTimer.Enabled := True;
  651. end;
  652. procedure TCustomControlScrollOnDragOver.EndDrag;
  653. begin
  654. if Assigned(FDragOverTimer) then
  655. FDragOverTimer.Enabled := False;
  656. end;
  657. type
  658. TPublicControl = class(TControl);
  659. procedure TCustomControlScrollOnDragOver.BeforeUpdate(ObjectToValidate: TObject);
  660. var
  661. DragImages: TDragImageList;
  662. begin
  663. if Assigned(FOnBeforeUpdate) then
  664. FOnBeforeUpdate(ObjectToValidate);
  665. DragImages := TPublicControl(FControl).GetDragImages;
  666. if Assigned(DragImages) then
  667. DragImages.HideDragImage;
  668. end;
  669. procedure TCustomControlScrollOnDragOver.AfterUpdate;
  670. var
  671. DragImages: TDragImageList;
  672. begin
  673. if Assigned(FOnAfterUpdate) then
  674. FOnAfterUpdate;
  675. DragImages := TPublicControl(FControl).GetDragImages;
  676. if Assigned(DragImages) then
  677. DragImages.ShowDragImage;
  678. end;
  679. procedure TTreeViewScrollOnDragOver.StartDrag;
  680. var
  681. KeyBoardState : TKeyBoardState;
  682. begin
  683. inherited;
  684. FLastDragNode := nil;
  685. if (GetKeyState(VK_SPACE) <> 0) and GetKeyboardState(KeyBoardState) then
  686. begin
  687. KeyBoardState[VK_SPACE] := 0;
  688. SetKeyBoardState(KeyBoardState);
  689. end;
  690. GetSystemTimeAsFileTime(FLastHScrollTime);
  691. end;
  692. { TTreeViewScrollOnDragOver }
  693. procedure TTreeViewScrollOnDragOver.DragOver(Point: TPoint);
  694. var
  695. TreeView: TCustomTreeView;
  696. NbPixels: Integer;
  697. KnowTime: TFileTime;
  698. Node: TTreeNode;
  699. TempTopItem: TTreeNode;
  700. ScrollInfo: TScrollInfo;
  701. KeyBoardState : TKeyBoardState;
  702. begin
  703. TreeView := (FControl as TCustomTreeView);
  704. Node := TreeView.GetNodeAt(Point.X, Point.Y);
  705. if Assigned(Node) then
  706. begin
  707. GetSystemTimeAsFileTime(KnowTime);
  708. if GetKeyState(VK_SPACE) = 0 then
  709. begin
  710. {Expand node after 2.5 seconds: }
  711. if not Assigned(FLastDragNode) or (FLastDragNode <> Node) then
  712. begin
  713. {not previous droptarget: start timer}
  714. GetSystemTimeAsFileTime(FDragOverTime);
  715. FLastDragNode := Node
  716. end
  717. else
  718. begin
  719. if ((Int64(KnowTime) - Int64(FDragOverTime)) > DDExpandDelay) then
  720. begin
  721. TempTopItem := TreeView.TopItem;
  722. BeforeUpdate(nil);
  723. Node.Expand(False);
  724. TreeView.TopItem := TempTopItem;
  725. TreeView.Update;
  726. AfterUpdate;
  727. FDragOverTime := KnowTime;
  728. end;
  729. end;
  730. end
  731. else
  732. begin
  733. {restart timer}
  734. GetSystemTimeAsFileTime(FDragOverTime);
  735. if GetKeyboardState(KeyBoardState) then
  736. begin
  737. KeyBoardState[VK_Space] := 0;
  738. SetKeyBoardState(KeyBoardState);
  739. end;
  740. TempTopItem := TreeView.TopItem;
  741. BeforeUpdate(Node);
  742. if Node.Expanded then
  743. begin
  744. if not TreeView.Selected.HasAsParent(Node) then
  745. Node.Collapse(False);
  746. end
  747. else Node.Expand(False);
  748. TreeView.TopItem := TempTopItem;
  749. TreeView.Update;
  750. AfterUpdate;
  751. end;
  752. NbPixels := Abs(TTreeView(FControl).Font.Height);
  753. {Vertical treescrolling:}
  754. if ((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  755. ((FVScrollCount > 3) and
  756. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay Div 4))) then
  757. begin
  758. {Scroll tree up, if droptarget is topitem:}
  759. if Node = TreeView.TopItem then
  760. begin
  761. BeforeUpdate(nil);
  762. TreeView.Perform(WM_VSCROLL, SB_LINEUP, 0);
  763. AfterUpdate;
  764. GetSystemTimeAsFileTime(FLastVScrollTime);
  765. Inc(FVScrollCount);
  766. end
  767. else
  768. {Scroll tree down, if next visible item of droptarget is not visible:}
  769. begin
  770. if Point.Y + 3 * nbPixels > TreeView.Height then
  771. begin
  772. BeforeUpdate(nil);
  773. TreeView.Perform(WM_VSCROLL, SB_LINEDOWN, 0);
  774. AfterUpdate;
  775. GetSystemTimeAsFileTime(FLastVScrollTime);
  776. Inc(FVScrollCount);
  777. end
  778. else
  779. begin
  780. FVScrollCount := 0;
  781. end;
  782. end;
  783. end; {VScrollDelay}
  784. {Horizontal treescrolling:}
  785. {Scroll tree Left}
  786. if ((Int64(KnowTime) - Int64(FLastHScrollTime)) > DDHScrollDelay) then
  787. begin
  788. GetSystemTimeAsFileTime(FLastHScrollTime);
  789. ScrollInfo.cbSize := SizeOf(ScrollInfo);
  790. ScrollInfo.FMask := SIF_ALL;
  791. GetScrollInfo(TreeView.Handle, SB_HORZ, ScrollInfo);
  792. if ScrollInfo.nMin <> ScrollInfo.nMax then
  793. begin
  794. if Point.X < 50 then
  795. begin
  796. if Node.DisplayRect(True).Right + 50 < TreeView.Width then
  797. begin
  798. BeforeUpdate(nil);
  799. TreeView.Perform(WM_HSCROLL, SB_LINELEFT, 0);
  800. AfterUpdate;
  801. end;
  802. end
  803. else
  804. if Point.X > (TreeView.Width - 50) then
  805. begin
  806. if Node.DisplayRect(True).Left > 50 then
  807. begin
  808. BeforeUpdate(nil);
  809. TreeView.Perform(WM_HSCROLL, SB_LINERIGHT, 0);
  810. AfterUpdate;
  811. end;
  812. end;
  813. end;
  814. end;
  815. end;
  816. end;
  817. { TListViewScrollOnDragOver }
  818. procedure TListViewScrollOnDragOver.DragOver(Point: TPoint);
  819. var
  820. ListView: TCustomListView;
  821. KnowTime: TFileTime;
  822. NbPixels: Integer;
  823. WParam: LongInt;
  824. begin
  825. ListView := (FControl as TCustomListView);
  826. GetSystemTimeAsFileTime(KnowTime);
  827. NbPixels := Abs(TListView(ListView).Font.Height);
  828. {Vertical scrolling, if viewstyle = vsReport:}
  829. if (TListView(ListView).ViewStyle = vsReport) and Assigned(ListView.TopItem) and
  830. (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  831. ((FVScrollCount > DDMaxSlowCount) and
  832. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
  833. begin
  834. if (Point.Y - 3 * nbPixels <= 0) and (ListView.TopItem.Index > 0) then WParam := SB_LINEUP
  835. else
  836. if (Point.Y + 3 * nbPixels > ListView.Height) then WParam := SB_LINEDOWN
  837. else WParam := -1;
  838. if WParam >= 0 then
  839. begin
  840. BeforeUpdate(nil);
  841. ListView.Perform(WM_VSCROLL, WParam, 0);
  842. if FVScrollCount > DDMaxSlowCount then
  843. ListView.Perform(WM_VSCROLL, WParam, 0);
  844. if FVScrollCount > DDMaxSlowCount * 3 then
  845. ListView.Perform(WM_VSCROLL, WParam, 0);
  846. ListView.Update;
  847. AfterUpdate;
  848. GetSystemTimeAsFileTime(FLastVScrollTime);
  849. Inc(FVScrollCount);
  850. end
  851. else FVScrollCount := 0;
  852. end;
  853. end;
  854. { TListBoxScrollOnDragOver }
  855. procedure TListBoxScrollOnDragOver.DragOver(Point: TPoint);
  856. var
  857. ListBox: TListBox;
  858. KnowTime: TFileTime;
  859. NbPixels: Integer;
  860. WParam: LongInt;
  861. begin
  862. ListBox := (FControl as TListBox);
  863. GetSystemTimeAsFileTime(KnowTime);
  864. NbPixels := Abs(ListBox.Font.Height);
  865. {Vertical scrolling, if viewstyle = vsReport:}
  866. if (ListBox.Items.Count > 0) and
  867. (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
  868. ((FVScrollCount > DDMaxSlowCount) and
  869. ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
  870. begin
  871. if (Point.Y - 3 * nbPixels <= 0) and (ListBox.TopIndex > 0) then WParam := SB_LINEUP
  872. else
  873. if (Point.Y + 3 * nbPixels > ListBox.Height) then WParam := SB_LINEDOWN
  874. else WParam := -1;
  875. if WParam >= 0 then
  876. begin
  877. BeforeUpdate(nil);
  878. ListBox.Perform(WM_VSCROLL, WParam, 0);
  879. if FVScrollCount > DDMaxSlowCount then
  880. ListBox.Perform(WM_VSCROLL, WParam, 0);
  881. if FVScrollCount > DDMaxSlowCount * 3 then
  882. ListBox.Perform(WM_VSCROLL, WParam, 0);
  883. ListBox.Update;
  884. AfterUpdate;
  885. GetSystemTimeAsFileTime(FLastVScrollTime);
  886. Inc(FVScrollCount);
  887. end
  888. else FVScrollCount := 0;
  889. end;
  890. end;
  891. function IsUncPath(Path: string): Boolean;
  892. begin
  893. Result := (Copy(Path, 1, 2) = '\\') or (Copy(Path, 1, 2) = '//');
  894. end;
  895. const ERROR_CANT_ACCESS_FILE = 1920;
  896. function DoExists(R: Boolean; Path: string): Boolean;
  897. var
  898. Error: Integer;
  899. begin
  900. Result := R;
  901. if not Result then
  902. begin
  903. Error := GetLastError();
  904. if (Error = ERROR_CANT_ACCESS_FILE) or // returned when resolving symlinks in %LOCALAPPDATA%\Microsoft\WindowsApps
  905. (Error = ERROR_ACCESS_DENIED) then // returned for %USERPROFILE%\Application Data symlink
  906. begin
  907. Result := DirectoryExists(ApiPath(ExtractFileDir(Path)));
  908. end;
  909. end;
  910. end;
  911. function FileExistsFix(Path: string): Boolean;
  912. begin
  913. // WORKAROUND
  914. SetLastError(ERROR_SUCCESS);
  915. Result := DoExists(FileExists(ApiPath(Path)), Path);
  916. end;
  917. function DirectoryExistsFix(Path: string): Boolean;
  918. begin
  919. // WORKAROUND
  920. SetLastError(ERROR_SUCCESS);
  921. Result := DoExists(DirectoryExists(ApiPath(Path)), Path);
  922. end;
  923. // VCLCOPY
  924. function FindMatchingFileEx(var F: TSearchRec): Integer;
  925. var
  926. LocalFileTime: TFileTime;
  927. begin
  928. while F.FindData.dwFileAttributes and F.ExcludeAttr <> 0 do
  929. if not FindNextFile(F.FindHandle, F.FindData) then
  930. begin
  931. Result := GetLastError;
  932. Exit;
  933. end;
  934. FileTimeToLocalFileTime(F.FindData.ftLastWriteTime, LocalFileTime);
  935. {$WARN SYMBOL_DEPRECATED OFF}
  936. FileTimeToDosDateTime(LocalFileTime, LongRec(F.Time).Hi,
  937. LongRec(F.Time).Lo);
  938. {$WARN SYMBOL_DEPRECATED ON}
  939. F.Size := F.FindData.nFileSizeLow or Int64(F.FindData.nFileSizeHigh) shl 32;
  940. F.Attr := F.FindData.dwFileAttributes;
  941. F.Name := F.FindData.cFileName;
  942. Result := 0;
  943. end;
  944. var
  945. FindexAdvancedSupport: Boolean = False;
  946. // VCLCOPY (with FindFirstFile replaced by FindFirstFileEx)
  947. function FindFirstEx(
  948. const Path: string; Attr: Integer; var F: TSearchRec; AdditionalFlags: DWORD; SearchOp: _FINDEX_SEARCH_OPS): Integer;
  949. const
  950. faSpecial = faHidden or faSysFile or faDirectory;
  951. var
  952. FindexInfoLevel: TFindexInfoLevels;
  953. begin
  954. F.ExcludeAttr := not Attr and faSpecial;
  955. // FindExInfoBasic = do not retrieve cAlternateFileName, which we do not use
  956. if FindexAdvancedSupport then FindexInfoLevel := FindExInfoBasic
  957. else
  958. begin
  959. FindexInfoLevel := FindExInfoStandard;
  960. AdditionalFlags := AdditionalFlags and (not FIND_FIRST_EX_LARGE_FETCH_PAS);
  961. end;
  962. F.FindHandle := FindFirstFileEx(PChar(Path), FindexInfoLevel, @F.FindData, SearchOp, nil, AdditionalFlags);
  963. if F.FindHandle <> INVALID_HANDLE_VALUE then
  964. begin
  965. Result := FindMatchingFileEx(F);
  966. if Result <> 0 then FindClose(F);
  967. end
  968. else
  969. Result := GetLastError;
  970. end;
  971. type TPreferredAppMode = (pamDefault, pamAllowDark, pamForceDark, pamForceLight, pamMax);
  972. var
  973. AAllowDarkModeForWindow: function(hWnd: HWND; Allow: BOOL): BOOL; stdcall;
  974. ARefreshImmersiveColorPolicyState: procedure; stdcall;
  975. ASetPreferredAppMode: function(AppMode: TPreferredAppMode): TPreferredAppMode; stdcall;
  976. function SupportsDarkMode: Boolean;
  977. begin
  978. Result := Assigned(AAllowDarkModeForWindow) and Assigned(ARefreshImmersiveColorPolicyState);
  979. end;
  980. procedure AllowDarkModeForWindow(Control: TWinControl; Allow: Boolean);
  981. begin
  982. Assert(Control.HandleAllocated);
  983. if SupportsDarkMode and Control.HandleAllocated then
  984. begin
  985. AAllowDarkModeForWindow(Control.Handle, Allow);
  986. end;
  987. end;
  988. procedure AllowDarkModeForWindow(Handle: THandle; Allow: Boolean);
  989. begin
  990. if SupportsDarkMode then
  991. begin
  992. AAllowDarkModeForWindow(Handle, Allow);
  993. end;
  994. end;
  995. procedure RefreshColorMode;
  996. begin
  997. if SupportsDarkMode then
  998. begin
  999. ARefreshImmersiveColorPolicyState;
  1000. end;
  1001. end;
  1002. var
  1003. SysDarkTheme: Integer;
  1004. procedure ResetSysDarkTheme;
  1005. begin
  1006. SysDarkTheme := -1;
  1007. end;
  1008. function DoGetSysDarkTheme(RootKey: HKEY): Integer;
  1009. const
  1010. ThemesPersonalizeKey = 'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';
  1011. AppsUseLightThemeValue = 'AppsUseLightTheme';
  1012. var
  1013. Registry: TRegistry;
  1014. begin
  1015. Registry := TRegistry.Create;
  1016. try
  1017. Registry.RootKey := RootKey;
  1018. Result := -1;
  1019. if Registry.OpenKeyReadOnly(ThemesPersonalizeKey) and
  1020. Registry.ValueExists(AppsUseLightThemeValue) then
  1021. begin
  1022. if Registry.ReadBool(AppsUseLightThemeValue) then Result := 0
  1023. else Result := 1;
  1024. end;
  1025. finally
  1026. Registry.Free;
  1027. end;
  1028. end;
  1029. function GetSysDarkTheme: Boolean;
  1030. begin
  1031. if SysDarkTheme < 0 then
  1032. begin
  1033. SysDarkTheme := DoGetSysDarkTheme(HKEY_CURRENT_USER);
  1034. if SysDarkTheme < 0 then
  1035. begin
  1036. SysDarkTheme := DoGetSysDarkTheme(HKEY_LOCAL_MACHINE);
  1037. if SysDarkTheme < 0 then
  1038. begin
  1039. SysDarkTheme := 0;
  1040. end;
  1041. end;
  1042. end;
  1043. Result := (SysDarkTheme > 0);
  1044. end;
  1045. const
  1046. LOAD_LIBRARY_SEARCH_SYSTEM32 = $00000800;
  1047. LOAD_LIBRARY_SEARCH_USER_DIRS = $00000400;
  1048. var
  1049. Lib: THandle;
  1050. OSVersionInfo: TOSVersionInfoEx;
  1051. SetDefaultDllDirectories: function(DirectoryFlags: DWORD): BOOL; stdcall;
  1052. initialization
  1053. FindexAdvancedSupport := IsWin7;
  1054. // Translated from PuTTY's dll_hijacking_protection().
  1055. // Inno Setup does not use LOAD_LIBRARY_SEARCH_USER_DIRS and falls back to SetDllDirectory.
  1056. Lib := LoadLibrary(kernel32);
  1057. SetDefaultDllDirectories := GetProcAddress(Lib, 'SetDefaultDllDirectories');
  1058. if Assigned(SetDefaultDllDirectories) then
  1059. begin
  1060. SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 or LOAD_LIBRARY_SEARCH_USER_DIRS);
  1061. end;
  1062. Lib := LoadLibrary('shcore');
  1063. if Lib <> 0 then
  1064. begin
  1065. GetDpiForMonitor := GetProcAddress(Lib, 'GetDpiForMonitor');
  1066. end;
  1067. Lib := LoadLibrary('user32');
  1068. if Lib <> 0 then
  1069. begin
  1070. GetSystemMetricsForDpi := GetProcAddress(Lib, 'GetSystemMetricsForDpi');
  1071. SystemParametersInfoForDpi := GetProcAddress(Lib, 'SystemParametersInfoForDpi');
  1072. end;
  1073. AAllowDarkModeForWindow := nil;
  1074. ARefreshImmersiveColorPolicyState := nil;
  1075. ASetPreferredAppMode := nil;
  1076. OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  1077. if GetVersionEx(OSVersionInfo) and (OSVersionInfo.dwBuildNumber >= 17763) then
  1078. begin
  1079. Lib := GetModuleHandle('uxtheme');
  1080. if Lib <> 0 then
  1081. begin
  1082. AAllowDarkModeForWindow := GetProcAddress(Lib, MakeIntResource(133));
  1083. ARefreshImmersiveColorPolicyState := GetProcAddress(Lib, MakeIntResource(104));
  1084. if OSVersionInfo.dwBuildNumber >= 18334 then
  1085. begin
  1086. ASetPreferredAppMode := GetProcAddress(Lib, MakeIntResource(135));
  1087. end;
  1088. if SupportsDarkMode then
  1089. begin
  1090. // Both SetPreferredAppMode and RefreshImmersiveColorPolicyState is needed for
  1091. // dark list view headers and dark list view and tree view scrollbars
  1092. if Assigned(ASetPreferredAppMode) then
  1093. begin
  1094. ASetPreferredAppMode(pamAllowDark);
  1095. end;
  1096. ARefreshImmersiveColorPolicyState;
  1097. end;
  1098. end;
  1099. end;
  1100. ResetSysDarkTheme;
  1101. finalization
  1102. // No need to release individual image lists as they are owned by Application object.
  1103. FreeAndNil(ShellImageLists);
  1104. end.