Forum: PC-Programmierung Win32: Probleme mit CreateProcess und Pipes


von Sigi (Gast)


Lesenswert?

Hi,

wenn ich in meinem Win32-Programm einen Prozess per
createProcess erzeuge und die Std-Ein/Ausgabe in
Pipes umlenke, dann terminiert der letzte
Read-Zugriff auf die Output-Pipe nicht.

Die einzige Abhilfe bis jetzt ist GetExitCodeProcess
kurz vor dem Read-Zugriff, was ein STILL_ACTIVE
auswirft. Ich bin mir aber relativ sicher, dass ein
Read-Zugriff automatisch bei Prozessterminierung
ebenfalls terminieren kann.

Habe ich vlt. vergessen, ein Flag oÄ zu setzen?

Gruss

von tictactoe (Gast)


Lesenswert?

Hast du auch auch sicher gestellt, dass das schreibende Ende entweder 
nicht auch vererbt worden ist oder in allen Prozessen, die es vererbt 
bekommen haben, auch geschlossen worden ist?

von Sigi (Gast)


Angehängte Dateien:

Lesenswert?

tictactoe schrieb:
> Hast du auch auch sicher gestellt, dass das schreibende
> Ende entweder nicht auch vererbt worden ist
> oder in allen Prozessen, die es vererbt
> bekommen haben, auch geschlossen worden ist?

Sorry, habe seit Jahren kaum noch was mit Prozessen
und Pipes gemacht, ich stelle am besten mal eine
gekürzte Version hier rein.

von Peter II (Gast)


Lesenswert?

Hast du es mal ohne das STARTF_USESTDHANDLES und Inheritance getestet?

von Sigi (Gast)


Lesenswert?

Peter II schrieb:
> Hast du es mal ohne das STARTF_USESTDHANDLES und Inheritance
> getestet?

ohne STARTF_USESTDHANDLES: keine Umleitung der Ausgabe
(Ausgabe wird direkt im Koonsolenfenster ausgegeben,
die Output-Pipe wird scheinbar nicht verwendet)

Ohne Inheritance: kein Unterschied, Inheritance wird
aber glaube ich benötigt.

von Peter II (Gast)


Lesenswert?

Sigi schrieb:
> ohne STARTF_USESTDHANDLES: keine Umleitung der Ausgabe
> (Ausgabe wird direkt im Koonsolenfenster ausgegeben,
> die Output-Pipe wird scheinbar nicht verwendet)
>
> Ohne Inheritance: kein Unterschied, Inheritance wird
> aber glaube ich benötigt.

wenn ich die doku richtig verstehe ist Inheritance etwas anders.

Statt dem STARTF_USESTDHANDLES verwende mal hStdInput, hStdOutput und 
hStdError

von Sigi (Gast)


Lesenswert?

Das werde ich mal Morgen Früh ausprobieren
und mich erst mal wieder in die Docs eingraben.

Bis dahin Gute Nacht.

von Peter II (Gast)


Lesenswert?

Ich habe noch mal darüber nachgedacht.

Es dürfte normal sein, das der Read blockiert. Die Pipe ist ja immer 
noch offen. Sie wird erst durch das CloseHandle geschlossen. Man könnte 
ja den nächsten Prozess mit diese Pipe starten.

von Sigi (Gast)


Lesenswert?

Ich habe ebenfalls nochmal in die Pipe-Docs
reingeschaut. Inheritance muss auf jeden Fall
sein (auf Pipes werden ja im ChildProcess
zugegriffen etc.). Und ReadFile blockiert
solange, bis der entsprechende Write-Seite
geschlossen wird.

Ich habe das jetzt wie folgt gemacht:

  DWORD dwExitCode;

  GetExitCodeProcess(piProcInfo.hProcess,&dwExitCode);
  if(dwExitCode != STILL_ACTIVE)
    CloseHandle(g_hStdOut_W);

Es muss aber sichergestellt werden, dass die
Write-Seite nur einmal geschlossen wird, sonst
gibt's einen Fehler. Kann man einfach durch ein
Flag lösen. Anschliessend kann von der Read-Seite
die restlichen Daten gelesen werden.

Inzwischen glaube ich, dass es auch keine andere
Möglichkeit gibt, denn wer sonst sollte die
Write-Seite schliessen.

von Peter II (Gast)


Lesenswert?

Sigi schrieb:
> Ich habe ebenfalls nochmal in die Pipe-Docs
> reingeschaut. Inheritance muss auf jeden Fall
> sein (auf Pipes werden ja im ChildProcess
> zugegriffen etc.).

kann nicht sein, sonst würde es keinen Sinn machen das es noch die 
andere Flags gibt.

Du willst ja nichts von deinen Prozess vererben, du willst neue Pipes 
übergeben.

von Sigi (Gast)


Lesenswert?

Stimmt, in meiner Source wird ja das
entsprechende Flag gelöscht:
  SetHandleInformation(g_hStdOut_R,HANDLE_FLAG_INHERIT,0)

Aber ohne manuelles Freigeben funktioniert
es trotzdem nicht. Muss ich mir aber Heute
Abend nochmal anschauen.

von tictactoe (Gast)


Lesenswert?

Erstens ist da ein Bug und zweitens denke ich, dass die Reihenfolge 
nicht optimal ist.

Ich lass das hier mal stehen als Gedächtnisstütze:
1
  if(!CreatePipe(&g_hStdIn_R,&g_hStdIn_W,&saAttr,0))
2
    ...
3
  
4
  if(!CreatePipe(&g_hStdOut_R,&g_hStdOut_W,&saAttr,0))
5
    ...
Der Bug ist hier:
1
  
2
  // Ensure the read handle to the pipe for STDOUT is not inherited.
3
  if(!SetHandleInformation(g_hStdIn_R,HANDLE_FLAG_INHERIT,0))
4
  {
5
    printf("Error: createStdIn inherited\n");
6
    return;
7
  }
8
9
  if(!SetHandleInformation(g_hStdOut_R,HANDLE_FLAG_INHERIT,0))
10
  {
11
    printf("Error: createStdOut inherited\n");
12
    return;
13
  }
Im ersten if soll natürlich g_hStdIn_W statt g_hStdIn_R stehen. Du 
willst ja das beschreibbare Ende nicht vererben.

Und dann solltest du nach dem Starten des Prozesses die nicht benutzten 
Enden der Pipes schließen, bevor du mit dem Datenaustausch beginnst:
1
  bSuccess = CreateProcess
2
  (
3
    NULL,
4
    szCmdLine,     // command line
5
    NULL,          // process security attributes
6
    NULL,          // primary thread security attributes
7
    TRUE,          // handles are inherited
8
    0,             // creation flags
9
    NULL,          // use parent's environment
10
    NULL,          // use parent's current directory
11
    &siStartInfo,  // STARTUPINFO pointer
12
    &piProcInfo    // receives PROCESS_INFORMATION
13
  );
14
   
15
  // If an error occurs, exit the application.
16
  if(!bSuccess)
17
  {
18
    printf("Error: createProcess failed\n");
19
    return;
20
  }
21
  CloseHandle(g_hStdIn_R);   // <- hier
22
  CloseHandle(g_hStdOut_W);  // <- und hier

von Sigi (Gast)


Lesenswert?

tictactoe schrieb:
> Im ersten if soll natürlich g_hStdIn_W statt g_hStdIn_R stehen.

Meine erste Version war aus mehreren Quellen
zusammenkopiert, da ist mir dieser Fehler
passiert. Den habe ich aber relativ schnell
rausbekommen.

tictactoe schrieb:
> Und dann solltest du nach dem Starten des Prozesses die nicht benutzten
> Enden der Pipes schließen, bevor du mit dem Datenaustausch beginnst:

Und genau da lag der eigentliche Knackpunkt:
Ohne das Schliessen bleiben die Pipes offen und
ReadFile terminiert nicht. Nach dem Schliessen
funktioniert's natürlich wunderbar.

Und sehr wichtig, weiterer Bug: In meinem Code
übergebe ich einmal StdIn und zweimal StdOut
(2. mal als StdError), und dass ist gefährlich.
Entweder muss hier per DublicateHandle StdOut
kopiert (=> StdError als "neuer" Handle) oder
aber es muss eine neue Pipe generiert werden.

Mit diesen Änderungen klappt's problemlos.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.