Tutorial 13: Memory Mapped Files Tutorial 14: Process Tutorial 15: Multithreading Programming

We will learn what a process is and how to create and terminate it.

Download the example here.

Preliminary:

What is a process ? I quote this definition from Win32 API reference:

"A process is an executing application that consists of a private virtual address space, code, data, and other operating system resources, such as files, pipes, and synchronization objects that are visible to the process."

As you can see from the definition above, a process "owns" several objects: the address space, the executing module(s), and anything that the executing modules create or open. At the minimum, a process must consist of an executing module, a private address space and a thread. Every process must have at least one thread. What's a thread ? A thread is actually an execution queue. When Windows first creates a process, it creates only one thread per process. This thread usually starts execution from the first instruction in the module. If the process later needs more threads, it can explicitly create them.
When Windows receives a command to create a process, it creates the private memory address space for the process and then it maps the executable file into the space. After that it creates the primary thread for the process.
Under Win32, you can also create processes from your own programs by calling CreateProcess function. CreateProcess has the following syntax:

CreateProcess PROTO lpApplicationName:DWORD, \ lpCommandLine:DWORD, \ lpProcessAttributes:DWORD,\ lpThreadAttributes:DWORD, \ bInheritHandles:DWORD, \ dwCreationFlags:DWORD, \ lpEnvironment:DWORD, \ lpCurrentDirectory:DWORD, \ lpStartupInfo:DWORD, \ lpProcessInformation:DWORD

Don't be alarmed by the number of parameters. We can ignore most of them.

Process handle and process ID are two different things. A process ID is a unique identifier for the process in the system. A process handle is a value returned by Windows for use with other process-related API functions. A process handle cannot be used to identify a process since it's not unique.

After the CreateProcess call, a new process is created and the CreateProcess call return immediately. You can check if the new process is still active by calling GetExitCodeProcess function which has the following syntax:

GetExitCodeProcess PROTO hProcess:DWORD, lpExitCode:DWORD

If this call is successful, lpExitCode contains the termination status of the process in question. If the value in lpExitCode is equal to STILL_ACTIVE, then that process is still running.

You can forcibly terminate a process by calling TerminateProcess function. It has the following syntax:

TerminateProcess PROTO hProcess:DWORD, uExitCode:DWORD

You can specify the desired exit code for the process, any value you like. TerminateProcess is not a clean way to terminate a process since any dll attached to the process will not be notified that the process was terminated.

Example:

The following example will create a new process when the user selects the "create process" menu item. It will attempt to execute "msgbox.exe". If the user wants to terminate the new process, he can select the "terminate process" menu item. The program will check first if the new process is already destroyed, if it is not, the program will call TerminateProcess function to destroy the new process.

.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 "Win32 ASM Process Example",0 MenuName db "FirstMenu",0 processInfo PROCESS_INFORMATION <;> programname db "msgbox.exe",0 .data? hInstance HINSTANCE ? CommandLine LPSTR ? hMenu HANDLE ? ExitCode DWORD ? ; contains the process exitcode ; status from GetExitCodeProcess call. .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

Analysis:

The program creates the main window and retrieves the menu handle for future use. It then waits for the user to select a command from the menu. When the user selects "Process" menu item in the main menu, we process WM_INITMENUPOPUP message to modify the menu items inside the popup menu before it's displayed.

.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

Why do we want to process this message ? Because we want to prepare the menu items in the popup menu before the user can see them. In our example, if the new process is not started yet, we want to enable the "start process" and gray out the "terminate process" menu items. We do the reverse if the new process is already active.

We first check if the new process is still running by calling GetExitCodeProcess function with the process handle that was filled in by CreateProcess function. If GetExitCodeProcess returns FALSE, it means the process is not started yet so we gray out the "terminate process" menu item. If GetExitCodeProcess returns TRUE, we know that a new process has been started, but we have to check further if it is still running. So we compare the value in ExitCode to the value STILL_ACTIVE, if they're equal, the process is still running: we must gray out the "start process" menu item since we don't want to start several concurrent processes.

.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

When the user selects "start process" menu item, we first check if hProcess member of PROCESS_INFORMATION structure is already closed. If this is the first time, the value of hProcess will always be zero since we define PROCESS_INFORMATION structure in .data section. If the value of hProcess member is not 0, it means the child process has exited but we haven't closed its process handle yet. So this is the time to do it.

We call GetStartupInfo function to fill in the STARTUPINFO structure that we will pass to CreateProcess function. After that we call CreateProcess function to start the new process. Note that I haven't checked the return value of CreateProcess because it will make the example more complex. In real life, you should check the return value of CreateProcess. Immediately after CreateProcess, we close the primary thread handle returned in PROCESSINFO structure. Closing the handle doesn't mean we terminate the thread, only that we don't want to use the handle to refer to the thread from our program. If we don't close it, it will cause a resource leak.

.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

When the user selects "terminate process" menu item, we check if the new process is still active by calling GetExitCodeProcess function. If it's still active, we call TerminateProcess function to kill the process. Also we close the child process handle since we have no need for it anymore.


Tutorial 13: Memory Mapped Files Overview Tutorial 15: Multithreading Programming
Software » wiki » Assembler » X86 » icz » uk » tute014.html