This repository has been archived on 2026-01-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Installer-Launcher/Inno Setup 코드.txt
jmaniuvc 3d46a685d4 코드
2025-05-29 15:31:13 +09:00

275 lines
7.3 KiB
Plaintext

[Code]
const
INVALID_HANDLE_VALUE = -1;
WM_COPYDATA = $004A;
type
TProcessInfoSet = record
PathSet: TStringList;
NameSet: TStringList;
end;
TCopyDataStruct = record
dwData: LongInt;
cbData: LongInt;
lpData: LongInt;
end;
WPARAM = LongWord;
LPARAM = LongInt;
LRESULT = LongInt;
// -------------------- StudioInstaller 메시지 전송 --------------------
function FindWindow(lpClassName, lpWindowName: string): HWND;
external 'FindWindowA@user32.dll stdcall';
function SendMessage(hWnd: HWND; Msg: UINT; wParam: LongWord; lParam: LongInt): LongInt;
external 'SendMessageA@user32.dll stdcall';
function GlobalAlloc(uFlags: UINT; dwBytes: LongInt): LongInt;
external 'GlobalAlloc@kernel32.dll stdcall';
function GlobalFree(hMem: LongInt): LongInt;
external 'GlobalFree@kernel32.dll stdcall';
function lstrcpy(lpDest: LongInt; lpSrc: AnsiString): LongInt;
external 'lstrcpyA@kernel32.dll stdcall';
function RtlMoveMemory(Destination: LongInt; Source: LongInt; Length: LongInt): LongInt;
external 'RtlMoveMemory@kernel32.dll stdcall';
procedure NotifyStudioInstaller();
var
hTargetWnd: HWND;
msgStr: AnsiString;
cdsPtr, msgPtr: LongInt;
msgLen: LongInt;
begin
MsgBox('타겟 창 제목 검색: "' + 'UVC Installer' + '"', mbInformation, MB_OK); // 테스트용 메시지창
hTargetWnd := FindWindow('', 'UVC Installer');
MsgBox('FindWindow 결과: ' + IntToStr(hTargetWnd), mbInformation, MB_OK); // 테스트용 메시지창
if hTargetWnd = 0 then exit;
MsgBox('0 아님', mbInformation, MB_OK); // 테스트용 메시지창
msgStr := 'UNINSTALL_COMPLETE';
msgLen := Length(msgStr) + 1;
// Allocate memory for message
msgPtr := GlobalAlloc(0, msgLen);
if msgPtr = 0 then exit;
// Copy message string
lstrcpy(msgPtr, msgStr);
// Allocate memory for COPYDATASTRUCT (12 bytes: 3x LongInt)
cdsPtr := GlobalAlloc(0, 12);
if cdsPtr = 0 then
begin
GlobalFree(msgPtr);
exit;
end;
// dwData := 0
RtlMoveMemory(cdsPtr + 0, 0, 4);
// cbData := msgLen
RtlMoveMemory(cdsPtr + 4, msgLen, 4);
// lpData := msgPtr
RtlMoveMemory(cdsPtr + 8, msgPtr, 4);
// Send WM_COPYDATA
SendMessage(hTargetWnd, WM_COPYDATA, 0, cdsPtr);
// Free memory
GlobalFree(cdsPtr);
GlobalFree(msgPtr);
end;
// -------------------- PowerShell 실행 및 결과 추출 --------------------
function ExecAndGetLines(const CommandLine: string; var Lines: TArrayOfString): Boolean;
var
TempFile: string;
Success: Boolean;
ResultCode: Integer;
begin
TempFile := ExpandConstant('{tmp}\ps_output.txt');
Success := Exec(
'powershell.exe',
'-Command "$ErrorActionPreference = ''SilentlyContinue''; Get-Process | ForEach-Object { $p = $_; $path = ''''; try { $path = $p.Path } catch {}; ''{0}|{1}'' -f $path, $p.Name } | Out-File -Encoding UTF8 ''' + TempFile + '''"',
'',
SW_HIDE,
ewWaitUntilTerminated,
ResultCode);
if Success then
Result := LoadStringsFromFile(TempFile, Lines)
else
Result := False;
end;
// -------------------- 유틸리티 --------------------
function PosEx(const SubStr, S: string; Offset: Integer): Integer;
var
i: Integer;
begin
Result := 0;
for i := Offset to Length(S) - Length(SubStr) + 1 do
begin
if Copy(S, i, Length(SubStr)) = SubStr then
begin
Result := i;
Exit;
end;
end;
end;
function SplitString(const S, Delimiter: string): TArrayOfString;
var
PosStart, PosDelim, Index: Integer;
begin
SetArrayLength(Result, 0);
PosStart := 1;
Index := 0;
repeat
PosDelim := PosEx(Delimiter, S, PosStart);
if PosDelim > 0 then
begin
SetArrayLength(Result, Index + 1);
Result[Index] := Copy(S, PosStart, PosDelim - PosStart);
PosStart := PosDelim + Length(Delimiter);
Inc(Index);
end
else
begin
SetArrayLength(Result, Index + 1);
Result[Index] := Copy(S, PosStart, Length(S));
Break;
end;
until False;
end;
// -------------------- 실행 중 프로세스 집합 구성 --------------------
function GetRunningProcessSets: TProcessInfoSet;
var
Lines: TArrayOfString;
I: Integer;
Parts: TArrayOfString;
Path, Name: string;
begin
Result.PathSet := TStringList.Create;
Result.PathSet.CaseSensitive := False;
Result.PathSet.Sorted := True;
Result.PathSet.Duplicates := dupIgnore;
Result.NameSet := TStringList.Create;
Result.NameSet.CaseSensitive := False;
Result.NameSet.Sorted := True;
Result.NameSet.Duplicates := dupIgnore;
if not ExecAndGetLines('', Lines) then
Exit;
for I := 0 to GetArrayLength(Lines) - 1 do
begin
Parts := SplitString(Lines[I], '|');
if GetArrayLength(Parts) = 2 then
begin
Path := LowerCase(Trim(Parts[0]));
Name := LowerCase(Trim(Parts[1]) + '.exe');
if Path <> '' then
Result.PathSet.Add(Path)
else
Result.NameSet.Add(Name);
end;
end;
end;
procedure FreeProcessSet(var ProcSet: TProcessInfoSet);
begin
ProcSet.PathSet.Free;
ProcSet.NameSet.Free;
end;
// -------------------- 실행 중 여부 판단 --------------------
function IsFileRunningOptimized(const TargetFile: string; const ProcSet: TProcessInfoSet): Boolean;
var
LowerPath: string;
FileName: string;
begin
LowerPath := LowerCase(TargetFile);
FileName := LowerCase(ExtractFileName(TargetFile));
Result := (ProcSet.PathSet.IndexOf(LowerPath) <> -1) or
(ProcSet.NameSet.IndexOf(FileName) <> -1);
end;
function IsExcluded(const FileName: string): Boolean;
begin
Result := (LowerCase(FileName) = 'unins000.exe');
end;
// -------------------- 디렉터리 재귀 탐색 --------------------
function HasRunningFilesInDir(const DirPath: string; const ProcSet: TProcessInfoSet): Boolean;
var
FindRec: TFindRec;
FullPath, Extension: string;
begin
Result := False;
if FindFirst(DirPath + '\*', FindRec) then
begin
repeat
if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
begin
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
begin
if HasRunningFilesInDir(DirPath + '\' + FindRec.Name, ProcSet) then
begin
Result := True;
Break;
end;
end;
end
else
begin
FullPath := DirPath + '\' + FindRec.Name;
Extension := LowerCase(ExtractFileExt(FindRec.Name));
if (Extension = '.exe') and not IsExcluded(FindRec.Name) then
begin
if IsFileRunningOptimized(FullPath, ProcSet) then
begin
Result := True;
Break;
end;
end;
end;
until not FindNext(FindRec);
FindClose(FindRec);
end;
end;
// -------------------- Entry Point --------------------
function InitializeUninstall(): Boolean;
var
ProcSet: TProcessInfoSet;
begin
ProcSet := GetRunningProcessSets;
if HasRunningFilesInDir(ExpandConstant('{app}'), ProcSet) then
begin
MsgBox('프로그램 실행 중인 파일이 감지되었습니다. 모든 관련 프로그램을 종료한 후 다시 시도하세요.', mbError, MB_OK);
Result := False;
end
else
Result := True;
FreeProcessSet(ProcSet);
end;
// -------------------- 언인스톨 종료 시 StudioInstaller에 알림 --------------------
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall then
begin
NotifyStudioInstaller();
end;
end;