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/)
-   -   Delphi Umgebungsvariable eines anderen, laufenden Programms ändern (https://www.delphipraxis.net/127888-umgebungsvariable-eines-anderen-laufenden-programms-aendern.html)

Tiemo 20. Jan 2009 19:58


Umgebungsvariable eines anderen, laufenden Programms ändern
 
Hallo Gemeinde,

ich suche nach einer Lösung, eine Umgebungsvariable in einem schon laufenden Programm zu verändern. Ich meine mich daran erinnern zu können, dass ich irgendwo dazu schon mal Code gefunden hatte, habe aber keine Ahnung mehr, wo das war.

Hintergrund:
Ich starte aus meinem Programm zwei weitere Anwendungen, die asynchron miteinander kommuzieren. Mein Programm und Programm A werden dann irgendwann wieder beendet, Programm B läuft aber weiter. Wenn mein Programm nun wieder ausgeführt wird, möchte ich nicht Programm B erst abschießen und dann mit einer neuen Umgebungsvariable neu starten sondern einfach die Instanze beigehalten und den Wert der Variable XY in der Sitzung ändern.

Es kann davon ausgegangen werden, dass sowohl mein Programm als auch das Programm B unter dem gleichen Benutzer mit dem gleichen Token laufen.

Hat hierzu jemand eine Idee?

Tausend Dank
Tiemo

Christian Seehase 20. Jan 2009 20:44

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Moin Tiemo,

bei der Konstellation wäre es vielleicht einfacher der laufenden Instanz von Programm B einfach auf anderem Wege, z.B., per WM_COPYDATA den gewünschten neuen Wert zu übergeben. Das Programm kann es dann, wenn's denn unbedingt eine Umgebungsvariable sein muss, auch in sein Environment eintragen. Das dürfte dann auch unter Vista funktionieren.
Die Variante direkt das Environment eines fremden Prozesses zu manipulieren habe ich hier in der DP auch schon mal beschrieben, aber wenn alle beteiligten Programme von Dir stammen, lohnt sich der Aufwand hier wohl nicht. Für Vista habe ich da auch noch keine Lösung ;-)

DeddyH 20. Jan 2009 20:46

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Bei so etwas weht auch immer ein leichter Hauch von Virus durch die Luft (zumindest für den Scanner) ;)

Christian Seehase 20. Jan 2009 20:58

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Zitat:

Zitat von DeddyH
Bei so etwas weht auch immer ein leichter Hauch von Virus durch die Luft (zumindest für den Scanner) ;)

Stimmt, aber ist machbar :mrgreen:
Ausserdem gibt es für solche Vorgehensweisen auch legitime Anwendungen. ;-)

Tiemo 20. Jan 2009 21:14

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Ich habe vergessen zu erwähnen, dass Programm A und B nicht von mir sind, so dass ich darauf leider keinen Einfluss nehmen kann.

Tiemo 20. Jan 2009 21:21

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Und noch etwas: Ich möchte natürlich keinen Virus schreiben. Meine Applikation ist ein Konfigurationsprogramm für eine 3D CAD Anwendung. Dieses Konfigurationsprogramm setzt eine Reihe von Voreinstellungen und startet dann das CAD Programm. Abhängig von den Einstellungen zieht das CAD unterschiedliche Lizenzen. Es ist auch möglich, verschiedene Versionen des CAD Programms zu starten.

Programm B wiederum ist eine Helfer-Anwendung für ein dazugehöriges PDM System. B läuft asynchron zum CAD Programm, so dass beim Beenden des CAD Programmes die Daten trotzdem noch auf den Server hochgeschoben werden können. B braucht aber den Pfad zum CAD, der sich wiederum durch mein Programm beim Start auswählen lässt. Wenn B jetzt aber nicht gestartet wird, kann ich den Pfad nicht ändern. Abschießen und Neustarten ist auch Mist, da evtl. noch Daten zum Server geschoben werden.

Das nur zur Beruhigung und Erklärung...

Christian Seehase 20. Jan 2009 21:58

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Moin Tiemo,

das Du einen Virus schreiben willst, haben Detlef und ich auch nicht angenommen, aber die Techniken die erforderlich sind (Manipulation eines fremden Adressraumes) kann von einem Virenscanner durchaus als verdächtig angesehen werden.
Um Umgebungsvariablen eines anderen Prozesses zu manipulieren, müsstest Du den Environmentblock dort überschreiben.
Die einfachste Variante wäre es das eigene Environment zu ändern, und dann diesen Speicherbereich in den korrespondierenden Speicherbereich des Zielprozesses zu kopieren (Wichtig: Ab Windows 2000 muss man hierzu den Block mit Widestrings verwenden)
Eine andere Möglichkeit wäre es, den Code der die Umgebungsvariablen setzt in den Zielprozess zu injizieren und dort zu starten, um gezielt die Umgebungsvariablen zu ändern (das habe ich allerdings noch nicht umgesetzt).

Was ich mich allerdings die ganze Zeit über frage:
Wann zieht das Programm eigentlich die Konfiguration?
Erwarten würde ich, dass dieses bei Programmstart, was die Änderung der Konfiguration bei laufendem Programm natürlich unmöglich machen würde ;-)

Tiemo 21. Jan 2009 16:00

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Hallo Christian,

vielen Dank für die Hilfestellung. Das mit dem EnvironmentBlock hatte ich schon mal probiert, bin aber gescheitert. Hast Du dafür vielleicht ein Beispiel? Wie übergebe ich den Env Block an das laufende Programm?

Zum letzten Punkt, ob das Programm die Env Variable beim Start ausliest und dann als lokale Variable speichert oder ob die Umgebungsvariable a la GetEnvironmentString immer dann ausgelesen wird, wenn sie benötigt wird, weiß ich leider nicht. Das Risiko besteht natürlich, dass das Programm die Variable lokal speichert. Dann wäre ich mit dem Ansatz am Ende.

Versuch macht Kluch. Gibt es vielleich ein Programm wie ProcessExplorer, womit ich die Umgebung eines laufenden Prozesses für diesen Test manipulieren kann, bevor ich es wirklich in meinen Code implementieren?

Besten Dank
Tiemo

Christian Seehase 21. Jan 2009 20:45

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Moin Tiemo,

Zitat:

Zitat von Tiemo
Zum letzten Punkt, ob das Programm die Env Variable beim Start ausliest und dann als lokale Variable speichert oder ob die Umgebungsvariable a la GetEnvironmentString immer dann ausgelesen wird, wenn sie benötigt wird, weiß ich leider nicht. Das Risiko besteht natürlich, dass das Programm die Variable lokal speichert. Dann wäre ich mit dem Ansatz am Ende.

Ich sehe da noch ein anderes Problem.
Es könnte auch sein, dass verschiedene Teile des Programmes sich den Wert unterschiedlich beschaffen.
Der eine Teil liest immer den aktuellen Inhalt der Umgebungsvariablen (da könnte man etwas machen) ein andere Teil greift, z.B., auf einen beim Programmstart zwischengespeicherten Wert zu.
Dann kann dann zu unvorhersehbaren Verhaltensweisen des Programmes kommen, da die Konfiguration inkonsistent ist.

Ein kommentiertes Beispiel für die Manipulation des Environmentes kann ich Dir noch liefern.
Ich muss es nur erst noch zusammenstellen ;-)

Tiemo 28. Jan 2009 12:04

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Zitat:

Zitat von Christian Seehase
Ein kommentiertes Beispiel für die Manipulation des Environmentes kann ich Dir noch liefern.
Ich muss es nur erst noch zusammenstellen ;-)

Christian, das wäre echt super.

Christian Seehase 29. Jan 2009 23:59

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Moin Tiemo,

soweit hab' ich es fertig, allerdings ungetestet.
Bislang habe ich das nur gebraucht, um das Environment des Parent-Prozesses zu manipulieren.
Es könnte also das Problem geben, dass Dein Prozess nicht die Rechte hat, ein fremdes Environment zu ändern.
Falls Du einen Virenscanner hast, wird der wahrscheinlich Alarm schlagen, wenn Du das Programm erzeugst ;-)

Delphi-Quellcode:
uses
  TlHelp32,PsAPI;

{$R *.dfm}

const
  // 488 bis 499 werden nicht von der API belegt, können hier also benutzt werden.
  _ERR_DIFFERENT_SIZE = 488;
  _ERR_SIZE_CHANGED  = 489;

function csGetProcIDFromPath(const AsPath : string;var AdwLastError : DWORD) : Integer;
// Die Prozess-ID aus dem Pfad ermitteln

var
  hSnapShot : DWORD;
  pe32      : PROCESSENTRY32;
  hProcess : DWORD;
  pFilepath : PChar;
  sPath    : string;

begin
  Result := 0;
  hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  if hSnapshot = INVALID_HANDLE_VALUE then begin
    AdwLastError := GetLastError;
    Exit;
  end;
  try
    sPath      := AnsiLowerCase(Trim(sPath));
    pe32.dwSize := SizeOf(pe32);
    if not Process32First(hSnapshot,pe32) then begin
      AdwLastError := GetLastError;
      Exit;
    end;
    repeat
      hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,pe32.th32ProcessID);
      try
        if hProcess <> 0 then begin
          pFilepath := AllocMem(MAX_PATH+1);
          try
            if GetModuleFileNameEx(hProcess,pe32.th32ModuleID,pFilePath,MAX_PATH+1) <> 0 then begin
              if AnsiLowerCase(Trim(pFilepath)) = sPath then begin
                Result := pe32.th32ProcessID;
                Exit;
              end;
            end;
          finally
            FreeMem(pFilepath,MAX_PATH+1);
          end;
        end;
      finally
        CloseHandle(hProcess);
      end;
    until not Process32Next(hSnapshot,pe32);
  finally
    CloseHandle(hSnapshot);
  end;
end;

function csReadProcessEnvironment(const AdwProcID : DWORD;var AdwEnvSize : DWORD;var ApEnvContent : Pointer;var AdwLastError : DWORD) : Boolean;
// Den Speicher mit den WideString Umgebungsvariablen in Abhängigkeit
// für eine Prozess-ID auslesen.

var
  pEnvironment : Pointer;
  mbi         : MEMORY_BASIC_INFORMATION;
  hProcess    : DWORD;
  dwDummy     : DWORD;

begin
  Result      := False;
  // Die Adresse ermitteln, an der das Environment liegt
  // Diese Adresse ist bei allen Prozessen gleich (bis incl. XP), weshalb man sich
  // hier nicht um den Prozess kümmern muss.
  pEnvironment := GetEnvironmentStringsW;
  hProcess    := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,AdwProcID);
  if hProcess = 0 then begin
    AdwLastError := GetLastError;
    Exit;
  end;
  try
    // Eckdaten des Speicherbereiches holen, in dem das Environment liegt
    // prozessabhängig auslesen...
    if VirtualQueryEx(hProcess,pEnvironment,mbi,SizeOf(mbi)) <> SizeOf(mbi) then begin
      AdwLastError := GetLastError;
      Exit;
    end;
    // und den Speicherinhalt lesen
    ApEnvContent := AllocMem(mbi.RegionSize);
    AdwEnvSize  := mbi.RegionSize;
    if not ReadProcessMemory(hProcess,mbi.BaseAddress,ApEnvContent,AdwEnvSize,dwDummy) then begin
      AdwLastError := GetLastError;
      FreeMem(ApEnvContent,AdwEnvSize);
      Exit;
    end;
    Result := True;
  finally
    CloseHandle(hProcess);
  end;
end;

function csWriteProcessEnvironment(const AdwProcID : DWORD;const ApEnvContent : Pointer;var AdwLastError : DWORD) : Boolean;

var
  hProc       : DWORD;
  mbi         : MEMORY_BASIC_INFORMATION;
  dwOldProtect : DWORD;
  dwDummy     : DWORD;
  pEnvironment : Pointer;

begin
  Result := False;
  hProc := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_WRITE or PROCESS_VM_OPERATION,False,AdwProcID);
  if hProc = 0 then begin
    AdwLastError := GetLastError;
    Exit;
  end;
  try
    pEnvironment := GetEnvironmentStringsW;
    if VirtualQueryEx(hProc,pEnvironment,mbi,SizeOf(mbi)) <> SizeOf(mbi) then begin
      AdwLastError := GetLastError;
      Exit;
    end;
    // Den Zielspeicher zum Schreiben vorbereiten
    if not VirtualProtectEx(hProc,pEnvironment,mbi.RegionSize,PAGE_READWRITE,dwOldProtect) then begin
      AdwLastError := GetLastError;
      Exit;
    end;
    try
      // Das alte Environment mit dem neuen überschreiben
      if not WriteProcessMemory(hProc,pEnvironment,ApEnvContent,mbi.RegionSize,dwDummy) then begin
        AdwLastError := GetLastError;
      end;
    finally
      // Den Einstellungen für den Speicherschutz wieder herstellen
      if not VirtualProtectEx(hProc,pEnvironment,mbi.RegionSize,dwOldProtect,dwOldProtect) then begin
        AdwLastError := GetLastError;
      end else begin
        Result := True;
      end;
    end;
  finally
    CloseHandle(hProc);
  end;
end;

function csChangeProcessEnvironment(const AsPath : string;const AsEnvVariable : string;const AsValue : string;var AdwLastError : DWORD) : Boolean;
// AsPath:       Der Pfad des Programmes, dessen Enviroment geändert werden soll
// AsEnvVariable: Name der Umgebungsvariablen
// AsValue:      Der Wert, den die Variable bekommen soll
// AdwLastError: Falls Result = false der Fehlercode

var
  dwProcessIDSelf    : DWORD;
  dwProcessID        : DWORD;
  pEnvironmentOldSelf : Pointer;
  dwEnvSizeOldSelf   : DWORD;
  pEnvironmentNewSelf : Pointer;
  dwEnvSizeNewSelf   : DWORD;
  pEnvironment       : Pointer;
  dwEnvSize          : DWORD;

begin
  Result         := False;
  dwProcessIDSelf := GetCurrentProcessId;
  dwProcessID    := csGetProcIDFromPath(AsPath,AdwLastError);
  if dwProcessID = 0 then Exit;
  pEnvironmentOldSelf := nil;
  pEnvironmentNewSelf := nil;
  pEnvironment       := nil;
  try
    // Das aktuelle eigene Environment auslesen
    if not csReadProcessEnvironment(dwProcessIDSelf,dwEnvSizeOldSelf,pEnvironmentOldSelf,AdwLastError) then Exit;
    // Das aktuelle Zielenvironment auslesen
    if not csReadProcessEnvironment(dwProcessID,dwEnvSize,pEnvironment,AdwLastError) then Exit;
    // Stimmen die Grössen nicht überein, können wir nicht weitermachen
    // da die Adressen nicht übereinstimmen werden
    if dwEnvSizeOldSelf <> dwEnvSize then begin
      AdwLastError := _ERR_DIFFERENT_SIZE;
      exit;
    end;
    // Die gewünschte Variable im eigenen Environment ändern
    if not SetEnvironmentVariable(PChar(AsEnvVariable),PChar(AsValue)) then begin
      AdwLastError := GetLastError;
      Exit;
    end;
    // Jetzt das geänderte eigene Enviroment auslesen
    if not csReadProcessEnvironment(dwProcessIDSelf,dwEnvSizeNewSelf,pEnvironmentNewSelf,AdwLastError) then Exit;
    // Wenn sich die Grösse geändert hat, können wir nicht weitermachen, da sich die Adressen
    // geändert haben
    if dwEnvSizeNewSelf <> dwEnvSize then begin
      AdwLastError := _ERR_SIZE_CHANGED;
      Exit;
    end;
    // Zielenviroment mit dem eigenen geänderten überschreiben
    if not csWriteProcessEnvironment(dwProcessID,pEnvironmentNewSelf,AdwLastError) then Exit;
    Result := true;
  finally
    // Aufräumen
    if Assigned(pEnvironmentOldSelf) then FreeMem(pEnvironmentOldSelf,dwEnvSizeOldSelf);
    if Assigned(pEnvironmentNewSelf) then FreeMem(pEnvironmentNewSelf,dwEnvSizeNewSelf);
    if Assigned(pEnvironment)       then FreeMem(pEnvironment,dwEnvSize);
  end;
end;

procedure TForm1.btn1Click(Sender: TObject);

var
  dwLastError : DWORD;
 
begin
  if csChangeProcessEnvironment('<HIER DEN PFAD ZUR EXE ÜBERGEBEN','NAME DER ENVIRONMENTVARIABLENN','ZU SETZENDER WERT',dwLastError) then begin
    ShowMessage('Erledigt.');
  end else begin
    case dwLastError of
      _ERR_DIFFERENT_SIZE : ShowMessage('Die Environments stimmen in der Grösse nicht überein.');
      _ERR_SIZE_CHANGED  : ShowMessage('Die Grösse des Environments hat sich geändert.');
      else ShowMessage(IntToStr(dwLastError)+#13#10+SysErrorMessage(dwLastError));
    end;
  end;
end;

Tiemo 3. Feb 2009 20:29

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
Danke, Christian. Wirklich super. Ich werde es probieren.... Tausend Dank!!!

himitsu 3. Feb 2009 20:54

Re: Umgebungsvariable eines anderen, laufenden Programms änd
 
wäre es da nicht einfacher das CAD-Programm direkt mit den gewünschten Umgebungsvariablen zu starten, als diese im Nachinein zu ändern?

nicht das dieses CAD-Programm sich noch 'ne Kopie dieser Variablen erstellt und damit arbeitet ... dan kann man da ja ändern was man will und nix passiert.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:26 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 by Thomas Breitkreuz