Rozdział XXI - Potok


Na tej lekcji zbadamy potok (pipe), czym on jest i do czego możemy go wykorzystać. Aby urozmaicić temat dorzuciłem technikę zmiany koloru tła i tekstu w kontrolce edycyjnej.

 

 

Kod źródłowy możesz pobrać {z tego archiwum}.

 

Teoria

Potok jest przewodem komunikacyjnym lub ścieżką z dwoma końcami. Potoku można użyć do wymiany danych pomiędzy dwoma różnymi procesami, albo pomiędzy takimi samymi procesami. To jak radiotelefon walkie-talkie. Dajesz drugiej osobie jeden zestaw i możecie się nawzajem porozumiewać.

Istnieją dwa rodzaje potoków: potoki anonimowe (anonymous pipes) oraz potoki nazwane (named pipes). Potok anonimowy jest, cóż, anonimowy: tj. możesz z niego korzystać bez potrzeby znania jego nazwy. Potok nazwany jest przeciwieństwem: musisz znać jego nazwę, zanim będziesz mógł go użyć.

Potoki można również sklasyfikować zgodnie z ich własnością: jednokierunkowe lub dwukierunkowe. W potoku jednokierunkowym dane mogą przepływać tylko w jedną stronę: z jednego końca na drugi. Natomiast w potoku dwukierunkowym dane można wymieniać pomiędzy oboma końcami.

Potok anonimowy jest zawsze jednokierunkowy, natomiast potok nazwany może być jedno lub dwukierunkowy. Potok nazwany stosowany jest zwykle w środowisku sieciowym, gdzie serwer może połączyć się z kilkoma klientami.

Na tej lekcji zbadamy nieco potok anonimowy, którego głównym przeznaczeniem jest zastosowanie jako ścieżka komunikacyjna pomiędzy procesem głównym a procesem potomnym lub pomiędzy procesami potomnymi.

Potok anonimowy jest naprawdę użyteczny, gdy obsługujesz aplikację konsoli. Jest ona pewnym typem programu Win32, który używa konsoli do wprowadzania i wyprowadzania danych. Konsola przypomina okienko systemu DOS. Jednakże aplikacja konsoli jest w pełni programem 32 bitowym. Może użyć dowolnej funkcji graficznego interfejsu użytkownika (GUI - Graphic User Interface), jak inne programy GUI. Tak się tylko składa, iż ma on konsolę do swojej dyspozycji.

Aplikacja konsoli ma trzy uchwyty, których może używać do wprowadzania i wyprowadzania danych. Noszą one nazwę uchwytów standardowych: standardowe wejście, standardowe wyjście oraz standardowy potok komunikatów o błędach. Uchwyt standardowego wejścia jest stosowany do odczytu informacji z konsoli, a uchwyt standardowego wyjścia umożliwia wyprowadzanie informacji do konsoli. Uchwyt standardowego kanału błędów ma zastosowanie przy zgłaszaniu błędów, ponieważ jego wyjścia nie można przekierować.

Aplikacja konsoli może pobrać te trzy uchwyty standardowe wywołując funkcję GetStdHandle i określając pożądany uchwyt. Aplikacja graficznego interfejsu użytkownika (GUI) nie posiada konsoli. Jeśli wywołasz GetStdHandle, zwróci ona błąd. Jeśli rzeczywiście chcesz skorzystać z konsoli, możesz wywołać AllocConsole, aby przydzielić nową konsolę. Jednakże nie zapomnij wywołać FreeConsole, aby zwolnić konsolę, gdy już przestanie ci być potrzebna.

Anonimowy potok najczęściej ma zastosowanie do przekierowywania wejścia lub wyjścia potomnej aplikacji konsoli. Proces nadrzędny może być aplikacją konsoli lub aplikacją GUI, ale proces potomny musi być aplikacją konsoli, aby to zadziałało. Jak wiesz, aplikacja konsoli używa standardowych uchwytów dla swojego wejścia lub wyjścia. Jeśli chcemy przekierować wejście lub wyjście aplikacji konsoli, możemy zastąpić te uchwyty uchwytem jednego z końców potoku. Aplikacja konsoli nie będzie miała pojęcia, iż używa uchwytu do jednego z końców potoku. Użyje go jak standardowego uchwytu. Jest to pewien rodzaj poliformizmu w żargonie OOP (Object Oriented Programming - Programowanie Zorientowane Obiektowo). Podejście to jest bardzo mocne, ponieważ w żaden sposób nie musimy zmieniać procesu potomnego.

Jeśli chodzi o aplikację konsoli, to powinieneś jeszcze dowiedzieć się, skąd ona pobiera te standardowe uchwyty. Gdy aplikacja konsoli zostaje utworzona, proces nadrzędny ma dwie możliwości: może utworzyć nową konsolę dla procesu potomnego lub pozwolić mu odziedziczyć własną konsolę. Aby drugie podejście zadziałało, proces nadrzędny musi być aplikacją konsoli lub, jeśli jest aplikacją GUI, musi wywołać najpierw AllocConsole w celu przydzielenia konsoli.

Weźmy się do pracy. Aby utworzyć anonimowy potok, musisz wywołać funkcję CreatePipe o następującym prototypie:

 

CreatePipe PROTO pReadHandle:     DWORD,\
                 pWriteHandle:    DWORD,\
                 pPipeAttributes: DWORD,\
                 nBufferSize:     DWORD

Jeśli wywołanie się powiedzie, to wartość zwrotna będzie różna od zera. W takim przypadku otrzymasz dwa uchwyty, jeden do końca odczytującego, drugi do końca zapisującego potoku. Teraz wypunktuję kroki niezbędne do przekierowania standardowego wyjścia potomnego programu konsoli do twojego własnego procesu. Moja metoda różni się od opisanej w pliku pomocy dla Win32 firmy Borland, która zakłada, iż proces nadrzędny jest aplikacją konsoli i stąd proces potomny może od niego odziedziczyć standardowe uchwyty. Częściej musimy przekierowywać wyjście z aplikacji konsoli do aplikacji GUI.

  1. Utwórz anonimowy potok za pomocą CreatePipe. Nie zapomnij ustawić pola bInheritable struktury SECURITY_ATTRIBUTES na TRUE, aby uchwyty mogły być dziedziczone.
  2. Teraz musisz przygotować parametry przekazywane do CreateProcess, ponieważ wykorzystamy to wywołanie do załadowania i uruchomienia aplikacji konsoli. Istotną strukturą jest STARTUPINFO. Struktura ta określa wygląd głównego okna procesu potomnego, gdy się ono pojawia po raz pierwszy. Jest bardzo ważna dla naszych celów. Możesz ukryć główne okno i przekazać uchwyt potoku potomnemu procesowi konsoli do pracy z nim. Poniżej wyszczególniam pola struktury, które muszą być wypełnione:
  1. Wywołaj CreateProcess, aby załadować aplikację potomną. Po sukcesie tego wywołania proces potomny jest wciąż uśpiony. Został załadowany do pamięci, lecz nie uruchomił się natychmiast.
  2. Zamknij uchwyt zapisujący potoku. Jest to konieczne, ponieważ proces nadrzędny nie używa uchwytu zapisującego potoku, a potok nie będzie pracował, jeśli istnieją dwa końce zapisujące, zatem MUSIMY zamknąć go zanim rozpoczniemy odczyt z potoku. Jednakże nie zamykaj uchwytu zapisującego przed wywołaniem CreateProcess, gdyż zaburzy to twój potok. Powinieneś zamknąć go tuż po powrocie z wywołania CreateProcess, a przed odczytem danych z końca odczytującego potoku.
  3. Teraz możesz odczytać dane z końca odczytującego potoku za pomocą ReadFile. Przy pomocy tej funkcji uruchamiasz proces potomny. Rozpocznie on wykonanie, a kiedy zapisze coś do standardowego uchwytu wyjścia (który w rzeczywistości jest uchwytem do końca zapisującego potoku), dane zostaną przesłane poprzez potok na koniec odczytujący. Możesz potraktować funkcję ReadFile jak wysysanie danych z końca odczytującego potoku. Funkcję tę należy wykonywać cyklicznie, aż zwróci 0, co oznacza, iż nie ma więcej danych do odczytania. Z danymi odczytanymi z potoku możesz zrobić co ci się podoba. W naszym przykładzie wstawiam je do kontrolki edycyjnej.
  4. Zamknij uchwyt odczytujący potoku.

Przykład

Plik PIPE.ASM.

 

.386

.MODEL FLAT, STDCALL

OPTION CASEMAP:NONE

INCLUDE    \masm32\include\windows.inc
INCLUDE    \masm32\include\user32.inc
INCLUDE    \masm32\include\kernel32.inc
INCLUDE    \masm32\include\gdi32.inc
INCLUDELIB \masm32\lib\gdi32.lib
INCLUDELIB \masm32\lib\user32.lib
INCLUDELIB \masm32\lib\kernel32.lib

WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD

.CONST

IDR_MAINMENU EQU     101
IDM_ASSEMBLE EQU 40001

.DATA

ClassName          DB "PipeWinClass", 0
AppName            DB "Potok Jednokierunkowy", 0
EditClass          DB "EDIT", 0
CreatePipeError    DB "Błąd przy tworzeniu potoku", 0
CreateProcessError DB "Błąd przy tworzeniu procesu", 0

;--------------------------------------------------------------
;UWAGA:
;Poniższy wiersz umożliwia wywołanie programu ml.exe. Jeśli
;znajduje się on w innym katalogu niż podany poniżej, to
;musisz odpowiednio zmodyfikować polecenie. W przeciwnym
;razie prezentowany przykład nie uruchomi tego procesu
;--------------------------------------------------------------

CommandLine DB "\masm32\bin\ml /c /coff /Cp test.asm", 0

.DATA?

hInstance HINSTANCE ?
hwndEdit  DD        ?

.CODE

start:
   
    INVOKE GetModuleHandle, NULL
    mov    hInstance, eax
    INVOKE WinMain, hInstance, NULL, NULL, SW_SHOWDEFAULT
    INVOKE ExitProcess, eax

WinMain PROC hInst:     DWORD,\
             hPrevInst: DWORD,\
             CmdLine:   DWORD,\
             CmdShow:   DWORD

LOCAL wc   : WNDCLASSEX
LOCAL msg  : MSG
LOCAL hwnd : HWND

    mov    wc.cbSize, SIZEOF WNDCLASSEX
    mov    wc.style, CS_HREDRAW OR CS_VREDRAW
    mov    wc.lpfnWndProc, OFFSET WndProc
    mov    wc.cbClsExtra, NULL
    mov    wc.cbWndExtra, NULL
    push   hInst
    pop    wc.hInstance
    mov    wc.hbrBackground, COLOR_APPWORKSPACE
    mov    wc.lpszMenuName, IDR_MAINMENU
    mov    wc.lpszClassName, OFFSET ClassName
    INVOKE LoadIcon, NULL, IDI_APPLICATION
    mov    wc.hIcon, eax
    mov    wc.hIconSm, eax
    INVOKE LoadCursor, NULL, IDC_ARROW
    mov    wc.hCursor, eax
    INVOKE RegisterClassEx, ADDR wc
    INVOKE CreateWindowEx, WS_EX_CLIENTEDGE,\
                           ADDR ClassName, ADDR AppName,\
                           WS_OVERLAPPEDWINDOW OR WS_VISIBLE,\
                           CW_USEDEFAULT, CW_USEDEFAULT,\
                           467, 200, NULL, NULL, hInst, NULL
    mov    hwnd, eax

    .WHILE TRUE
        INVOKE GetMessage, ADDR msg, NULL, 0, 0
        .BREAK .IF (!eax)
        INVOKE TranslateMessage, ADDR msg
        INVOKE DispatchMessage, ADDR msg
    .ENDW

    mov eax, msg.wParam
    ret

WinMain ENDP

WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

LOCAL rect:         RECT
LOCAL hRead:        DWORD
LOCAL hWrite:       DWORD
LOCAL startupinfo:  STARTUPINFO
LOCAL pinfo:        PROCESS_INFORMATION
LOCAL buffer[1024]: BYTE
LOCAL bytesRead:    DWORD
LOCAL hdc:          DWORD
LOCAL sat:          SECURITY_ATTRIBUTES

    .IF uMsg==WM_CREATE
        INVOKE CreateWindowEx, NULL, ADDR EditClass, NULL,\
                               WS_CHILD OR WS_VISIBLE OR \
                               ES_MULTILINE OR ES_AUTOHSCROLL OR \
                               ES_AUTOVSCROLL, 0, 0, 0, 0,\
                               hWnd, NULL, hInstance, NULL
        mov    hwndEdit, eax
    .ELSEIF uMsg==WM_CTLCOLOREDIT
        INVOKE SetTextColor, wParam, Yellow
        INVOKE SetBkColor, wParam, Black
        INVOKE GetStockObject, BLACK_BRUSH
        ret
    .ELSEIF uMsg==WM_SIZE
        movzx   edx, WORD PTR lParam
        movzx   ecx, WORD PTR lParam + 2
        INVOKE MoveWindow, hwndEdit, 0, 0, edx, ecx, TRUE
    .ELSEIF uMsg==WM_COMMAND
        .IF lParam==0
            mov eax, wParam
            .IF ax==IDM_ASSEMBLE
                mov   sat.nLength, SIZEOF SECURITY_ATTRIBUTES
                mov   sat.lpSecurityDescriptor, NULL
                mov   sat.bInheritHandle, TRUE
                INVOKE CreatePipe, ADDR hRead, ADDR hWrite, ADDR sat, NULL
                .IF eax==NULL
                    INVOKE MessageBox, hWnd, ADDR CreatePipeError, ADDR AppName, MB_ICONERROR OR MB_OK
                .ELSE
                    mov       startupinfo.cb, SIZEOF STARTUPINFO
                    INVOKE GetStartupInfo, ADDR startupinfo
                    mov       eax, hWrite
                    mov       startupinfo.hStdOutput, eax
                    mov       startupinfo.hStdError, eax
                    mov       startupinfo.dwFlags, STARTF_USESHOWWINDOW OR STARTF_USESTDHANDLES
                    mov       startupinfo.wShowWindow, SW_HIDE

;-------------------------------------------------
; Tworzenie procesu
;-------------------------------------------------

                    INVOKE CreateProcess, NULL, ADDR CommandLine,\
                                          NULL, NULL, TRUE, NULL, NULL,\
                                          NULL, ADDR startupinfo,\
                                          ADDR pinfo
                    .IF eax==NULL
                        INVOKE MessageBox, hWnd,\
                                           ADDR CreateProcessError,\
                                           ADDR AppName,\
                                           MB_ICONERROR OR MB_OK
                    .ELSE
                        INVOKE CloseHandle, hWrite
                        .WHILE TRUE
                            INVOKE RtlZeroMemory, ADDR buffer, 1024
                            INVOKE ReadFile, hRead, ADDR buffer, 1023, ADDR bytesRead, NULL
                            .BREAK .IF (!eax)
                            INVOKE SendMessage, hwndEdit, EM_SETSEL, -1, 0
                            INVOKE SendMessage, hwndEdit, EM_REPLACESEL, FALSE, ADDR buffer
                       .ENDW
                    .ENDIF
                    INVOKE CloseHandle, hRead
                    INVOKE CloseHandle, pinfo.hProcess
                    INVOKE CloseHandle, pinfo.hThread
                .ENDIF
            .ENDIF
        .ENDIF
    .ELSEIF uMsg==WM_DESTROY
        INVOKE PostQuitMessage, NULL
    .ELSE
        INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam		
        ret
    .ENDIF

    xor eax, eax
    ret

WndProc ENDP

END start

 

Plik zasobów PIPE.RC:

 

#define IDR_MAINMENU 101
#define IDM_ASSEMBLE 40001

IDR_MAINMENU MENU DISCARDABLE
{
  POPUP "&Dzia-anie"
  {
    MENUITEM "&Uruchom Asembler", IDM_ASSEMBLE
  }
}

 

Analiza

Nasz przykład wywoła aplikację ml.exe (to oczywiście nasz poczciwy MASM), aby dokonać asemblacji pliku o nazwie test.asm (dołączony do archiwum zip) i przekieruje wyjście ml.exe do kontrolki edycyjnej w jej obszarze roboczym.

Gdy program zostaje załadowany, jak zwykle rejestruje on klasę okna i tworzy okno główne. Podczas tworzenia okna głównego powstaje kontrolka edycyjna, która będzie używana do wyświetlenia danych z wyjścia programu ml.exe.

A teraz interesujący fragment - zmienimy kolory tekstu oraz tła w kontrolce edycyjnej. Gdy kontrolka edycyjna ma zamiar malować swój obszar roboczy, wysyła wiadomość WM_CTLCOLOREDIT do swojego okna nadrzędnego. wParam zawiera uchwyt kontekstu urządzenia, który użyje kontrolka edycyjna do zapisu swojego własnego obszaru roboczego. Możemy wykorzystać tę okazję do zmodyfikowania charakterystyki uchwytu kontekstu urządzenia.

 

    .ELSEIF uMsg==WM_CTLCOLOREDIT
        INVOKE SetTextColor, wParam, Yellow
        INVOKE SetBkColor, wParam, Black
        INVOKE GetStockObject, BLACK_BRUSH
        ret

 

SetTextColor zmienia kolor tekstu na żółty (Yellow). SetBkColor zmienia kolor tła tekstu na czarny (Black). A na koniec pobieramy uchwyt do czarnego pędzla, który zwracamy do systemu Windows. Przy wiadomości WM_CTLCOLOREDIT musisz zwrócić uchwyt do pędzla (brush), który zostanie użyty przez system Windows do pomalowania tła kontrolki edycyjnej. W naszym przykładzie chcemy mieć czarne tło, zatem zwracamy systemowi Windows uchwyt do czarnego pędzla.

Teraz gdy użytkownik wybierze opcję menu Uruchom Asembler, powstanie anonimowy potok.

 

    .IF ax==IDM_ASSEMBLE
        mov  sat.nLength, SIZEOF SECURITY_ATTRIBUTES
        mov  sat.lpSecurityDescriptor, NULL
        mov  sat.bInheritHandle, TRUE

 

Przed wywołaniem CreatePipe musimy najpierw wypełnić strukturę SECURITY_ATTRIBUTES. Zwróć uwagę, iż możemy użyć wartości NULL w polu lpSecurityDescriptor, jeśli nie zależy nam na zabezpieczeniach. A pole bInheritHandle musi zawierać wartość TRUE, aby uchwyty potoków mogły być dziedziczone w procesach potomnych.

 

    INVOKE CreatePipe, ADDR hRead, ADDR hWrite, ADDR sat, NULL

 

Po wykonaniu tych czynności wywołujemy funkcję CreatePipe, która w przypadku powodzenia wypełni zmienne hRead i hWrite kolejno uchwytami do końców czytającego i zapisującego potoku.

 

    mov    startupinfo.cb, SIZEOF STARTUPINFO
    INVOKE GetStartupInfo, ADDR startupinfo
    mov    eax, hWrite
    mov    startupinfo.hStdOutput, eax
    mov    startupinfo.hStdError, eax
    mov    startupinfo.dwFlags, STARTF_USESHOWWINDOW OR STARTF_USESTDHANDLES
    mov    startupinfo.wShowWindow, SW_HIDE

 

Następnie musimy wypełnić strukturę STARTUPINFO. Wywołujemy funkcję GetStartupInfo w celu wypełnienia struktury STARTUPINFO wartościami standardowymi procesu nadrzędnego. MUSISZ wypełnić tę strukturę przy pomocy tego wywołania, jeśli planujesz pracę swojego kodu zarówno pod Windows 9x jak i NT/2000/XP. Po powrocie z wywołania GetStartupInfo możesz zmodyfikować istotne pola. Kopiujemy uchwyt zapisującego końca potoku do pól hStdOutput i hStdError, ponieważ chcemy, aby proces potomny korzystał z niego w miejsce standardowych uchwytów wyjścia i błędu. Chcemy również ukryć okno konsoli procesu potomnego, zatem wstawiamy wartość SW_HIDE do pola wShowWidow. A na koniec musimy zaznaczyć, iż pola hStdOutput, hStdError i wShowWindow zawierają informację i muszą być użyte. Dokonujemy tego określając znaczniki STARTF_USESHOWWINDOW oraz STARTF_USESTDHANDLES w polu dwFlags.

 

    INVOKE CreateProcess, NULL, ADDR CommandLine,\
                          NULL, NULL, TRUE, NULL, NULL,\
                          NULL, ADDR startupinfo,\
                          ADDR pinfo

 

Teraz tworzymy proces potomny za pomocą wywołania CreateProcess. Zwróć uwagę, iż parametr bInheritHandles musi mieć wartość TRUE, aby działał uchwyt do potoku.

 

INVOKE CloseHandle, hWrite

 

Gdy z powodzeniem utworzymy proces potomny, musimy zamknąć zapisujący koniec potoku. Przypomnij sobie, iż przekazaliśmy uchwyt zapisującego końca potoku procesowi potomnemu poprzez strukturę STARTUPINFO. Jeśli nie zamkniemy uchwytu zapisu z naszego końca, to będą istniały dwa końce zapisu. A wtedy potok nie będzie pracował prawidłowo. Musimy zamknąć uchwyt zapisu po wywołaniu CreateProcess, lecz przed odczytem danych z końca odczytującego potoku.

 

    .WHILE TRUE
        INVOKE RtlZeroMemory, ADDR buffer, 1024
        INVOKE ReadFile, hRead, ADDR buffer, 1023, ADDR bytesRead, NULL
        .BREAK .IF (!eax)
        INVOKE SendMessage, hwndEdit, EM_SETSEL, -1, 0
        INVOKE SendMessage, hwndEdit, EM_REPLACESEL, FALSE, ADDR buffer
    .ENDW

 

Teraz jesteśmy gotowi do odczytu danych ze standardowego wyjścia procesu potomnego. Pozostajemy w nieskończonej pętli, aż zabraknie danych do odczytu z potoku. Wywołujemy funkcję RtlZeroMemory, aby wypełnić bufor zerami, a następnie wywołujemy ReadFile przekazując uchwyt potoku w miejscu uchwytu pliku. Zwróć uwagę, iż odczytujemy maksymalnie 1023 bajty, ponieważ ostatni znak (1024-ty) w buforze musi być zerem, aby powstał łańcuch ASCIIZ, który dalej możemy przekazać do kontrolki edycyjnej.

Gdy funkcja ReadFile powróci z danymi w buforze, wypełniamy nimi kontrolkę edycyjną. Jednakże mamy tutaj niewielki problem. Jeśli użyjemy funkcji SetWindowText do wstawienia danych do kontrolki edycyjnej, nowe dane zapiszą dane poprzednio wpisane! My chcemy dołączać dane na koniec tych, które tam już się znajdują.

Aby osiągnąć ten cel, musimy najpierw przesunąć kursor na koniec tekstu w kontrolce edycyjnej wysyłając wiadomość EM_SETSEL z wParam równym -1. Następnie dołączamy dane w tym punkcie przy pomocy wiadomości EM_REPLACESEL.

 

INVOKE CloseHandle, hRead

 

Gdy funkcja ReadFile zwróci wartość NULL, przerywamy wykonywanie pętli i zamykamy uchwyt odczytu.

 

Dodatek w Pascalu

Ta sama aplikacja w Pascalu:

 

{********************************
**  I Liceum Ogólnokształcące  **
**           w Tarnowie        **
**       mgr Jerzy Wałaszek    **
********************************}

program Pipe;

uses Windows;

const
  IDR_MAINMENU       =   101;
  IDM_ASSEMBLE       = 40001;
  ClassName          = 'PipeWinClass';
  AppName            = 'Potok Jednokierunkowy';
  EditClass          = 'EDIT';
  CreatePipeError    = 'Błąd przy tworzeniu potoku';
  CreateProcessError = 'Błąd przy tworzeniu procesu';

//--------------------------------------------------------------
//UWAGA:
// Poniższy wiersz umożliwia wywołanie programu ml.exe. Jeśli
// znajduje się on w innym katalogu niż podany poniżej, to
// musisz odpowiednio zmodyfikować polecenie. W przeciwnym
// razie prezentowany przykład nie uruchomi tego procesu
//--------------------------------------------------------------

CommandLine = 'c:\masm32\bin\ml /c /coff /Cp test.asm';

var
  hInstance : HINST;
  hwndEdit  : HANDLE;

function WndProc(hWnd:HWND;uMsg:UINT;wParam:WPARAM;lParam:LPARAM) : longint;
var
  rect          : RECT;
  sat           : SECURITY_ATTRIBUTES;
  startupinfo   : STARTUPINFO;
  pinfo         : PROCESS_INFORMATION;
  buffer        : array[1..1023] of byte;
  hRead         : longword;
  hWrite        : longword;
  bytesRead     : longword;
  hdc           : longword;

begin
  Result := 0;
  case uMsg of
    WM_CREATE:
      hwndEdit := CreateWindowEx(0,EditClass,0,
                  WS_CHILD or WS_VISIBLE or ES_MULTILINE or ES_AUTOHSCROLL or
                  ES_AUTOVSCROLL,0,0,0,0,hWnd,0,hInstance,0);
    WM_CTLCOLOREDIT:
    begin     
      SetTextColor(wParam,$00ffff);
      SetBkColor(wParam,$000000);
      Result := GetStockObject(BLACK_BRUSH);
    end;
    WM_SIZE:
      MoveWindow(hwndEdit,0,0,lParam and $ffff,lParam shr 16,true);
    WM_COMMAND:
      if (lParam = 0) and ((wParam and $ffff) = IDM_ASSEMBLE) then
      begin
        sat.nLength := sizeof(SECURITY_ATTRIBUTES);
        sat.lpSecurityDescriptor := 0;
        sat.bInheritHandle             := true;
        if not CreatePipe(hRead,hWrite,@sat,0) then
          MessageBox(hWnd,CreatePipeError,AppName,MB_ICONERROR or MB_OK)
        else
        begin
          with startupinfo do
          begin
            cb := sizeof(STARTUPINFO);
            GetStartupInfo(@startupinfo);
            hStdOutput  := hWrite;
            hStdError   := hWrite;
            dwFlags     := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
            wShowWindow := SW_HIDE;
          end;
      
//-------------------------------------------------
// Tworzenie procesu
//-------------------------------------------------

          if not CreateProcess(0,CommandLine,0,0,true,0,0,0,startupinfo,pinfo) then
            MessageBox(hWnd,CreateProcessError,AppName,MB_ICONERROR or MB_OK)
          else
          begin
            CloseHandle(hWrite);
            while true do
            begin
              FillChar(buffer,1024,0);
              if not ReadFile(hRead,buffer,1023,bytesRead,0) then break;
              SendMessage(hwndEdit,EM_SETSEL,-1,0);
              SendMessage(hwndEdit,EM_REPLACESEL,0,longint(@buffer));
            end;
          end;
          CloseHandle(hRead);
          CloseHandle(pinfo.hProcess);
          CloseHandle(pinfo.hThread);
        end;
      end;
    WM_DESTROY: PostQuitMessage(0);
    else Result := DefWindowProc(hWnd,uMsg,wParam,lParam);
  end;
end;

function WinMain(hInst,hPrevInst,CmdLine,CmdShow:longword) : longint;
var
  wc   : WNDCLASSEX;
  msg  : MSG;
  hwnd : HWND;

begin
  with wc do
  begin
    cbSize        := sizeof(WNDCLASSEX);
    style         := CS_HREDRAW or CS_VREDRAW;
    lpfnWndProc   := @WndProc;
    cbClsExtra    := 0;
    cbWndExtra    := 0;
    hInstance     := hInst;
    hbrBackground := COLOR_APPWORKSPACE;
    lpszMenuName  := IDR_MAINMENU;
    lpszClassName := ClassName;
    hIcon         := LoadIcon(0,IDI_APPLICATION);
    hIconSm       := hIcon;
    hCursor       := LoadCursor(0,IDC_ARROW);
  end;
  RegisterClassEx(wc);
  hwnd := CreateWindowEx(WS_EX_CLIENTEDGE,ClassName,AppName,
          WS_OVERLAPPEDWINDOW or WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,
          467,200,0,0,hInst,0);
  while GetMessage(msg,0,0,0) do
  begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;
  Result := msg.wParam;
end;

begin
  hInstance := GetModuleHandle(0);
  ExitProcess(WinMain(hInstance,0,0,SW_SHOWDEFAULT));
end.

 

Autorem kursu jest Iczelion. Kurs programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną zgodą autora.
Tłumaczenie z języka angielskiego, opracowanie HTML i konwersję przykładów programów wykonał mgr Jerzy Wałaszek.



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2017 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.