Przekierowanie standardowych strumieni

2008-03-21 10:33

Dawno, dawno temu miałem dość oryginalny pomysł na program użytkowy. Miała to być (o wiele) lepsza wersja konsoli Windows, której wyższość miała się objawiać w wyglądzie oraz możliwościach; pewne inspiracje czerpałem tutaj z terminali linuksowych oraz basha (nie, nie chodzi tu o serwis z cytatami z IRC-a ;P). Jak większość “genialnych” pomysłów, także i ten nie doczekał się realizacji. Przyczyną było to, iż nie za bardzo wówczas wiedziałem, jak można zapewnić, aby aplikacje konsolowe funkcjonowały w ramach projektowanego terminala tak, jak to robią w domyślnym oknie konsoli w Windows. Większość problemu rozbijała się o to, jak pobierać dane przez ów program produkowane i jak przekazywać do niego te wpisywane przez użytkownika.

Przekierowane standardowe wyjście
Konsolka mała i ciasna, ale własna

Jako że było to bardzo dawno temu, nie znałem wtedy jeszcze takich terminów jak ‘standardowe wejście/wyjście’, ‘proces potomny’ czy ‘pipe‘, które byłyby tutaj bardzo pomocne. Dowiedziałem się o nich więcej dopiero znacznie później, przy czym wiedza ta w większości odnosiła się do systemu zgoła innego niż Windows :)
Pomyślałem jednak, że do problemu można by wrócić – zwłaszcza, że pytanie o to, jak uruchomić program z przekierowanym wejściem i wyjściem, pojawia się stosunkowo często. Okazało się, że nie jest to specjalnie trudne i sprowadza się właściwie do trzech kroków – z których tylko pierwszy może być nieco zakręcony. Cała procedura (w Windows) może wyglądać na przykład tak:

  1. Tworzymy dwie lub trzy jednokierunkowe rury (pipe, tworzone funkcją CreatePipe), które posłużą nam do odbierania wyjścia od i dostarczania wejścia do procesu potomnego. Należy przy tym zwrócić uwagę na dwie rzeczy:
    • Kij ma dwa końce i rura też, więc trzeba uważać, aby właściwie je zidentyfikować. Jeden koniec każdego pipe‘a podamy do procesu potomnego, zaś drugi zachowamy dla siebie. Ważne jest, aby były to właściwe końce pod względem kierunku przepływu danych – i tak w przypadku przekierowywania standardowego wejścia, powinniśmy podać procesowi potomnemu uchwyt do czytania, zaś po stronie naszej aplikacji wykorzystywać uchwyt do pisania.
    • Powstałe uchwyty będziemy chcieli dziedziczyćw procesie potomnym, zaś Windows dopuszcza to tylko wtedy, kiedy wyraźnie taką chęć określimy przy tworzeniu danego uchwytu. Robi się to, wypełniając jedno pole struktury SECURITY_ATTRIBUTES (tak, tej którą w 95% przypadków się ignoruje) i przekazując ją do funkcji tworzącej pipe.
    • Oprócz standardowego wejścia (STDIN) i wyjścia (STDOUT), istnieje jeszcze standardowe wyjście błędów (STDERR), które również możemy chcieć przekierować. Stąd może być konieczny trzeci pipe, o takim samym przypisaniu końców jak ten od STDOUT.
  2. Tworzymy proces potomny (CreateProcess). Musimy mu podać właściwe końce rurek w strukturze STARTUPINFO oraz poinstruować Windows, by były one wykorzystywane (flaga STARTF_USESTDHANDLES). Ponadto musimy wskazać, że chcemy dziedziczyć uchwyty i że utworzony proces konsolowy nie powinien pokazywać okienka (CREATE_NO_WINDOW) – to już przekazujemy w parametrach CreateProcess.
  3. Obsługujemy wejście i wyjście procesu potomnego. Na to drugie (oraz na zakończenie procesu potomnego) możemy czekać przy pomocy funkcji w rodzaju WaitForMultipleObjects. Natomiast organizacja wejścia zależy już od naszej aplikacji. Warto pamiętać, że w standardowej konsoli jest ono buforowane wierszami, zatem i my powinniśmy wysyłać coś do procesu potomnego dopiero wtedy, gdy zbierzemy całą linijkę danych wejściowych.

Łatwe, prawda? ;-) W innych systemach operacyjnych robi się to minimalnie inaczej (pipe, fork, dup2, …), ale ogólna idea jest podobna. Jak widać, kilkoma małymi rurkami można zdziałać całkiem sporo :]

Tags: , , , ,
Author: Xion, posted under Programming »


One comment for post “Przekierowanie standardowych strumieni”.
Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.