![]() |
Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr
Jerzy Wałaszek
|
Nauczymy się czym jest proces oraz jak go utworzyć i zatrzymać.

Pobierz plik z przykładem {z tego archiwum}.
Czym jest proces? Cytuję poniższą definicję z pliku pomocy Win32 API:
"Proces jest wykonującą się aplikacją złożoną z
prywatnej, wirtualnej przestrzeni adresowej, kodu, danych oraz innych zasobów
systemowych takich jak pliki, podprocesy oraz obiekty synchronizacji widoczne
dla tego procesu."
Z powyższego tekstu można wyciągnąć wniosek, iż proces "posiada" kilka obiektów: przestrzeń adresową, wykonujący(e) się moduł(y) oraz wszystko, co te moduły utworzą lub otworzą. Proces co najmniej musi się składać z wykonującego się modułu, prywatnej przestrzeni adresowej oraz wątku. Każdy proces musi posiadać przynajmniej jeden watek. Co to jest wątek? W rzeczywistości wątek jest kolejką wykonań. Gdy system Windows po raz pierwszy tworzy pewien proces, przydziela mu tylko jeden watek, który zwykle rozpoczyna wykonanie od pierwszej instrukcji modułu. Jeśli później proces ten będzie potrzebował więcej wątków, to może je sobie jawnie stworzyć.
Gdy system Windows otrzyma polecenie utworzenia procesu, tworzy dla niego prywatną przestrzeń adresową w pamięci, a następnie rozmieszcza w niej plik wykonywalny. Po wykonaniu tego zadania tworzy główny wątek dla procesu.
W bibliotece Win32 istnieje również możliwość tworzenia procesów z wnętrza swoich własnych programów poprzez wywołanie funkcji CreateProcess o następującej składni:
CreateProcess PROTO lpApplicationName: DWORD,\
lpCommandLine: DWORD,\
lpProcessAttributes: DWORD,\
lpThreadAttributes: DWORD,\
bInheritHandles: DWORD,\
dwCreationFlags: DWORD,\
lpEnvironment: DWORD,\
lpCurrentDirectory: DWORD,\
lpStartupInfo: DWORD,\
lpProcessInformation: DWORD
Niech cię nie przeraża liczba parametrów. Większość z nich można zignorować.
PROCESS_INFORMATION STRUCT
hProcess HANDLE ? ; uchwyt procesu potomnego
hThread HANDLE ? ; uchwyt wątku głównego w procesie potomnym
dwProcessId DWORD ? ; numer ID procesu potomnego
dwThreadId DWORD ? ; numer ID głównego wątku procesu potomnego
PROCESS_INFORMATION ENDS
Uchwyt do procesu i numer ID procesu to dwie różne
rzeczy. Numer ID procesu jest unikalnym identyfikatorem
dla procesu w systemie. Uchwyt procesu jest pewną wartością zwracaną przez
system Windows do użycia przez inne funkcje API
powiązane z procesami. Uchwytu procesu nie można użyć do identyfikacji ponieważ
nie jest on unikalny.
Po wywołaniu CreateProcess zostaje utworzony nowy proces i następuje natychmiastowy powrót z wywołania. Można sprawdzić, czy nowy proces jest wciąż aktywny wywołując funkcję GetExitCodeProcess o następującej składni:
GetExitCodeProcess PROTO hProcess:DWORD, lpExitCode:DWORD
Jeśli wywołanie to się powiedzie, to pod adresem lpExitCode
umieszczony zostanie stan zakończenia badanego procesu. Jeśli wartość pod
adresem lpExitCode jest równa stałej
STILL_ACTIVE, to proces się wciąż wykonuje.
Możesz siłowo wymusić zakończenie procesu poprzez wywołanie funkcji TerminateProcess o następującej składni:
TerminateProcess PROTO hProcess:DWORD, uExitCode:DWORD
Można określić dowolny kod wyjścia dla procesu. TerminateProcess
nie jest czystym sposobem zakończenia procesu, ponieważ biblioteki
dynamiczne DLL dołączone do procesu nie zostaną
poinformowane, iż ich proces zakończył się.

Poniższy przykład utworzy nowy proces, gdy użytkownik wybierze z menu opcję Twórz Proces. Będzie on próbował wykonać program "msgbox.exe", który utworzyliśmy na początku naszego kursu (plik programu jest dołączony do archiwum zip z kodem źródłowym). Gdy użytkownik zechce zakończyć ten nowy proces, może wybrać z menu opcję Zakończ Proces. Program sprawdzi najpierw, czy nowy proces został już usunięty, a jeśli nie, to program wywoła funkcję TerminateProcess w celu usunięcia go.
Plik PROCESS.ASM
.386
.MODEL FLAT, STDCALL
OPTION CASEMAP:NONE
WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD
INCLUDE \masm32\include\windows.inc
INCLUDE \masm32\include\user32.inc
INCLUDE \masm32\include\kernel32.inc
INCLUDELIB \masm32\lib\user32.lib
INCLUDELIB \masm32\lib\kernel32.lib
.CONST
IDM_CREATE_PROCESS EQU 1
IDM_TERMINATE EQU 2
IDM_EXIT EQU 3
.DATA
ClassName DB "Win32ASMProcessClass", 0
AppName DB "Przykład Procesu w Asemblerze", 0
MenuName DB "FirstMenu", 0
ProcessInfo PROCESS_INFORMATION <>
ProgramName DB "msgbox.exe", 0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HANDLE ?
ExitCode DWORD ?
.CODE
start:
INVOKE GetModuleHandle, NULL
mov hInstance, eax
INVOKE GetCommandLine
mov CommandLine, eax
INVOKE WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
INVOKE ExitProcess, eax
WinMain PROC hInst: HINSTANCE,\
hPrevInst: HINSTANCE,\
CmdLine: LPSTR,\
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_WINDOW+1
mov wc.lpszMenuName, OFFSET MenuName
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, CW_USEDEFAULT,\
CW_USEDEFAULT, 300, 200, NULL, NULL,\
hInst, NULL
mov hwnd, eax
INVOKE ShowWindow, hwnd, SW_SHOWNORMAL
INVOKE UpdateWindow, hwnd
INVOKE GetMenu, hwnd
mov hMenu, 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 startInfo: STARTUPINFO
.IF uMsg==WM_DESTROY
INVOKE PostQuitMessage, NULL
.ELSEIF uMsg==WM_INITMENUPOPUP
INVOKE GetExitCodeProcess, ProcessInfo.hProcess, ADDR ExitCode
.IF eax==TRUE
.IF ExitCode==STILL_ACTIVE
INVOKE EnableMenuItem, hMenu, IDM_CREATE_PROCESS, MF_GRAYED
INVOKE EnableMenuItem, hMenu, IDM_TERMINATE, MF_ENABLED
.ELSE
INVOKE EnableMenuItem, hMenu, IDM_CREATE_PROCESS, MF_ENABLED
INVOKE EnableMenuItem, hMenu, IDM_TERMINATE, MF_GRAYED
.ENDIF
.ELSE
INVOKE EnableMenuItem, hMenu, IDM_CREATE_PROCESS, MF_ENABLED
INVOKE EnableMenuItem, hMenu, IDM_TERMINATE, MF_GRAYED
.ENDIF
.ELSEIF uMsg==WM_COMMAND
mov eax, wParam
.IF lParam==0
.IF ax==IDM_CREATE_PROCESS
.IF ProcessInfo.hProcess!=0
INVOKE CloseHandle, ProcessInfo.hProcess
mov ProcessInfo.hProcess, 0
.ENDIF
INVOKE GetStartupInfo, ADDR startInfo
INVOKE CreateProcess, ADDR ProgramName,\
NULL, NULL, NULL, FALSE,\
NORMAL_PRIORITY_CLASS,\
NULL, NULL, ADDR startInfo,\
ADDR ProcessInfo
INVOKE CloseHandle, ProcessInfo.hThread
.ELSEIF ax==IDM_TERMINATE
INVOKE GetExitCodeProcess, ProcessInfo.hProcess,\
ADDR ExitCode
.IF ExitCode==STILL_ACTIVE
INVOKE TerminateProcess, ProcessInfo.hProcess, 0
.ENDIF
INVOKE CloseHandle, ProcessInfo.hProcess
mov ProcessInfo.hProcess, 0
.ELSE
INVOKE DestroyWindow, hWnd
.ENDIF
.ENDIF
.ELSE
INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc ENDP
END start
Plik PROCESS.RC
// Stałe dla menu
#define IDM_CREATE_PROCESS 1
#define IDM_TERMINATE 2
#define IDM_EXIT 3
FirstMenu MENU
{
POPUP "&Proces"
{
MENUITEM "&Twórz Proces", IDM_CREATE_PROCESS
MENUITEM "&Zakończ Proces", IDM_TERMINATE
MENUITEM SEPARATOR
MENUITEM "&Koniec", IDM_EXIT
}
}
Program ten tworzy główne okno i pobiera uchwyt menu do przyszłego użytku. Następnie czeka, aż użytkownik wybierze polecenie z menu. Gdy użytkownik wybierze element menu Proces w głównym menu, obsłużymy wiadomość WM_INITMENUPOPUP, aby zmodyfikować elementy menu wewnątrz listy zanim zostaną wyświetlone.
.ELSEIF uMsg==WM_INITMENUPOPUP
INVOKE GetExitCodeProcess, ProcessInfo.hProcess, ADDR ExitCode
.IF eax==TRUE
.IF ExitCode==STILL_ACTIVE
INVOKE EnableMenuItem, hMenu, IDM_CREATE_PROCESS, MF_GRAYED
INVOKE EnableMenuItem, hMenu, IDM_TERMINATE, MF_ENABLED
.ELSE
INVOKE EnableMenuItem, hMenu, IDM_CREATE_PROCESS, MF_ENABLED
INVOKE EnableMenuItem, hMenu, IDM_TERMINATE, MF_GRAYED
.ENDIF
.ELSE
INVOKE EnableMenuItem, hMenu, IDM_CREATE_PROCESS, MF_ENABLED
INVOKE EnableMenuItem, hMenu, IDM_TERMINATE, MF_GRAYED
.ENDIF
Dlaczego chcemy obsługiwać tę wiadomość? Ponieważ chcemy przygotować
elementy menu na rozwijanej liście zanim użytkownik będzie mógł je
zobaczyć. W naszym przykładzie jeżeli nowy proces nie został jeszcze
uruchomiony, chcemy uaktywnić element menu Twórz Proces
i wyłączyć Zakończ Proces. Jeśli proces jest już
aktywny, to wykonujemy operacje odwrotne.
Sprawdzenia, czy proces wciąż wykonuje się, dokonujemy wywołując funkcję GetExitCodeProcess z parametrem uchwytu tego procesu, który zwróciła funkcja CreateProcess. Jeśli GetExitCodeProcess zwraca wartość FALSE, to oznacza to, iż proces nie został jeszcze uruchomiony, zatem wyłączamy element menu Zakończ Proces. Jeśli GetExitCodeProcess zwraca TRUE, to wiemy, iż nowy proces został uruchomiony, lecz musimy sprawdzić dalej, czy wciąż wykonuje się. Zatem porównujemy wartość w ExitCode z wartością stałej STILL_ACTIVE, a jeśli są równe, to proces wciąż wykonuje się: musimy wyłączyć opcję menu Twórz Proces, ponieważ nie chcemy uruchamiać kilku równolegle wykonujących się procesów.
.IF ax==IDM_CREATE_PROCESS
.IF ProcessInfo.hProcess!=0
INVOKE CloseHandle, ProcessInfo.hProcess
mov ProcessInfo.hProcess, 0
.ENDIF
INVOKE GetStartupInfo, ADDR startInfo
INVOKE CreateProcess, ADDR ProgramName,\
NULL, NULL, NULL, FALSE,\
NORMAL_PRIORITY_CLASS,\
NULL, NULL, ADDR startInfo,\
ADDR ProcessInfo
INVOKE CloseHandle, ProcessInfo.hThread
Gdy użytkownik wybierze element menu Twórz Proces,
najpierw sprawdzamy, czy pole hProcess
struktury PROCESS_INFORMATION jest już zamknięte.
Jeśli jest to pierwszy raz, to wartość hProcess zawsze
będzie wynosiła zero, ponieważ strukturę
PROCESS_INFORMATION definiujemy w sekcji danych
zainicjowanych .DATA. Jeśli wartość pola
hProcess nie wynosi 0, oznacza to, iż proces
potomny zakończył się, lecz nie zamknęliśmy jeszcze jego uchwytu. Zatem
dokonujemy zamknięcia uchwytu procesu.
Wywołujemy funkcję GetStartupInfo w celu wypełnienia struktury startupinfo, którą przekażemy do funkcji CreateProcess. W następnej kolejności wywołujemy funkcję CreateProcess, aby rozpocząć nowy proces. Zwróć uwagę, iż nie sprawdzam wartości zwrotnej z CreateProcess, ponieważ skomplikowało by to nasz przykład. W rzeczywistości powinieneś to zrobić. Bezpośrednio za CreateProcess zamykamy uchwyt głównego wątku zwrócony w strukturze ProcessInfo. Zamknięcie uchwytu nie oznacza zakończenia wątku, a jedynie to, iż nie chcemy używać tego uchwytu do odwoływania się do wątku z naszego programu. Jeśli go nie zamkniemy, to spowodujemy wyciek zasobów.
.ELSEIF ax==IDM_TERMINATE
INVOKE GetExitCodeProcess, ProcessInfo.hProcess,\
ADDR ExitCode
.IF ExitCode==STILL_ACTIVE
INVOKE TerminateProcess, ProcessInfo.hProcess, 0
.ENDIF
INVOKE CloseHandle, ProcessInfo.hProcess
mov ProcessInfo.hProcess, 0
Gdy użytkownik wybierze opcję menu Zakończ Proces,
sprawdzamy, czy nowy proces jest wciąż aktywny wywołując funkcję
GetExitCodeProcess. Jeśli tak, to wywołujemy
funkcję TerminateProcess w celu zakończenia działania
tego procesu. Również zamykamy uchwyt procesu potomnego, ponieważ nie jest nam
on już do niczego potrzebny.
Ta sama aplikacja w Pascalu:
Uwaga: do katalogu projektowego przekopiuj z archiwum tut14.zip program msbox.exe. Plik zasobów zapisz pod nazwą RSRC.RC.
{********************************
** I Liceum Ogólnokształcące **
** w Tarnowie **
** mgr Jerzy Wałaszek **
********************************}
program Proces;
uses Windows;
const
IDM_CREATE_PROCESS = 1;
IDM_TERMINATE = 2;
IDM_EXIT = 3;
ClassName = 'Win32ASMProcessClass';
AppName = 'Przykład Procesu w Asemblerze';
MenuName = 'FirstMenu';
ProgramName = 'msgbox.exe';
var
ProcessInfo : PROCESS_INFORMATION;
hInstance : HINST;
hMenu : HANDLE;
ExitCode : longword;
function WndProc(hWnd:HWND;uMsg:UINT;wParam:WPARAM;lParam:LPARAM) : longint;
var
startInfo : STARTUPINFO;
begin
Result := 0;
case uMsg of
WM_DESTROY: PostQuitMessage(0);
WM_INITMENUPOPUP:
begin
if GetExitCodeProcess(ProcessInfo.hProcess,ExitCode) then
begin
if ExitCode = STILL_ACTIVE then
begin
EnableMenuItem(hMenu,IDM_CREATE_PROCESS,MF_GRAYED);
EnableMenuItem(hMenu,IDM_TERMINATE,MF_ENABLED);
end
else
begin
EnableMenuItem(hMenu,IDM_CREATE_PROCESS,MF_ENABLED);
EnableMenuItem(hMenu,IDM_TERMINATE,MF_GRAYED);
end
end
else
begin
EnableMenuItem(hMenu,IDM_CREATE_PROCESS,MF_ENABLED);
EnableMenuItem(hMenu,IDM_TERMINATE,MF_GRAYED);
end
end;
WM_COMMAND:
if lParam = 0 then
case (wParam and $ffff) of
IDM_CREATE_PROCESS:
begin
if ProcessInfo.hProcess <> 0 then
begin
CloseHandle(ProcessInfo.hProcess);
ProcessInfo.hProcess := 0;
end;
GetStartupInfo(@startInfo);
CreateProcess(ProgramName,0,0,0,false,NORMAL_PRIORITY_CLASS,
0,0,startInfo,ProcessInfo);
CloseHandle(ProcessInfo.hThread);
end;
IDM_TERMINATE:
begin
GetExitCodeProcess(ProcessInfo.hProcess,ExitCode);
if ExitCode = STILL_ACTIVE then
TerminateProcess(ProcessInfo.hProcess,0);
CloseHandle(ProcessInfo.hProcess);
ProcessInfo.hProcess := 0;
end
else DestroyWindow(hWnd);
end;
else Result := DefWindowProc(hWnd,uMsg,wParam,lParam);
end;
end;
function WinMain(hInst,hPrevInst:HINST;CmdLine:LPSTR;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_WINDOW + 1;
lpszMenuName := MenuName;
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,CW_USEDEFAULT,CW_USEDEFAULT,
300,200,0,0,hInst,0);
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
hMenu := GetMenu(hwnd);
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,GetCommandLine,SW_SHOWDEFAULT));
end.
|
Autorem
kursu jest
Iczelion. Kurs
programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną
zgodą autora. |
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe