Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Programmstart nach Update (https://www.delphipraxis.net/173770-programmstart-nach-update.html)

madas 15. Mär 2013 10:23

Programmstart nach Update
 
Hallo,

ich bin gerade dabei für unsere Programme eine Update-Komponente zu schreiben. Der Ablauf ist wie folgt:

- Updateliste wird von einem Webservice angefordet
- falls Updates vorhanden sind, wird ein Updater aus den Resourcen entpackt
- der Updater wird gestartet und die Updateliste wird an den Updater übergeben
- der Updater arbeitet die Liste ab und startet am Ende die Applikation neu

Problem dabei ist, dass der Updater je nach Installationort der Applikation mit Adminrechten gestartet wird, um z.B. Dateien unter C:\Programme\Applikation ersetzen zu können. Da der Updater in solch einem Fall mit Adminrechten läuft, wird aber auch am Ende die Applikation mit Adminrechten gestartet, was ja so nicht sein soll. Daher habe ich versucht die Applikation über CreateProcessAsUser zu starten. Jedoch läuft hier die Ermittlung des Usertoken per WTSQueryUserToken mit der Fehlermeldung ERROR_PRIVILEGE_NOT_HELD fehl. Nach intensiver Suche scheint es daran zu liegen, dass man WTSQueryUserToken nur aus einem Service heraus erfolgreich aufrufen kann.

Daher wäre nun meine Frage, wie ich es schaffe die Applikation mit den Rechten des aktuell angemeldeten Users vom Updater, der mit Adminrechten läuft, aus auszuführen. Das ganze ohne die Verwendung von WTSQueryUserToken.

Vielen Dank.

madas

CCRDude 15. Mär 2013 10:26

AW: Programmstart nach Update
 
  • Neuere Windows-Versionen haben einen Restart Manager.
  • Ist das nicht Aufgabe des Installers? Ist der selbstgeschrieben?
  • Ggfls. bei InnoSetup abschauen, gibt es im Source :)

madas 15. Mär 2013 10:58

AW: Programmstart nach Update
 
Zitat:

Zitat von CCRDude (Beitrag 1207531)
  • Neuere Windows-Versionen haben einen Restart Manager.
  • Ist das nicht Aufgabe des Installers? Ist der selbstgeschrieben?
  • Ggfls. bei InnoSetup abschauen, gibt es im Source :)

Ähm, es soll nicht der Rechner neugestartet werden. Sondern die Applikation, die upgedatet wird, soll nach dem Update wieder im normalen Userkontext vom Updater, der mit Adminrechten läuft, gestartet werden.

madas

CCRDude 15. Mär 2013 11:00

AW: Programmstart nach Update
 
Ja, das hatte ich schon verstanden. Wo habe ich etwas von Rechnerneustart geschrieben? :)

Morphie 15. Mär 2013 11:37

AW: Programmstart nach Update
 
Kannst du nicht ein Zwischenprogramm einführen?

Dein Programm lädt Updates usw. herunter > Programm ruft anschließend das Zwischenprogramm auf

Das Zwischenprogramm hat zwei Aufgaben:
1. es startet das Updatesetup als Administrator und wartet darauf, dass es beendet wird.
2. Im Falle eines erfolgreichen Updates wird anschließend das eigentliche Programm gestartet. Da das Zwischenprogramm keine erweiterten Rechte hat, brauchst du hier auch keine weiteren Rechte o.ä. anfordern

Danach beendet sich das Zwischenprogramm und du siehst das geupdatete Programm wieder.

CCRDude 15. Mär 2013 13:14

AW: Programmstart nach Update
 
@Morphie: das verlagert das Problem doch nur. Ob der Updater nun, weil er elevated ist, eine elevierte Instanz des Programmes startet, oder eine elevierte Instanz des Zwischenprogrammes, dass dann eine elevierte Instanz des Programmes startet, macht keinen Unterschied ;)

Zitat:

Zitat von Morphie (Beitrag 1207542)
Da das Zwischenprogramm keine erweiterten Rechte hat,

Wieso nicht? Danach sucht madas ja eben - von einem elevierten Prozess aus einen Prozess zu starten, der es nicht ist.

Morphie 15. Mär 2013 13:56

AW: Programmstart nach Update
 

Code:
Programm (normale Userrechte) > Zwischenprogramm (normale Userrechte) > Startet Setup mit erweiterten Rechten (UAC poppt auf)
                                                                      > Startet das ursprüngliche Programm (normale Userrechte)
Das Zwischenprogramm läuft also in dem gleichen Kontext wie das ursprüngliche Programm. Daher kann es das Programm nach dem Setup auch einfach ganz normal starten.
Erst das Setup bekommt erweiterte Rechte, ruft aber nach dem Setup kein Programm selbst auf.

madas 18. Mär 2013 14:33

AW: Programmstart nach Update
 
Die Quelltexte von Innosetup habe ich durch forstet, konnte jedoch nichts sinnvolles finden, was zu meiner Lösung beitragen wollte.
Nach weiterer Recherche im WWW bin ich auf folgenden Artikel gestoßen:

http://blogs.msdn.com/b/aaron_margos...vated-app.aspx

Ich habe dann versucht das Ganze mal nach Delphi zu portieren und es scheint auf den ersten Blick zu funzen. Die Funktion macht nur Sinn bzw. ist nur ausführbar, wenn das Programm in dem sie aufrufen wird mit Admin-Rechten läuft. Hier der Delphi Quelltext:

Delphi-Quellcode:
interface

  function RunsAsDesktopUser(appFileName, appParams, currDir: WideString): Boolean;

uses
  ..., JwaWindows, JwsclToken, JwsclPrivileges, ...;

implementation

function RunsAsDesktopUser(appFileName, appParams, currDir: WideString): Boolean;
const
  dwTokenRights: DWORD = TOKEN_QUERY or TOKEN_ASSIGN_PRIMARY or TOKEN_DUPLICATE or TOKEN_ADJUST_DEFAULT or TOKEN_ADJUST_SESSIONID;
var
  hProcessToken, hShellProcess, hShellProcessToken, hPrimaryToken: THandle;
  hHWND: HWND;
  dwPID: DWORD;
  tkp: TOKEN_PRIVILEGES;
  startupInfo: TSTARTUPINFOW;
  processInfo: TPROCESSINFORMATION;
  lastError: String;
  pAppParams: PWideChar;
begin
  Result := False;
  hProcessToken := THandle(nil); hShellProcess := THandle(nil); hShellProcessToken := THandle(nil); hPrimaryToken := THandle(nil);
  dwPID := 0; lastError := EmptyStr;
  ZeroMemory(@startupInfo, sizeof(startupInfo));
  startupInfo.cb := sizeof(startupInfo);
  startupInfo.lpDesktop := 'winsta0\default';
  startupInfo.dwFlags := STARTF_USESHOWWINDOW;
  startupInfo.wShowWindow := SW_SHOWDEFAULT;
  try
    if (OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, hProcessToken)) then
    begin
      try
        tkp.PrivilegeCount := 1;
        if (LookupPrivilegeValue(nil, SE_INCREASE_QUOTA_NAME, tkp.Privileges[0].Luid)) then
        begin
          tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
          AdjustTokenPrivileges(hProcessToken, false, @tkp, SizeOf(TOKEN_PRIVILEGES), nil, nil);
          if (GetLastError <> ERROR_SUCCESS) then
          begin
            lastError := 'AdjustTokenPrivileges: ' + SysErrorMessage(GetLastError);
            Result := False;
            Exit;
          end;
        end;
      finally
        CloseHandle(hProcessToken);
      end;
    end else
    begin
      lastError := 'OpenProcessToken: ' + SysErrorMessage(GetLastError);
      Result := false;
      Exit;
    end;

    hHWND := GetShellWindow;
    if (hHWND = 0) then
    begin
      lastError := 'GetShellWindow: ' + SysErrorMessage(GetLastError);
      Result := False;
      Exit;
    end;

    GetWindowThreadProcessId(hHWND, @dwPID);
    if (hHWND = 0) then
    begin
      lastError := 'GetWindowThreadProcessId: ' + SysErrorMessage(GetLastError);
      Result := False;
      Exit;
    end;

    hShellProcess := OpenProcess(PROCESS_QUERY_INFORMATION, False, dwPID);
    if (hHWND = 0) then
    begin
      lastError := 'OpenProcess: ' + SysErrorMessage(GetLastError);
      Result := False;
      Exit;
    end;

    Result := OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, hShellProcessToken);
    if (not Result) then
    begin
      lastError := 'OpenProcessToken: ' + SysErrorMessage(GetLastError);
      Exit;
    end;

    Result := DuplicateTokenEx(hShellProcessToken, dwTokenRights, nil, SecurityImpersonation, TokenPrimary, hPrimaryToken);
    if (not Result) then
    begin
      lastError := 'DuplicateTokenEx: ' + SysErrorMessage(GetLastError);
      Exit;
    end;

    if (appParams = EmptyWideStr) then
      pAppParams := nil
    else
      pAppParams := PWideChar(appFileName + ' ' + appParams);
    Result := CreateProcessWithTokenW(hPrimaryToken, 0, PWideChar(appFileName), pAppParams, 0, nil, PWideChar(currDir),   @startupInfo, @processInfo);
    if (not Result) then
    begin
      lastError := 'CreateProcessWithTokenW: ' + SysErrorMessage(GetLastError);
      Exit;
    end;

    Result := True;
  finally
    CloseHandle(hShellProcessToken);
    CloseHandle(hPrimaryToken);
    CloseHandle(hShellProcess);
    if (not Result) then
      ShowMessage(lastError);
  end;
end;
Grüße.

madas

PS: Falls es Verbesserungsvorschläge gibt nur raus damit. :)


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:34 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz