#define MyAppName "AzA" #ifndef MyAppVersion #define MyAppVersion "0.1.0" #endif #ifndef MyOutputBaseFilename #define MyOutputBaseFilename "aza_desktop_setup" #endif #define MyAppPublisher "AZA MedWork" #define MyAppExeName "aza_desktop.exe" #define MyStartPanelExe "aza_start_panel.exe" #define MyUpdaterExe "aza_updater.exe" #define MyChatExe "AZA_EmpfangShell.exe" #define MyAppSourceDir SourcePath + "\..\dist\aza_desktop" [Setup] AppId={{B7E4C0D2-6B5D-4D39-9D7C-5B0D5E8C2A11} AppName={#MyAppName} AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL=https://aza-medwork.ch DefaultDirName={autopf}\AzA Medwork\AzA DefaultGroupName=AzA DisableProgramGroupPage=no OutputDir={#MyAppSourceDir}\..\..\dist\installer OutputBaseFilename={#MyOutputBaseFilename} Compression=lzma SolidCompression=yes WizardStyle=modern PrivilegesRequired=admin ArchitecturesInstallIn64BitMode=x64 UninstallDisplayIcon={app}\logo.ico SetupIconFile={#MyAppSourceDir}\..\..\logo.ico SetupMutex=AZADesktopSetupMutex CloseApplications=no RestartApplications=no [Languages] Name: "german"; MessagesFile: "compiler:Languages\German.isl" [Messages] german.FinishedRestartMessage=Ein Neustart von Windows ist noetig, damit alle AzA-Dateien ersetzt werden koennen.%n%nWichtig: AzA setzt die Installation nach dem Neustart nicht automatisch fort.%nBitte starten Sie nach dem Neustart die Installationsdatei erneut manuell. [Types] Name: "standard"; Description: "AzA Standardinstallation (empfohlen)" Name: "chatonly"; Description: "Nur AzA PraxisChat" Name: "custom"; Description: "Benutzerdefiniert"; Flags: iscustom [Components] Name: "office"; Description: "AzA Office — Hauptprogramm (Dokumentation)"; Types: standard custom; Flags: checkablealone Name: "chat"; Description: "AzA PraxisChat"; Types: standard chatonly custom; Flags: checkablealone [Tasks] Name: "desktopicon"; Description: "Desktop-Verknuepfung: AzA"; GroupDescription: "Verknuepfungen:"; Flags: checkedonce [Dirs] Name: "{app}\config"; Components: office; Permissions: users-modify [InstallDelete] Type: filesandordirs; Name: "{app}\_internal\*.pyd" Type: filesandordirs; Name: "{app}\_internal\*.dll" Type: files; Name: "{app}\_internal\*.tmp" [Files] ; --- Immer installiert: Startpanel, Updater, Assets, Version --- Source: "{#MyAppSourceDir}\{#MyStartPanelExe}"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete Source: "{#MyAppSourceDir}\{#MyUpdaterExe}"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete Source: "{#MyAppSourceDir}\assets\*"; DestDir: "{app}\assets"; Flags: ignoreversion recursesubdirs createallsubdirs skipifsourcedoesntexist Source: "{#MyAppSourceDir}\version.json"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist Source: "{#MyAppSourceDir}\..\..\logo.ico"; DestDir: "{app}"; Flags: ignoreversion Source: "{#MyAppSourceDir}\..\..\logo.png"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist ; --- AzA Office (optional) --- Source: "{#MyAppSourceDir}\{#MyAppExeName}"; DestDir: "{app}"; Components: office; Flags: ignoreversion restartreplace uninsrestartdelete Source: "{#MyAppSourceDir}\_internal\*"; DestDir: "{app}\_internal"; Components: office; Flags: ignoreversion recursesubdirs createallsubdirs restartreplace Source: "{#MyAppSourceDir}\_internal\backend_url.txt"; DestDir: "{app}"; Components: office; Flags: ignoreversion skipifsourcedoesntexist Source: "{#MyAppSourceDir}\_internal\backend_token.txt"; DestDir: "{app}"; Components: office; Flags: ignoreversion skipifsourcedoesntexist Source: "{#MyAppSourceDir}\BUILD_INFO.txt"; DestDir: "{app}"; Components: office; Flags: ignoreversion skipifsourcedoesntexist Source: "{#MyAppSourceDir}\..\..\setup_openai_runtime.ps1"; DestDir: "{app}"; Components: office; Flags: ignoreversion ; --- AzA PraxisChat (optional) --- Source: "{#MyAppSourceDir}\{#MyChatExe}"; DestDir: "{app}"; Components: chat; Flags: ignoreversion restartreplace uninsrestartdelete [Icons] Name: "{group}\AzA"; Filename: "{app}\{#MyStartPanelExe}"; IconFilename: "{app}\logo.ico" Name: "{autodesktop}\AzA"; Filename: "{app}\{#MyStartPanelExe}"; IconFilename: "{app}\logo.ico"; Tasks: desktopicon Name: "{group}\AzA Office"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\logo.ico"; Components: office Name: "{group}\OpenAI Schluessel einrichten"; Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\setup_openai_runtime.ps1"""; Components: office Name: "{group}\AzA PraxisChat"; Filename: "{app}\{#MyChatExe}"; IconFilename: "{app}\logo.ico"; Components: chat [Run] Filename: "{app}\{#MyStartPanelExe}"; Description: "AzA oeffnen"; Flags: nowait postinstall skipifsilent [UninstallRun] Filename: "netsh.exe"; Parameters: "advfirewall firewall delete rule name=""AZA Desktop - Lokale Kommunikation"""; Flags: runhidden [Code] const SW_RESTORE = 9; ASFW_ANY = $FFFFFFFF; HWND_TOPMOST = -1; HWND_NOTOPMOST = -2; SWP_NOSIZE = $0001; SWP_NOMOVE = $0002; SWP_SHOWWINDOW = $0040; function SetForegroundWindow(hWnd: HWND): BOOL; external 'SetForegroundWindow@user32.dll stdcall'; function GetForegroundWindow(): HWND; external 'GetForegroundWindow@user32.dll stdcall'; function GetWindowThreadProcessId(hWnd: HWND; var lpdwProcessId: DWORD): DWORD; external 'GetWindowThreadProcessId@user32.dll stdcall'; function AttachThreadInput(idAttach, idAttachTo: DWORD; fAttach: BOOL): BOOL; external 'AttachThreadInput@user32.dll stdcall'; function BringWindowToTop(hWnd: HWND): BOOL; external 'BringWindowToTop@user32.dll stdcall'; function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; external 'ShowWindow@user32.dll stdcall'; function SetWindowPos(hWnd, hWndInsertAfter: HWND; X, Y, cx, cy: Integer; uFlags: UINT): BOOL; external 'SetWindowPos@user32.dll stdcall'; function AllowSetForegroundWindow(dwProcessId: DWORD): BOOL; external 'AllowSetForegroundWindow@user32.dll stdcall'; function FlashWindow(hWnd: HWND; bInvert: BOOL): BOOL; external 'FlashWindow@user32.dll stdcall'; function GetCurrentProcessId(): DWORD; external 'GetCurrentProcessId@kernel32.dll stdcall'; function IsAzaProcessRunning(const ExeName: String): Boolean; var R: Integer; begin Result := False; if Exec('cmd.exe', '/C tasklist /FI "IMAGENAME eq ' + ExeName + '" /NH | find /I "' + ExeName + '"', '', SW_HIDE, ewWaitUntilTerminated, R) then Result := (R = 0); end; procedure CloseAzaProcesses(); var R, Attempt: Integer; begin for Attempt := 1 to 3 do begin Exec('taskkill.exe', '/IM aza_start_panel.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Exec('taskkill.exe', '/IM aza_desktop.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Exec('taskkill.exe', '/IM aza_updater.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Exec('taskkill.exe', '/IM AZA_EmpfangShell.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Sleep(400); Exec('taskkill.exe', '/F /IM aza_start_panel.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Exec('taskkill.exe', '/F /IM aza_desktop.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Exec('taskkill.exe', '/F /IM aza_updater.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Exec('taskkill.exe', '/F /IM AZA_EmpfangShell.exe', '', SW_HIDE, ewWaitUntilTerminated, R); Sleep(600); if not IsAzaProcessRunning('aza_start_panel.exe') and not IsAzaProcessRunning('aza_desktop.exe') and not IsAzaProcessRunning('aza_updater.exe') and not IsAzaProcessRunning('AZA_EmpfangShell.exe') then Break; end; end; function AnyAzaProcessRunning(): Boolean; begin Result := IsAzaProcessRunning('aza_start_panel.exe') or IsAzaProcessRunning('aza_desktop.exe') or IsAzaProcessRunning('aza_updater.exe') or IsAzaProcessRunning('AZA_EmpfangShell.exe'); end; procedure ForceSetupForeground(); var Wnd: HWND; ForegroundThread, TargetThread, DummyPid: DWORD; begin try AllowSetForegroundWindow(ASFW_ANY); AllowSetForegroundWindow(GetCurrentProcessId()); except end; try WizardForm.FormStyle := fsStayOnTop; WizardForm.Show; except end; Wnd := WizardForm.Handle; if Wnd = 0 then Exit; ForegroundThread := GetWindowThreadProcessId(GetForegroundWindow(), DummyPid); TargetThread := GetWindowThreadProcessId(Wnd, DummyPid); if (ForegroundThread <> 0) and (TargetThread <> 0) and (ForegroundThread <> TargetThread) then begin AttachThreadInput(ForegroundThread, TargetThread, True); SetForegroundWindow(Wnd); AttachThreadInput(ForegroundThread, TargetThread, False); end else SetForegroundWindow(Wnd); ShowWindow(Wnd, SW_RESTORE); BringWindowToTop(Wnd); { Kurz TOPMOST, danach wieder normal — erzwingt Z-Reihenfolge } SetWindowPos(Wnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW); SetWindowPos(Wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW); FlashWindow(Wnd, True); end; procedure InitializeWizard(); begin ForceSetupForeground(); end; procedure CurPageChanged(CurPageID: Integer); begin ForceSetupForeground(); end; function NextButtonClick(CurPageID: Integer): Boolean; begin Result := True; if CurPageID = wpSelectComponents then begin if not WizardIsComponentSelected('office') and not WizardIsComponentSelected('chat') then begin MsgBox('Bitte mindestens AzA Office und/oder AzA PraxisChat waehlen.', mbError, MB_OK); Result := False; Exit; end; if WizardIsComponentSelected('office') and not WizardIsComponentSelected('chat') then begin if MsgBox('Ohne AzA PraxisChat startet der Chat-Button in AzA nicht.' + #13#10 + #13#10 + 'Empfehlung: Aktivieren Sie AzA PraxisChat zusaetzlich zu AzA Office.' + #13#10 + #13#10 + 'Trotzdem fortfahren und nur AzA Office installieren?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDNO then Result := False; end; end; end; function InitializeSetup(): Boolean; begin Result := True; try AllowSetForegroundWindow(ASFW_ANY); AllowSetForegroundWindow(GetCurrentProcessId()); except end; CloseAzaProcesses(); end; function InitializeUninstall(): Boolean; begin Result := True; CloseAzaProcesses(); end; function PrepareToInstall(var NeedsRestart: Boolean): String; begin Result := ''; NeedsRestart := False; CloseAzaProcesses(); if AnyAzaProcessRunning() then begin Result := 'AzA laeuft noch im Hintergrund.' + #13#10 + #13#10 + 'Bitte schliessen Sie alle AzA-Fenster (Startpanel, Office, PraxisChat, Updater) ' + 'und starten Sie die Installation erneut.' + #13#10 + #13#10 + 'Ein Neustart von Windows ist normalerweise nicht noetig.'; end; end; procedure CurStepChanged(CurStep: TSetupStep); var R: Integer; AppPath, ExePath, Cmd: String; begin if CurStep = ssPostInstall then begin AppPath := ExpandConstant('{app}'); ExePath := AddBackslash(AppPath) + 'aza_desktop.exe'; if WizardIsComponentSelected('office') and FileExists(ExePath) then begin Exec('netsh.exe', 'advfirewall firewall delete rule name="AZA Desktop - Lokale Kommunikation"', '', SW_HIDE, ewWaitUntilTerminated, R); Sleep(200); Cmd := 'advfirewall firewall add rule name="AZA Desktop - Lokale Kommunikation" dir=in action=allow program="' + ExePath + '" localport=8000 protocol=tcp remoteip=127.0.0.1'; if not Exec('netsh.exe', Cmd, '', SW_HIDE, ewWaitUntilTerminated, R) or (R <> 0) then Log('Firewall-Regel konnte nicht angelegt werden (Code: ' + IntToStr(R) + ')'); end; end; if (CurStep = ssDone) and NeedRestart then begin MsgBox( 'Ein Neustart von Windows ist noetig, damit alle AzA-Dateien ersetzt werden koennen.' + #13#10 + #13#10 + 'Wichtig: AzA setzt die Installation nach dem Neustart nicht automatisch fort.' + #13#10 + 'Bitte starten Sie nach dem Neustart die Installationsdatei erneut manuell.', mbInformation, MB_OK); end; end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); var UserDataDir: String; begin if CurUninstallStep = usUninstall then CloseAzaProcesses(); if CurUninstallStep = usPostUninstall then begin UserDataDir := ExpandConstant('{userappdata}\AzA'); Log('AzA deinstalliert. Persoenliche Daten bleiben unter "' + UserDataDir + '" erhalten (kein automatisches Loeschen).'); if NeedRestart then begin MsgBox( 'Ein Neustart von Windows ist noetig, um die Deinstallation abzuschliessen.' + #13#10 + #13#10 + 'Wichtig: AzA setzt die Deinstallation nach dem Neustart nicht automatisch fort.' + #13#10 + 'Bitte starten Sie nach dem Neustart die Installationsdatei erneut manuell, falls Sie AzA neu installieren moechten.', mbInformation, MB_OK); end; end; end;