FreeLaunch
1213 строк · 39.2 Кб
1{
2##########################################################################
3# FreeLaunch is a free links manager for Microsoft Windows #
4# #
5# Copyright (C) 2024 Alexey Tatuyko <feedback@ta2i4.ru> #
6# Copyright (C) 2019 Mykola Petrivskiy #
7# Copyright (C) 2010 Joker-jar <joker-jar@yandex.ru> #
8# #
9# This file is part of FreeLaunch. #
10# #
11# FreeLaunch is free software: you can redistribute it and/or modify #
12# it under the terms of the GNU General Public License as published by #
13# the Free Software Foundation, either version 3 of the License, or #
14# (at your option) any later version. #
15# #
16# FreeLaunch is distributed in the hope that it will be useful, #
17# but WITHOUT ANY WARRANTY; without even the implied warranty of #
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
19# GNU General Public License for more details. #
20# #
21# You should have received a copy of the GNU General Public License #
22# along with FreeLaunch. If not, see <http://www.gnu.org/licenses/>. #
23##########################################################################
24}
25
26unit FLFunctions;
27
28interface
29
30uses
31Winapi.Windows, Winapi.Messages, System.Classes,
32Vcl.Graphics, Vcl.Imaging.PNGImage, Vcl.Themes, Vcl.Styles;
33
34
35
36type
37
38TFLThemeInfo = record
39ID: Integer;
40Name: string;
41NameForGUI: string;
42end;
43
44const
45UM_ShowMainForm = WM_USER + 1;
46UM_HideMainForm = WM_USER + 2;
47UM_LaunchDone = WM_USER + 3;
48//default themes (integrated in exe)
49FLThemes : array [0..2] of TFLThemeInfo = (
50/// first theme is always classic
51(ID: 0; Name: 'Windows'; NameForGUI: 'Classic'),
52// second theme is always for Windows 10+ dark mode
53(ID: 1; Name: 'Windows10 SlateGray'; NameForGUI: 'Slate Gray'),
54/// third theme is always for Windows 10+ light mode
55(ID: 2; Name: 'Windows10'; NameForGUI: 'Light')
56);
57
58
59type
60TAByte = array [0..maxInt-1] of byte;
61TPAByte = ^TAByte;
62TRGBArray = array[Word] of TRGBTriple;
63pRGBArray = ^TRGBArray;
64
65TLink = record
66ltype: byte;
67active: boolean;
68exec: string;
69workdir: string;
70icon: string;
71iconindex: integer;
72params: string;
73dropfiles: boolean;
74dropparams: string;
75descr: string;
76ques: boolean;
77hide: boolean;
78pr: byte;
79wst: byte;
80IsAdmin: Boolean;
81AsAdminPerm: Boolean;
82end;
83
84//--Структура информации о ярлыке
85TShellLinkInfoStruct = record
86FullPathAndNameOfLinkFile: array[0..MAX_PATH] of Char;
87FullPathAndNameOfFileToExecute: array[0..MAX_PATH] of Char;
88ParamStringsOfFileToExecute: array[0..MAX_PATH] of Char;
89FullPathAndNameOfWorkingDirectroy: array[0..MAX_PATH] of Char;
90Description: array[0..MAX_PATH] of Char;
91FullPathAndNameOfFileContiningIcon: array[0..MAX_PATH] of Char;
92IconIndex: Integer;
93HotKey: Word;
94ShowCommand: Integer;
95FindData: TWIN32FINDDATA;
96end;
97PShellLinkInfoStruct = ^TShellLinkInfoStruct;
98
99// Функция не позволяет уйти значению за пределы допустимых
100function InRange(Value, FromV, ToV: byte): byte;
101// Функции определяют количество иконок в файле
102function GetIconCount(FileName: string): integer;
103function GetNegativeCount(FileName: string): Integer;
104// Функция извлекает иконку из файла по индексу
105function GetFileIcon(FileName: string; Index: integer; Size: Integer = 32): HIcon;
106// Функция возвращает путь к специальным папкам в Windows
107function GetSpecialDir(const CSIDL: Byte): string;
108function GetAbsolutePath(s: string): string;
109// Преобразование битмапа в PNG с сохранением альфы
110procedure AlphaToPng(Src: TBitmap; Dest: TPngImage);
111// Функция делает ресайз изображения
112procedure SmoothResize(Src, Dst: TBitmap);
113// Функция извлекает описание исполняемого файла
114function GetFileDescription(FileName: string): string;
115// Функция извлекает имя файла без разширения
116function ExtractFileNameNoExt(FileName: string): string;
117// Функция извлекает информацию из ярлыка (*.lnk)
118procedure GetLinkInfo(lpShellLinkInfoStruct: PShellLinkInfoStruct);
119// Обрезает строку Str до длины Len с добавлением троеточия в конец
120function MyCutting(Str: string; Len: byte): string;
121// Простая обертка над MessageBox
122procedure WarningMessage(AHandle: HWND; AText: string);
123/// MessageBox with YES and NO buttons
124function RequestMessage(AHandle: HWND; AText: string): Integer;
125// Определение типа файла
126function IsExecutable(Ext: string): Boolean;
127// Обертка над CreateProcess
128function CreateProcessFL(AExecutable, AParameters, APath: string; AWindowState,
129APriority: Integer; var AErrorCode: Integer): Boolean;
130// Запуск процесса внутри потока
131procedure ThreadLaunch(var ALink: TLink; AMainHandle: HWND; ADroppedFile: string);
132// Процедура для запуска процесса в потоке (при клике по кнопке)
133procedure NewProcess(ALink: TLink; AMainHandle: HWND; ALaunchID: Integer;
134ADroppedFile: string);
135// launch help file
136procedure ExecHelpFile(AMainHandle: HWND; AHelpFileName: string);
137// Замена всех переменных окружения их значениями
138function ExpandEnvironmentVariables(const AFileName: string): string;
139// Добавление новой переменной окружения
140procedure AddEnvironmentVariable(const AName, AValue: string);
141// Конвертация линка в набор строк
142procedure LinkToStrings(ALink: TLink; AStrings: TStrings);
143/// <summary> Конвертация набора строк в линк </summary>
144function StringsToLink(AStrings: TStrings): TLink;
145/// <summary> Рисует иконку Щит UAC на канве </summary>
146procedure DrawShieldIcon(ACanvas: TCanvas; APosition: TPoint; ASize: TSize);
147/// <summary> Инициализация путей </summary>
148procedure InitEnvironment;
149/// <summary> Проверка режима работы программы </summary>
150function IsPortable: Boolean;
151/// <summary> Конвертация пути в путь с использованием переменных окружения </summary>
152function PathToPortable(APath: string): string;
153/// Check Windows visual theme
154function WinThemeDetect: string;
155/// Get current App visual theme
156function GetAppTheme: string;
157/// Get index of visual theme by name
158function GetAppThemeIndex(AName: string): Integer;
159/// Set App visual theme
160procedure SetAppTheme(AName: string);
161/// Set App visual theme by ID
162function FindSysUserDefLangFile: string;
163
164var
165fl_root, fl_dir, fl_WorkDir, FLVersion: string;
166SettingsMode: integer; //Режим работы (0 - инсталляция, настройки хранятся в APPDATA;
167//1 - инсталляция, настройки хранятся в папке программы;
168//2 - портабельный режим, инсталляция, настройки хранятся в папке программы)
169
170implementation
171
172uses
173System.SysUtils, System.IniFiles, System.IOUtils, System.StrUtils,
174System.Win.ComObj, System.Win.Registry, System.Math,
175Winapi.CommCtrl, Winapi.ShellApi, Winapi.ShFolder, Winapi.ActiveX,
176Winapi.ShlObj,
177FLLanguage;
178
179type
180
181PBGRAInt = ^TBGRAInt;
182
183TBGRAInt = record
184R: Integer;
185G: Integer;
186B: Integer;
187A: Integer;
188end;
189
190PBGRA = ^TBGRA;
191
192TBGRA = packed record
193B: Byte;
194G: Byte;
195R: Byte;
196A: Byte;
197end;
198
199
200PContributor = ^TContributor;
201
202TContributor = record
203Weight: Integer;
204Pixel: Integer;
205end;
206
207TContributors = array of TContributor;
208
209PContributorEntry = ^TContributorEntry;
210TContributorEntry = record
211N: Integer;
212Contributors: TContributors;
213end;
214
215TContributorList = array of TContributorEntry;
216
217TBGRAIntArray = array of TBGRAInt;
218
219procedure FillLineCacheHorz(N: Integer; Line: Pointer;
220const ACurrentLine: TBGRAIntArray);
221var
222Run: PBGRA;
223Data: PBGRAInt;
224begin
225Run := Line;
226Data := @ACurrentLine[0];
227Dec(N);
228while N >= 0 do begin
229Data.B := Run.B;
230Data.G := Run.G;
231Data.R := Run.R;
232Data.A := Run.A;
233Inc(Run);
234Inc(Data);
235Dec(N);
236end;
237end;
238
239function IntToByte(Value: Integer): Byte;
240begin
241Result := 255;
242if Value >= 0 then begin
243if Value <= 255 then Result := Value;
244end else Result := 0;
245end;
246
247function BitmapFilter(Value: Single): Single;
248const
249B = 1.0 / 3.0;
250C = 1.0 / 3.0;
251OneSixth = 1.0 / 6.0;
252var
253Temp: Single;
254begin
255if Value < 0.0 then Value := - Value;
256Temp := Sqr(Value);
257if Value < 1.0 then begin
258Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * Temp)) +
259((-18.0 + 12.0 * B + 6.0 * C) * Temp) + (6.0 - 2.0 * B));
260Result := Value * OneSixth;
261end else
262if Value < 2.0 then begin
263Value := (((-B - 6.0 * C) * (Value * Temp)) +
264((6.0 * B + 30.0 * C) * Temp) +
265((-12.0 * B - 48.0 * C) * Value) +
266(8.0 * B + 24.0 * C));
267Result := Value * OneSixth;
268end else Result := 0.0;
269end;
270
271procedure FillLineCacheVert(N, Delta: Integer; Line: Pointer;
272const ACurrentLine: TBGRAIntArray);
273var
274Run: PBGRA;
275Data: PBGRAInt;
276begin
277Run := Line;
278Data := @ACurrentLine[0];
279Dec(N);
280while N >= 0 do begin
281Data.B := Run.B;
282Data.G := Run.G;
283Data.R := Run.R;
284Data.A := Run.A;
285Inc(PByte(Run), Delta);
286Inc(Data);
287Dec(N);
288end;
289end;
290
291function ApplyContributors(Contributor: PContributorEntry;
292const ACurrentLine: TBGRAIntArray): TBGRA;
293var
294J, Total, Weight: Integer;
295RGB: TBGRAInt;
296Contr: PContributor;
297Data: PBGRAInt;
298begin
299Total := 0;
300RGB.B := Total;
301RGB.G := Total;
302RGB.R := Total;
303RGB.A := Total;
304Contr := @Contributor.Contributors[0];
305for J := 0 to Contributor.N - 1 do begin
306Weight := Contr.Weight;
307Inc(Total, Weight);
308Data := @ACurrentLine[Contr.Pixel];
309Inc(RGB.R, Data.R * Weight);
310Inc(RGB.G, Data.G * Weight);
311Inc(RGB.B, Data.B * Weight);
312Inc(RGB.A, Data.A * Weight);
313Inc(Contr);
314end;
315Result.B := IntToByte(IfThen(Total <> 0, RGB.B div Total, RGB.B shr 8));
316Result.G := IntToByte(IfThen(Total <> 0, RGB.G div Total, RGB.G shr 8));
317Result.R := IntToByte(IfThen(Total <> 0, RGB.R div Total, RGB.R shr 8));
318Result.A := IntToByte(IfThen(Total <> 0, RGB.A div Total, RGB.A shr 8));
319end;
320
321procedure DoStretch(Source, Target: TBitmap);
322var
323ScaleX, ScaleY: Single;
324I, J, K, N: Integer;
325Center: Single;
326Width: Single;
327Weight: Integer;
328Left, Right: Integer;
329Work: TBitmap;
330ContributorList: TContributorList;
331SourceLine, DestLine: PBGRA;
332DestPixel: PBGRA;
333Delta, DestDelta: Integer;
334SourceHeight, SourceWidth: Integer;
335TargetHeight, TargetWidth: Integer;
336CurrentLine: TBGRAIntArray;
337begin
338SourceHeight := Source.Height;
339SourceWidth := Source.Width;
340TargetHeight := Target.Height;
341TargetWidth := Target.Width;
342Work := TBitmap.Create;
343try
344Work.PixelFormat := pf32bit;
345Work.Height := SourceHeight;
346Work.Width := TargetWidth;
347ScaleX := IfThen(SourceWidth = 1, TargetWidth / SourceWidth,
348Pred(TargetWidth) / Pred(SourceWidth));
349ScaleY := IfThen(SourceHeight = 1, TargetHeight / SourceHeight,
350Pred(TargetHeight) / Pred(SourceHeight));
351SetLength(ContributorList, TargetWidth);
352if ScaleX < 1 then begin
353Width := 2.0 / ScaleX;
354for I := 0 to Pred(TargetWidth) do begin
355ContributorList[I].N := 0;
356Center := I / ScaleX;
357Left := System.Math.Floor(Center - Width);
358Right := System.Math.Ceil(Center + Width);
359SetLength(ContributorList[I].Contributors, Right - Left + 1);
360for J := Left to Right do begin
361Weight := Round(BitmapFilter((Center - J) * ScaleX) * ScaleX * 256);
362if Weight <> 0 then begin
363if J < 0 then N := -J
364else N := IfThen(J >= SourceWidth, 2 * SourceWidth - J - 1, J);
365K := ContributorList[I].N;
366Inc(ContributorList[I].N);
367ContributorList[I].Contributors[K].Pixel := N;
368ContributorList[I].Contributors[K].Weight := Weight;
369end;
370end;
371end;
372end else begin
373for I := 0 to Pred(TargetWidth) do begin
374ContributorList[I].N := 0;
375Center := I / ScaleX;
376Left := System.Math.Floor(Center - 2.0);
377Right := System.Math.Ceil(Center + 2.0);
378SetLength(ContributorList[I].Contributors, Right - Left + 1);
379for J := Left to Right do begin
380Weight := Round(BitmapFilter(Center - J) * 256);
381if Weight <> 0 then begin
382if J < 0 then N := -J
383else N := IfThen(J >= SourceWidth, 2 * SourceWidth - J - 1, J);
384K := ContributorList[I].N;
385Inc(ContributorList[I].N);
386ContributorList[I].Contributors[K].Pixel := N;
387ContributorList[I].Contributors[K].Weight := Weight;
388end;
389end;
390end;
391end;
392if SourceWidth > SourceHeight then SetLength(CurrentLine, SourceWidth)
393else SetLength(CurrentLine, SourceHeight);
394for K := 0 to Pred(SourceHeight) do begin
395SourceLine := Source.ScanLine[K];
396FillLineCacheHorz(SourceWidth, SourceLine, CurrentLine);
397DestPixel := Work.ScanLine[K];
398for I := 0 to Pred(TargetWidth) do begin
399DestPixel^ := ApplyContributors(@ContributorList[I], CurrentLine);
400Inc(DestPixel);
401end;
402end;
403for I := 0 to Pred(TargetWidth) do ContributorList[I].Contributors := nil;
404ContributorList := nil;
405SetLength(ContributorList, TargetHeight);
406if ScaleY < 1 then begin
407Width := 2.0 / ScaleY;
408for I := 0 to Pred(TargetHeight) do begin
409ContributorList[I].N := 0;
410Center := I / ScaleY;
411Left := System.Math.Floor(Center - Width);
412Right := System.Math.Ceil(Center + Width);
413SetLength(ContributorList[I].Contributors, Right - Left + 1);
414for J := Left to Right do
415begin
416Weight := Round(BitmapFilter((Center - J) * ScaleY) * ScaleY * 256);
417if Weight <> 0 then begin
418if J < 0 then N := -J
419else N := IfThen(J >= SourceHeight, 2 * SourceHeight - J - 1, J);
420K := ContributorList[I].N;
421Inc(ContributorList[I].N);
422ContributorList[I].Contributors[K].Pixel := N;
423ContributorList[I].Contributors[K].Weight := Weight;
424end;
425end;
426end;
427end else begin
428for I := 0 to Pred(TargetHeight) do begin
429ContributorList[I].N := 0;
430Center := I / ScaleY;
431Left := System.Math.Floor(Center - 2.0);
432Right := System.Math.Ceil(Center + 2.0);
433SetLength(ContributorList[I].Contributors, Right - Left + 1);
434for J := Left to Right do begin
435Weight := Round(BitmapFilter(Center - J) * 256);
436if Weight <> 0 then begin
437if J < 0 then N := -J
438else N := IfThen(J >= SourceHeight, 2 * SourceHeight - J - 1, J);
439K := ContributorList[I].N;
440Inc(ContributorList[I].N);
441ContributorList[I].Contributors[K].Pixel := N;
442ContributorList[I].Contributors[K].Weight := Weight;
443end;
444end;
445end;
446end;
447SourceLine := Work.ScanLine[0];
448Delta := PAnsiChar(Work.ScanLine[1]) - PAnsiChar(SourceLine);
449DestLine := Target.ScanLine[0];
450DestDelta := PAnsiChar(Target.ScanLine[1]) - PAnsiChar(DestLine);
451for K := 0 to Pred(TargetWidth) do begin
452DestPixel := Pointer(DestLine);
453FillLineCacheVert(SourceHeight, Delta, SourceLine, CurrentLine);
454for I := 0 to Pred(TargetHeight) do begin
455DestPixel^ := ApplyContributors(@ContributorList[I], CurrentLine);
456Inc(Integer(DestPixel), DestDelta);
457end;
458Inc(SourceLine);
459Inc(DestLine);
460end;
461for I := 0 to Pred(TargetHeight) do ContributorList[I].Contributors := nil;
462ContributorList := nil;
463finally
464Work.Free;
465Target.Modified := True;
466end;
467end;
468
469procedure Stretch(NewWidth, NewHeight: Cardinal; Source: TGraphic;
470Target: TBitmap);
471var
472Temp: TBitmap;
473OriginalPixelFormat: TPixelFormat;
474begin
475if Source.Empty then Exit;
476Temp := TBitmap.Create;
477try
478Temp.Assign(Source);
479Temp.PixelFormat := pf32bit;
480OriginalPixelFormat := Target.PixelFormat;
481Target.FreeImage;
482Target.PixelFormat := pf32bit;
483Target.Width := NewWidth;
484Target.Height := NewHeight;
485DoStretch(Temp, Target);
486Target.PixelFormat := OriginalPixelFormat;
487finally
488Temp.Free;
489end;
490end;
491
492//--Функция не позволяет уйти значению за пределы допустимых
493//--Входные параметры: значение, минимальное значение, максимальное значение
494function InRange(Value, FromV, ToV: byte): byte;
495begin
496Result := Value;
497if Value < FromV then Result := FromV;
498if Value > ToV then Result := ToV;
499end;
500
501//--Функция определяет количество иконок в файле
502function GetIconCount(FileName: string): Integer;
503var
504LIC, SIC: HICON;
505begin
506Result := ExtractIconEx(PChar(FileName), -1, LIC, SIC, 1);
507end;
508
509function GetNegativeCount(FileName: string): Integer;
510var
511LIC, SIC: HICON;
512icount, I: Integer;
513begin
514Result := 0;
515icount := GetIconCount(FileName);
516LIC := 0;
517SIC := 0;
518for I := - icount + 1 to 0 do begin
519LIC := 0;
520SIC := 0;
521if ExtractIconEx(PChar(FileName), I, LIC, SIC, 1) <> 0 then begin
522Result := -I + 1;
523Break;
524end;
525end;
526end;
527
528function GetShellIcon(FileName: string): HIcon;
529var
530SFI: TSHFileInfo;
531begin
532ShGetFileInfo(PChar(FileName), 0, SFI, SizeOf(TShFileInfo), SHGFI_ICON);
533Result := SFI.hIcon;
534end;
535
536//--Функция извлекает иконку из файла по индексу
537function GetFileIcon(FileName: string; Index, Size: Integer): HIcon;
538var
539LIC, SIC: HICON;
540begin
541Result := 0;
542if GetIconCount(FileName) > 0 then begin
543ExtractIconEx(PChar(FileName), Index, LIC, SIC, 1);
544Result := LIC;
545if Result = 0 then Result := SIC;
546end;
547if Result = 0 then Result := GetShellIcon(FileName);
548if Result = 0 then Result := LoadIcon(HInstance, 'RBLANKICON');
549end;
550
551//--Функция возвращает путь к специальным папкам в Windows
552//--Входной параметр: идентификатор пути
553//-- CSIDL_APPDATA - Application Data
554//-- CSIDL_BITBUCKET - Корзина
555//-- CSIDL_CONTROLS - Панель управления
556//-- CSIDL_COOKIES - Cookies
557//-- CSIDL_DESKTOP - Рабочий стол
558//-- CSIDL_DESKTOPDIRECTORY - папка Рабочего стола
559//-- CSIDL_DRIVES - Мой компьютер
560//-- CSIDL_FAVORITES - Избранное
561//-- CSIDL_FONTS - Шрифты
562function GetSpecialDir(const CSIDL: byte): string;
563var
564Buf: array[0..MAX_PATH] of Char;
565begin
566Result := '';
567if SHGetFolderPath(0, CSIDL, 0, 0, Buf) = 0 then
568Result := Buf
569else
570exit;
571if Result[length(Result)] <> '\' then Result := Result + '\';
572end;
573
574function GetAbsolutePath(s: string): string;
575begin
576result := ExpandEnvironmentVariables(s);
577end;
578
579type
580TRGBQuadArray = array[0..MaxInt div sizeof(TRGBQuad) - 1] of TRGBQuad;
581PRGBQuadArray = ^TRGBQuadArray;
582
583procedure AlphaToPng(Src: TBitmap; Dest: TPngImage);
584var
585X, Y: Integer;
586LineS: PRGBQuadArray;
587ALineD: VCL.Imaging.PNGImage.PByteArray;
588begin
589Src.PixelFormat := pf32bit; //На всякий случай
590Src.AlphaFormat := afIgnored;
591Dest.Assign(Src);
592Dest.CreateAlpha;
593
594for Y := 0 to Pred(Src.Height) do
595begin
596LineS := Src.ScanLine[Y];
597ALineD := Dest.AlphaScanline[Y];
598
599for X := 0 to Pred(Src.Width) do
600ALineD[X] := LineS[X].rgbReserved;
601end;
602
603Src.AlphaFormat := afDefined;
604Dest.Modified := True;
605end;
606
607
608//--Функция делает ресайз изображения
609procedure SmoothResize(Src, Dst: TBitmap);
610begin
611Dst.PixelFormat := pf32bit;
612Stretch(Dst.Width, Dst.Height, Src, Dst);
613Dst.AlphaFormat := afDefined;
614end;
615
616//--Функция извлекает описание исполняемого файла
617function GetFileDescription(FileName: string): string;
618var
619P: Pointer;
620Value: Pointer;
621Len: UINT;
622GetTranslationString:string;
623FValid:boolean;
624FSize: DWORD;
625FHandle: DWORD;
626FBuffer: PChar;
627begin
628FSize := 0;
629FBuffer := nil;
630try
631FValid := False;
632FSize := GetFileVersionInfoSize(PChar(FileName), FHandle);
633if FSize > 0 then
634begin
635GetMem(FBuffer, FSize);
636FValid := GetFileVersionInfo(PChar(FileName), FHandle, FSize, FBuffer);
637end;
638Result := '';
639if FValid then
640VerQueryValue(FBuffer, '\VarFileInfo\Translation', p, Len)
641else
642p := nil;
643if P <> nil then
644GetTranslationString := IntToHex(MakeLong(HiWord(Longint(P^)), LoWord(Longint(P^))), 8);
645if FValid then
646begin
647if VerQueryValue(FBuffer,
648PChar('\StringFileInfo\' + GetTranslationString + '\FileDescription'),
649Value, Len)
650then
651Result := StrPas(PChar(Value));
652end;
653finally
654if FBuffer <> nil then
655FreeMem(FBuffer, FSize);
656end;
657end;
658
659//--Функция извлекает имя файла без разширения
660function ExtractFileNameNoExt(FileName: string): string;
661var
662TempStr: string;
663begin
664TempStr := ExtractFileName(FileName);
665Result := Copy(TempStr, 1, Length(TempStr) - Length(ExtractFileExt(FileName)));
666end;
667
668//--Функция извлекает информацию из ярлыка (*.lnk)
669procedure GetLinkInfo(lpShellLinkInfoStruct: PShellLinkInfoStruct);
670var
671ShellLink: IShellLink;
672PersistFile: IPersistFile;
673AnObj: IUnknown;
674ch_temp: array [0..MAX_PATH] of Char;
675s_temp: string;
676begin
677AnObj := CreateComObject(CLSID_ShellLink);
678ShellLink := AnObj as IShellLink;
679PersistFile := AnObj as IPersistFile;
680PersistFile.Load(PChar(string(lpShellLinkInfoStruct^.FullPathAndNameOfLinkFile)), 0);
681with ShellLink do
682begin
683GetPath(lpShellLinkInfoStruct^.FullPathAndNameOfFileToExecute, SizeOf(lpShellLinkInfoStruct^.FullPathAndNameOfLinkFile), lpShellLinkInfoStruct^.FindData, SLGP_RAWPATH);
684//32-bit app specific code for 64-bit Windows below
685if not FileExists(lpShellLinkInfoStruct^.FullPathAndNameOfFileToExecute) then
686begin
687ExpandEnvironmentStrings('%ProgramW6432%', ch_temp, SizeOf(ch_temp));
688SetString(s_temp, PChar(@ch_temp[0]), High(ch_temp));
689StrPCopy(lpShellLinkInfoStruct^.FullPathAndNameOfFileToExecute, StringReplace(lpShellLinkInfoStruct^.FullPathAndNameOfFileToExecute, GetSpecialDir(CSIDL_PROGRAM_FILES), IncludeTrailingPathDelimiter(TrimRight(s_temp)), [rfReplaceAll, rfIgnoreCase]));
690end;
691//end of specific code
692GetDescription(lpShellLinkInfoStruct^.Description, SizeOf(lpShellLinkInfoStruct^.Description));
693GetArguments(lpShellLinkInfoStruct^.ParamStringsOfFileToExecute, SizeOf(lpShellLinkInfoStruct^.ParamStringsOfFileToExecute));
694GetWorkingDirectory(lpShellLinkInfoStruct^.FullPathAndNameOfWorkingDirectroy, SizeOf(lpShellLinkInfoStruct^.FullPathAndNameOfWorkingDirectroy));
695GetIconLocation(lpShellLinkInfoStruct^.FullPathAndNameOfFileContiningIcon, SizeOf(lpShellLinkInfoStruct^.FullPathAndNameOfFileContiningIcon), lpShellLinkInfoStruct^.IconIndex);
696GetHotKey(lpShellLinkInfoStruct^.HotKey);
697GetShowCmd(lpShellLinkInfoStruct^.ShowCommand);
698end;
699end;
700
701//--Обрезает строку Str до длины Len с добавлением троеточия в конец (если строка длинее Len)
702function MyCutting(Str: string; Len: byte): string;
703begin
704if Length(Str) <= Len then
705Result := Str
706else
707Result := Copy(Str, 1, Len) + '...';
708end;
709
710function RequestMessage(AHandle: HWND; AText: string): Integer;
711begin
712Result := MessageBox(AHandle, PChar(AText),
713PChar(Language.Messages.Confirmation),
714MB_YESNO or MB_ICONQUESTION or MB_DEFBUTTON2 or MB_TOPMOST);
715end;
716
717procedure WarningMessage(AHandle: HWND; AText: string);
718begin
719MessageBox(AHandle, PChar(AText), PChar(Language.Messages.Caution),
720MB_ICONWARNING or MB_OK);
721end;
722
723function IsExecutable(Ext: string): Boolean;
724begin
725Result := Ext.EndsWith('.exe', True) or Ext.EndsWith('.bat', True);
726end;
727
728procedure ShellExecuteFL(const AWnd: HWND; const AOperation, AFileName: String;
729const AParameters: String = ''; const ADirectory: String = ''; const AShowCmd: Integer = SW_SHOWNORMAL);
730var
731ExecInfo: TShellExecuteInfo;
732NeedUninitialize: Boolean;
733begin
734Assert(AFileName <> '');
735
736NeedUninitialize := SUCCEEDED(CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE));
737try
738FillChar(ExecInfo, SizeOf(ExecInfo), 0);
739ExecInfo.cbSize := SizeOf(ExecInfo);
740
741ExecInfo.Wnd := AWnd;
742ExecInfo.lpVerb := Pointer(AOperation);
743ExecInfo.lpFile := PChar(AFileName);
744ExecInfo.lpParameters := Pointer(AParameters);
745ExecInfo.lpDirectory := Pointer(ADirectory);
746ExecInfo.nShow := AShowCmd;
747ExecInfo.fMask := SEE_MASK_NOASYNC { = SEE_MASK_FLAG_DDEWAIT для старых версий Delphi }
748or SEE_MASK_FLAG_NO_UI;
749{$IFDEF UNICODE}
750// Необязательно, см. http://www.transl-gunsmoker.ru/2015/01/what-does-SEEMASKUNICODE-flag-in-ShellExecuteEx-actually-do.html
751ExecInfo.fMask := ExecInfo.fMask or SEE_MASK_UNICODE;
752{$ENDIF}
753
754{$WARN SYMBOL_PLATFORM OFF}
755Win32Check(ShellExecuteEx(@ExecInfo));
756{$WARN SYMBOL_PLATFORM ON}
757finally
758if NeedUninitialize then
759CoUninitialize;
760end;
761end;
762
763function CreateProcessFL(AExecutable, AParameters, APath: string; AWindowState,
764APriority: Integer; var AErrorCode: Integer): Boolean;
765var
766pi: TProcessInformation;
767si: TStartupInfo;
768begin
769ZeroMemory(@si, sizeof(si));
770si.cb := SizeOf(si);
771si.dwFlags := STARTF_USESHOWWINDOW;
772si.wShowWindow := AWindowState;
773ZeroMemory(@PI, SizeOf(PI));
774
775SetLastError(ERROR_INVALID_PARAMETER);
776{$WARN SYMBOL_PLATFORM OFF}
777Result := Winapi.Windows.CreateProcess(PChar(AExecutable), PChar(AParameters),
778nil, nil, false,
779APriority or CREATE_DEFAULT_ERROR_MODE or CREATE_UNICODE_ENVIRONMENT, nil,
780PChar(APath), si, pi);
781if Result then
782AErrorCode := 0
783else
784AErrorCode := GetLastError;
785{$WARN SYMBOL_PLATFORM ON}
786CloseHandle(PI.hThread);
787CloseHandle(PI.hProcess);
788end;
789
790procedure LaunchInExecutor(ALink: TLink; AMainHandle: HWND;
791ADroppedFile: string);
792var
793Executor, Parameters: string;
794LinkStrings: TStringList;
795begin
796Executor := GetAbsolutePath('%FL_DIR%\FLExecutor.exe');
797
798LinkStrings := TStringList.Create;
799try
800LinkToStrings(ALink, LinkStrings);
801LinkStrings.Delimiter := ';';
802LinkStrings.QuoteChar := '''';
803Parameters := AnsiQuotedStr(LinkStrings.DelimitedText, '"');
804finally
805LinkStrings.Free;
806end;
807
808Parameters := Parameters + ' ' + IntToStr(AMainHandle);
809Parameters := Parameters + ' ' + AnsiQuotedStr(Language.FileName, '"');
810Parameters := Parameters + ' ' + AnsiQuotedStr(ADroppedFile, '"');
811
812ShellExecuteFL(AMainHandle, '', Executor, Parameters);
813end;
814
815procedure ThreadLaunch(var ALink: TLink; AMainHandle: HWND; ADroppedFile: string);
816const
817ERROR_ELEVATION_REQUIRED = 740;
818var
819WinType, Prior, ErrorCode: integer;
820execparams, path, exec, params: string;
821
822function RunasCanBeUsed: Boolean;
823begin
824Result := Prior = NORMAL_PRIORITY_CLASS;
825end;
826
827procedure RunElevated;
828begin
829if RunasCanBeUsed then
830ShellExecuteFL(AMainHandle, 'runas', exec, execparams, path, WinType)
831else
832LaunchInExecutor(ALink, AMainHandle, ADroppedFile);
833end;
834
835begin
836exec := GetAbsolutePath(ALink.exec);
837path := GetAbsolutePath(ALink.workdir);
838if path = '' then
839path := ExtractFilePath(exec);
840if not ALink.active then
841Exit;
842if (ALink.ques) and
843(RequestMessage(AMainHandle, Format(Language.Messages.RunProgram,
844[ExtractFileName(exec)])) = IDNO)
845then Exit;
846case ALink.wst of
8470: WinType := SW_SHOW;
8481: WinType := SW_SHOWMAXIMIZED;
8492: WinType := SW_SHOWMINIMIZED;
8503: WinType := SW_HIDE;
851end;
852if ALink.ltype = 0 then
853begin
854case ALink.pr of
8550: Prior := NORMAL_PRIORITY_CLASS;
8561: Prior := HIGH_PRIORITY_CLASS;
8572: Prior := IDLE_PRIORITY_CLASS;
8583: Prior := REALTIME_PRIORITY_CLASS;
8594: Prior := BELOW_NORMAL_PRIORITY_CLASS;
8605: Prior := ABOVE_NORMAL_PRIORITY_CLASS;
861end;
862if ADroppedFile <> '' then
863params := stringreplace(ALink.dropparams, '%1', ADroppedFile, [rfReplaceAll])
864else
865params := ALink.params;
866params := GetAbsolutePath(params);
867execparams := Format('"%s" %s', [exec, params]);
868if (ALink.IsAdmin or ALink.AsAdminPerm) and (not ParamStr(0).Contains('FLExecutor.exe')) then
869RunElevated
870else
871if not CreateProcessFL(exec, execparams, path, WinType, Prior, ErrorCode)
872then begin
873if ErrorCode = ERROR_ELEVATION_REQUIRED then
874begin
875ALink.IsAdmin := True;
876RunElevated;
877end
878else
879RaiseLastOSError(ErrorCode);
880end;
881end
882else
883ShellExecuteFL(AMainHandle, '', exec, '', path, WinType);
884if ALink.hide then
885PostMessage(AMainHandle, UM_HideMainForm, 0, 0);
886end;
887
888procedure NewProcess(ALink: TLink; AMainHandle: HWND; ALaunchID: Integer;
889ADroppedFile: string);
890begin
891TThread.CreateAnonymousThread(procedure
892begin
893try
894ThreadLaunch(ALink, AMainHandle, ADroppedFile);
895except
896on E: EOSError do
897if not (e.ErrorCode = ERROR_CANCELLED) then
898WarningMessage(AMainHandle,
899StringReplace(e.Message, '%1', ExtractFileName(ALink.exec), [rfReplaceAll]));
900on E: Exception do
901WarningMessage(AMainHandle,
902StringReplace(e.Message, '%1', ExtractFileName(ALink.exec), [rfReplaceAll]));
903end;
904PostMessage(AMainHandle, UM_LaunchDone, ALink.IsAdmin.ToInteger, ALaunchID);
905end).Start;
906end;
907
908procedure ExecHelpFile(AMainHandle: HWND; AHelpFileName: string);
909begin
910TThread.CreateAnonymousThread(procedure
911begin
912try
913ShellExecuteFL(AMainHandle, '', GetAbsolutePath(AHelpFileName), '',
914GetAbsolutePath(ExtractFilePath(AHelpFileName)), SW_SHOW);
915except
916on E: Exception do
917WarningMessage(AMainHandle,
918StringReplace(e.Message, '%1', ExtractFileName(AHelpFileName), [rfReplaceAll]));
919end;
920end).Start;
921end;
922
923function ExpandEnvironmentVariables(const AFileName: string): string;
924var
925BuffSize: integer;
926Buffer: string;
927begin
928Result := AFileName;
929SetLastError(0);
930BuffSize := ExpandEnvironmentStrings(PChar(AFileName), nil, 0);
931if BuffSize = 0 then
932RaiseLastOSError
933else
934begin
935SetLength(Buffer, BuffSize);
936if ExpandEnvironmentStrings(PChar(AFileName), PChar(Buffer), BuffSize) = 0 then
937RaiseLastOSError;
938end;
939Result := Copy(Buffer, 1, BuffSize - 1);
940end;
941
942procedure AddEnvironmentVariable(const AName, AValue: string);
943begin
944SetLastError(0);
945if not SetEnvironmentVariable(PChar(AName),
946PChar(ExcludeTrailingPathDelimiter(AValue)))
947then
948RaiseLastOSError;
949end;
950
951const
952BUTTON_INI_SECTION = 'button';
953
954procedure LinkToStrings(ALink: TLink; AStrings: TStrings);
955var
956Ini: TMemIniFile;
957begin
958Ini := TMemIniFile.Create('');
959try
960Ini.WriteString(BUTTON_INI_SECTION, 'version', FLVersion);
961Ini.WriteString(BUTTON_INI_SECTION, 'object', ALink.Exec);
962Ini.WriteString(BUTTON_INI_SECTION, 'workdir', ALink.WorkDir);
963Ini.WriteString(BUTTON_INI_SECTION, 'icon', ALink.Icon);
964Ini.WriteInteger(BUTTON_INI_SECTION, 'iconindex', ALink.IconIndex);
965Ini.WriteString(BUTTON_INI_SECTION, 'parameters', ALink.Params);
966Ini.WriteBool(BUTTON_INI_SECTION, 'dropfiles', ALink.DropFiles);
967Ini.WriteString(BUTTON_INI_SECTION, 'dropparameters', ALink.DropParams);
968Ini.WriteString(BUTTON_INI_SECTION, 'describe', ALink.Descr);
969Ini.WriteBool(BUTTON_INI_SECTION, 'question', ALink.Ques);
970Ini.WriteBool(BUTTON_INI_SECTION, 'hide', ALink.Hide);
971Ini.WriteInteger(BUTTON_INI_SECTION, 'priority', ALink.Pr);
972Ini.WriteInteger(BUTTON_INI_SECTION, 'windowstate', ALink.WSt);
973Ini.WriteBool(BUTTON_INI_SECTION, 'IsAdmin', ALink.IsAdmin);
974
975Ini.GetStrings(AStrings);
976finally
977Ini.Free;
978end;
979end;
980
981function StringsToLink(AStrings: TStrings): TLink;
982var
983Ini: TMemIniFile;
984Ext: string;
985begin
986Ini := TMemIniFile.Create('');
987try
988Ini.SetStrings(AStrings);
989
990Result.Exec := Ini.ReadString(BUTTON_INI_SECTION, 'object', '');
991Result.WorkDir := Ini.ReadString(BUTTON_INI_SECTION, 'workdir', '');
992Result.Icon := Ini.ReadString(BUTTON_INI_SECTION, 'icon', '');
993Result.IconIndex := Ini.ReadInteger(BUTTON_INI_SECTION, 'iconindex', 0);
994Result.Params := Ini.ReadString(BUTTON_INI_SECTION, 'parameters', '');
995Result.DropFiles := Ini.ReadBool(BUTTON_INI_SECTION, 'dropfiles', false);
996Result.DropParams := Ini.ReadString(BUTTON_INI_SECTION, 'dropparameters', '');
997Result.Descr := Ini.ReadString(BUTTON_INI_SECTION, 'describe', '');
998Result.Ques := Ini.ReadBool(BUTTON_INI_SECTION, 'question', false);
999Result.Hide := Ini.ReadBool(BUTTON_INI_SECTION, 'hide', false);
1000Result.Pr := Ini.ReadInteger(BUTTON_INI_SECTION, 'priority', 0);
1001Result.WSt := Ini.ReadInteger(BUTTON_INI_SECTION, 'windowstate', 0);
1002Result.IsAdmin := Ini.ReadBool(BUTTON_INI_SECTION, 'IsAdmin', False);
1003
1004Result.Active := True;
1005Ext := ExtractFileExt(Result.Exec).ToLower;
1006if IsExecutable(Ext) then
1007Result.LType := 0
1008else
1009Result.LType := 1;
1010finally
1011Ini.Free;
1012end;
1013end;
1014
1015// Modified version of http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1160302&msg=17742423
1016function GetSystemIcon(AIconID: PChar; ALarge: Boolean; ASz: PSize): HICON;
1017var
1018IcoWidth: Integer;
1019IcoHeight: Integer;
1020LoadIconWithScaleDown: function(hinst: HMODULE; pszName: PWideChar; cx, cy: Integer; out Ico: HICON): HRESULT; stdcall;
1021
1022procedure SetStandartSize;
1023begin
1024if ALarge then
1025begin
1026IcoWidth := GetSystemMetrics(SM_CXICON);
1027IcoHeight := GetSystemMetrics(SM_CYICON);
1028end
1029else
1030begin
1031IcoWidth := GetSystemMetrics(SM_CXSMICON);
1032IcoHeight := GetSystemMetrics(SM_CYSMICON);
1033end;
1034end;
1035
1036begin
1037if Assigned(ASz) then
1038begin
1039IcoWidth := ASz.cx;
1040IcoHeight := ASz.cy;
1041end
1042else
1043SetStandartSize;
1044
1045LoadIconWithScaleDown := GetProcAddress(GetModuleHandle(comctl32), 'LoadIconWithScaleDown'); // Do Not Localize
1046if Assigned(LoadIconWithScaleDown) then
1047begin
1048if Failed(LoadIconWithScaleDown(0, AIconID, IcoWidth, IcoHeight, Result)) then
1049Result := 0;
1050end
1051else
1052Result := 0;
1053
1054try
1055if Result = 0 then
1056begin
1057SetStandartSize;
1058Result := LoadImage(0, AIconID, IMAGE_ICON, IcoWidth, IcoHeight, LR_DEFAULTCOLOR or LR_SHARED);
1059if Result = 0 then
1060RaiseLastOSError;
1061Result := CopyIcon(Result);
1062if Result = 0 then
1063RaiseLastOSError;
1064end;
1065except
1066if Result <> 0 then
1067DestroyIcon(Result);
1068raise;
1069end;
1070
1071if Assigned(ASz) then
1072begin
1073ASz.cx := IcoWidth;
1074ASz.cy := IcoHeight;
1075end;
1076end;
1077
1078procedure DrawShieldIcon(ACanvas: TCanvas; APosition: TPoint; ASize: TSize);
1079var
1080IconHandle: HICON;
1081begin
1082IconHandle := GetSystemIcon(IDI_SHIELD, False, @ASize);
1083DrawIconEx(ACanvas.Handle, APosition.X, APosition.Y, IconHandle, ASize.cx,
1084ASize.cy, 0, 0, DI_NORMAL);
1085end;
1086
1087procedure InitEnvironment;
1088var
1089sini: TIniFile;
1090begin
1091fl_dir := ExtractFilePath(ParamStr(0));
1092fl_root := IncludeTrailingPathDelimiter(ExtractFileDrive(fl_dir));
1093//Считываем файл первичных настроек для определения режима работы программы
1094//и места хранения настроек
1095sini := TIniFile.Create(fl_dir + 'UseProfile.ini');
1096try
1097SettingsMode := sini.ReadInteger('general', 'settingsmode', 0);
1098if SettingsMode > 2 then SettingsMode := 0;
1099if (SettingsMode = 0) then
1100begin
1101fl_WorkDir := GetSpecialDir(CSIDL_APPDATA) + 'FreeLaunch\';
1102if not DirectoryExists(fl_WorkDir) then
1103CreateDir(fl_WorkDir);
1104end
1105else
1106fl_WorkDir := fl_dir;
1107finally
1108sini.Free;
1109end;
1110{*--Заполняем переменные FL_*--*}
1111AddEnvironmentVariable('FL_DIR', FL_DIR);
1112AddEnvironmentVariable('FL_ROOT', FL_ROOT);
1113AddEnvironmentVariable('FL_CONFIG', fl_WorkDir);
1114end;
1115
1116function IsPortable: Boolean;
1117begin
1118Result := SettingsMode = 2;
1119end;
1120
1121function PathToPortable(APath: string): string;
1122var
1123FullPath: string;
1124begin
1125Result := APath;
1126if APath = '' then Exit;
1127FullPath := TPath.GetFullPath(GetAbsolutePath(APath));
1128if ContainsText(FullPath, fl_dir) then
1129Result := ReplaceText(FullPath, fl_dir, '%FL_DIR%\')
1130else
1131if ContainsText(FullPath, fl_root) then
1132Result := ReplaceText(FullPath, fl_root, '%FL_ROOT%\');
1133end;
1134
1135function WinThemeDetect: string;
1136const
1137DarkKey = 'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\';
1138DarkValue = 'AppsUseLightTheme';
1139var
1140rval: Integer;
1141reg: TRegistry;
1142begin
1143Result := FLThemes[0].Name;
1144reg := TRegistry.Create(KEY_READ);
1145try
1146reg.RootKey := HKEY_CURRENT_USER;
1147if TOSVersion.Check(10) then begin
1148if not reg.KeyExists(DarkKey) then Exit;
1149if not reg.OpenKeyReadOnly(DarkKey) then Exit;
1150if not reg.ValueExists(DarkValue) then Exit;
1151rval := reg.ReadInteger(DarkValue) + 1;
1152if not (rval in [0..2]) then rval := 0;
1153Result := FLThemes[rval].Name;
1154end;
1155finally
1156reg.CloseKey;
1157reg.Free;
1158end;
1159end;
1160
1161procedure SetAppTheme(AName: string);
1162begin
1163TStyleManager.TrySetStyle(AName, False);
1164end;
1165
1166function GetAppTheme: string;
1167begin
1168Result := TStyleManager.ActiveStyle.Name;
1169end;
1170
1171function GetAppThemeIndex(AName: string): Integer;
1172var
1173I: Integer;
1174begin
1175Result := 0;
1176for I := Low(FLThemes) to High(FLThemes) do
1177if FLThemes[I].Name = AName then begin
1178Result := I;
1179Exit;
1180end;
1181end;
1182
1183function FindSysUserDefLangFile: string;
1184var
1185CurrLCID: Word;
1186sRec: TSearchRec;
1187Dir: string;
1188lngfile: TIniFile;
1189begin
1190Result := 'english.lng'; //default language
1191// get current user language code ID. See the for LCID: https://learn.microsoft.com/ru-ru/openspecs/windows_protocols/ms-lcid/
1192CurrLCID := GetUserDefaultUILanguage;
1193Dir := ExtractFilePath(ParamStr(0)) + 'languages\';
1194if FindFirst(Dir + '*.*', faAnyFile, sRec) = 0 then repeat
1195if (sRec.Name = '.') or (sRec.Name = '..') then Continue;
1196if ExtractFileExt(sRec.Name).ToLower = '.lng' then begin
1197lngfile := TIniFile.Create(Dir + sRec.Name);
1198try
1199if lngfile.ReadInteger('information','langid', - 1) = CurrLCID
1200then begin
1201Result := sRec.Name;
1202FindClose(sRec);
1203Exit;
1204end;
1205finally
1206lngfile.Free;
1207end;
1208end;
1209until FindNext(sRec) <> 0;
1210FindClose(sRec);
1211end;
1212
1213end.
1214