платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 14 - Процесс

Предыдущая тема Следующая тема Перейти вниз

платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 14 - Процесс

Сообщение  electrik в Пн Июн 29, 2009 6:31 pm

Win32 API. Урок 14. Процесс

Здесь мы изучим, что такое процесс и как его создать и прервать.


Вступление:

Что такое процесс? Я процитирую определение из справочника по Win32 ApI.

"Процесс - это выполняющееся приложение, которое состоит из личного виртуального адресного пространства, кода, данных и других ресурсов операционной системы, таких как файлы, пайпы и синхронизационные объекты, видимые для процесса."

Как вы можете видеть из вышеприведенного определения, у процесса есть несколько объектов: адресное пространство, выполняемый модуль (модули) и все, что эти модули создают или открывают. Как минимум, процесс должен состоять из выполняющегося модуля, личного адресного пространства и ветви. У каждого процесса по крайней мере одна ветвь. Что такое ветвь? Фактически, ветвь - это выполняющаяся очередь. Когда Windows впервые создает процесс, она делает только одну ветвь на процесс. Эта ветвь обычно начинает выполнение с первой инструкции в модуле. Если в дальнейшем понадобится больше ветвей, он может сам создать их.

Когда Windows получает команду для создания процесса, она создает личное адресное пространство для процесса, а затем она загружает исполняемый файл в пространство. После этого она создает основную ветвь для процесса.

Под Win32 вы также можете создать процессы из своих программ с помощью функции Createprocess. Она имеет следующий синтаксис:

function CreateProcess _
(byval lpApplicationName as LPCSTR, _
byval lpCommandLine as LPSTR, _
byval lpprocessAttributes as LPSECURITY_ATTRIBUTES, _
byval lpThreadAttributes as LPSECURITY_ATTRIBUTES, _
byval bInheritHandles  as BOOL, _
byval dwCreationFlags as DWORD, _
byval lpEnvironment as PVOID, _
byval lpCurrentDirectory as LPCSTR, _
byval lpStartupInfo as LPSTARTUPINFO, _
byval lpprocessInformation as LPPROCESS_INFORMATION) as BOOL

Не пугайтесь количества параметров. Большую их часть мы можем игнорировать.

• lpAplicationName --> Имя исполняемого файла с путем или без пути, который вы хотите запустить. Если параметр равен нулю, вы должны предоставить имя исполняемого файла в параметре lpCommandLine.
• lpCommandLine --> Аргументы командной строки к программе, которую вам требуется запустить. Заметьте, что если lpAplicationName равен нулю, этот параметр должен содержать также имя исполняемого файла. Например так: "notepad.exe readme.txt".
• lpprocessAttributes и lpThreadAttributes --> Укажите атрибуты безопасности для процесса и основной ветви. Если они равны NULL'ам, то используются атрибуты безопасности по умолчанию.
• bInheritHandles --> Флаг, который указывает, хотите ли вы, чтобы новый процесс наследовал все открытые хэндлы из вашего процесса.
• dwCreationFlags --> Несколько флагов, которые определяют поведение процесса, который вы хотите создать, например, хотите ли вы, чтобы процесс был создан, но тут же приостановлен, чтобы вы могли проверить его или изменить, прежде, чем он запустится. Вы также можете указать класс приоритета ветви(ей) в новом процессе. Этот класс приоритета используется, чтобы определить планируемый приоритет ветвей внутри процесса. Обычно мы используем флаг NORMAL_PRIORITY_CLASS.
• lpEnviromment --> Указатель на блок памяти, который содержит несколько переменных окружения для нового процесса. Если этот параметр равен NULL, новый процесс наследует их от родительского процесса.
• lpCurrentDirectory --> Указатель на строку, которая указывает текущий диск и директорию для дочернего процесса. NULL - если вы хотите, чтобы дочерний процесс унаследовал их от родительского процесса.
• lpStartupInfo --> Указывает на структуру STARTUPINFO, которая определяет, как должно появиться основное окно нового процесса. Эта структура содержит много членов, которые определяют появление главного окна дочернего процесса. Если вы не хотите ничего особенного, вы можете заполнить данную структуру значениями
родительского процесса, вызвав функцию GetStartupInfo.
• lpprocessInformation --> Указывает на структуру PROCESS_INFORMATION, которая получает идентификационную информацию о новом процессе. Структура PROCESS_INFORMATION имеет следующие параметры:

type PROCESS_INFORMATION
hProcess as HANDLE '  хэндл дочернего процесса
hThread as HANDLE ' хэндл основной ветви дочернего процесса
dwProcessId as DWORD ' ID дочернего процесса
dwThreadId as DWORD ' ID основной ветви
end type

Хэндл процесса и ID процесса - это две разные вещи. ID процесса - это уникальный идентификатор процесса в системе. Хэндл процесса - это значение, возвращаемое Windows для использования другими API-функциями, связанными с процессами. Хэндл процесса не может использоваться для идентификации процесса, так как он не уникален.

После вызова функции CreateProcess, создается новый процесс и функция сразу же возвращается. Вы можете проверить, является ли еще процесс активным, вызвав функцию GetExitCodeProcess, которая имеет следующий синтаксис:

function GetExitCodeProcess (byval hprocess as HANDLE, byval lpExitCode as PDWORD) as BOOL

Если вызов этой функции успешен, lpExitCode будет содержать код выхода запрашиваемого процесса. Если значение в lpExitCode равно STILL_ACTIVE, тогда это означает, что процесс по-прежнему запущен.

Вы можете принудительно прервать процесс, вызвав функцию Terminateprocess. У нее следующий синтаксис:

function TerminateProcess (byval hprocess as HANDLE, byval uExitCode as UINT) as BOOL

Вы можете указать желаемый код выхода для процесса, любое значение, какое захотите. TerminateProcess - не лучший путь прервать процесс, так как любые используемые им dll не будут уведомлены о том, что процесс был прерван.

Пример:

Следующий пример создаст новый процесс, когда юзер выберет пункт меню "create process". Он попытается запустить "msgbox.exe". Если пользователь захочет прервать новый процесс, он может выбрать пункт меню "terminate process". Программа будет сначала проверять, уничтожен ли уже новый процесс, если нет, программа вызовет TerminateProcess для этого.

файл process.bas

Код:

' процессы
#define WIN_INCLUDEALL
#include "windows.bi"
#include "process.bi"
declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
  byval szCmdLine as LPSTR, _
  byval iCmdShow as integer ) as integer



' начало программы

dim shared processInfo as PROCESS_INFORMATION ' структура PROCESS_INFORMATION
dim shared ExitCode as DWORD ' содержит код выхода процесса после вызова функции GetExitCodeprocess
dim shared hMenu as HANDLE ' хэндл меню
end WinMain(GetModuleHandle( NULL ),NULL,GetCommandLine() , SW_SHOWNORMAL ) ' вызвать основную функцию
' здесь заканчивается программа

' процедура окна
function  WndProc _
(byval hwnd as HWND, _ ' хэндл окна
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный  параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений
dim startInfo as STARTUPINFO ' структура STARTUPINFO
function = 0
select case uMsg  'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
case WM_CREATE
hMenu = GetMenu(hWnd) ' Получаем хэндл меню
case WM_INITMENUPOPUP ' если получили сообщение WM_INITMENUPOPUP
if GetExitCodeProcess _ ' получим код выхода
(processInfo.hProcess, _ '  хэндл дочернего процесса
@ExitCode) _ ' указатель на переменную куда запишется код выхода
= TRUE then ' если успешно
if ExitCode = STILL_ACTIVE then ' если процесс активен
    EnableMenuItem _ ' изменим пункт меню
    (hMenu, _ ' хэндл меню
    IDM_CREATE_PROCESS, _ ' идентификатор меню
    MF_GRAYED) ' сделаем недоступным
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_TERMINATE, _ ' идентификатор меню
MF_ENABLED) ' сделаем доступным
else
    EnableMenuItem _ ' изменим пункт меню
    (hMenu, _ ' хэндл меню
    IDM_CREATE_PROCESS, _ ' идентификатор меню
    MF_ENABLED) ' сделаем доступным
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_TERMINATE, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным
end if
else
    EnableMenuItem _ ' изменим пункт меню
    (hMenu, _ ' хэндл меню
    IDM_CREATE_PROCESS, _ ' идентификатор меню
    MF_ENABLED) ' сделаем доступным
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_TERMINATE, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным
end if

case WM_COMMAND ' если получили сообщение WM_COMMAND
if lParam = 0 then ' если lParam = 0, значит идет сообщение от меню
select case loword(wParam)  ' начинаем обрабатывать элементы меню
case IDM_CREATE_PROCESS' если в меню выбрали Create Process
if processInfo.hProcess <> 0 then ' если хэндл дочернего процесса открыт
CloseHandle _ ' закроем хэндл
(processInfo.hProcess) ' хэндл дочернего процесса
processInfo.hProcess = 0 ' запишем в хэндл дочернего процесса 0
end if
GetStartupInfo (@startInfo) ' заполним структуру STARTUPINFO
CreateProcess _ ' создадим процесс
(programname, _ ' имя исполняемой программы
NULL, _ ' параметры командной строки
NULL, _ ' атрибуты безопасности для процесса
NULL, _ ' атрибуты безопасности основной ветви
FALSE, _ ' вданном случае не наследовать открытые хэндлы
NORMAL_PRIORITY_CLASS, _ ' поведение процесса
NULL, _ ' переменные окружения
NULL, _ ' указатель на строку которая указывает текущий диск и директорию
@startInfo, _ ' указатель на структуру STARTUPINFO
@processInfo) ' указатель на структуру PROCESS_INFORMATION
CloseHandle _ ' закроем хэндл
(processInfo.hThread) ' хэндл основной ветви дочернего процесса
case IDM_TERMINATE ' если в меню выбрали Terminate Process
GetExitCodeProcess _ ' получим код выхода
(processInfo.hProcess, _ '  хэндл дочернего процесса
@ExitCode) ' указатель на переменную куда запишется код выхода
if ExitCode = STILL_ACTIVE then ' если процесс активен
    TerminateProcess _ ' завершим процесс
    (processInfo.hprocess, _ ' хэндл дочернего процесса
    0)
end if
CloseHandle _ ' закроем хэндл
(processInfo.hprocess)  ' хэндл дочернего процесса
processInfo.hprocess = 0 ' запишем в хэндл дочернего процесса 0
    case IDM_EXIT
        DestroyWindow(hWnd) ' разрушим окно и выдем из программы
        end select
    end if
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function


'функция WinMain
function WinMain _
(byval hInst as HINSTANCE, _ ' хэндл программы
byval hPrevInst as HINSTANCE, _ 'в win32 всегда 0
byval szCmdLine as LPSTR, _  'указатель на командную строку
byval iCmdShow as integer ) as integer  ' состояние окна при первом появлении

dim wc as WNDCLASSEX ' структура параметров окна
dim wMsg as MSG  ' структура сообщений
dim hWnd as HWND ' хэндл окна

 
 ' заполняем структуру wc
wc.cbSize = SIZEOF( WNDCLASSEX )  ' размер структуры WNDCLASSEX
wc.style = CS_HREDRAW or CS_VREDRAW  ' Стиль окна
wc.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
wc.cbClsExtra = NULL  ' резервирование  дополнительных байт за концом структуры
wc.cbWndExtra = NULL
wc.hInstance = hInst  ' хэндл модуля
wc.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1) ' Цвет фона
wc.lpszMenuName = @MenuName ' хэндл меню
wc.lpszClassName = @ClassName ' имя класса окна
wc.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
wc.hIconSm = wc.hIcon 'Хэндл маленькой иконки
wc.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора


' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if

' Создадим окно
hwnd = CreateWindowEx _
(WS_EX_CLIENTEDGE, _ ' дополнительные стили
ClassName, _ ' строка с именем класса окна
AppName, _ ' строка с именем окна
WS_OVERLAPPEDWINDOW, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
300, _ ' ширина окна
200, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hInst, _ ' хэндл модуля
NULL) ' указатель на структуру данных

ShowWindow( hwnd,iCmdShow) ' отобразить наше окно на десктопе
UpdateWindow( hwnd) ' обновить клиентскую область

while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )  'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend
function = wMsg.wParam
end function

файл process.bi

Код:

#define ClassName "Win32FreeBasicProcessClass" ' класс окна
#define AppName "Win32 FreeBasic process Example" ' название программы
#define MenuName "FirstMenu" ' название меню
#define programname "msgbox.exe"
#define IDM_CREATE_PROCESS  32000
#define IDM_TERMINATE  32001
#define IDM_EXIT 32002

файл process.rc

Код:

#include "process.bi"
FirstMenu MENU
{
POPUP "&Process"
{
MENUITEM "&Start Process",IDM_CREATE_PROCESS
MENUITEM "&Terminate Process",IDM_TERMINATE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
}


Последний раз редактировалось: electrik (Вт Май 23, 2017 5:44 pm), всего редактировалось 1 раз(а)

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 37
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 14 - Процесс

Сообщение  electrik в Пн Июн 29, 2009 6:32 pm

Win32 API. Урок 14. Процесс - продолжение

Анализ:

Программа создает основное окно и получает хэндл меню для последующего использования. Затем она ждет, пока пользователь выберет команду в меню. Когда пользователь выберет "Process", мы обрабатываем сообщение WM_INITMENUPOPUP, чтобы изменить пункты меню.

case WM_INITMENUPOPUP
if GetExitCodeProcess _
(processInfo.hProcess, _
@ExitCode) = TRUE then
if ExitCode = STILL_ACTIVE then
EnableMenuItem _
(hMenu, _
IDM_CREATE_PROCESS, _
MF_GRAYED)
EnableMenuItem _
(hMenu, _
IDM_TERMINATE, _
MF_ENABLED)
else
EnableMenuItem _
(hMenu, _
IDM_CREATE_PROCESS, _
MF_ENABLED)
EnableMenuItem _
(hMenu, _
IDM_TERMINATE, _
MF_GRAYED)
end if
else
EnableMenuItem _
(hMenu, _
IDM_CREATE_PROCESS, _
MF_ENABLED)
EnableMenuItem _
(hMenu, _
IDM_TERMINATE, _
MF_GRAYED)
end if

Почему мы обрабатываем это сообщение? Потому что мы хотим изменить пункты в выпадающем меню прежде, чем пользователь их увидит. В нашем примере, если новый процесс еще не стартовал, мы хотим разрешить "start Process" и запретить доступ к пункту "terminate Process". Мы делаем обратное, если программа уже запущена.

Вначале мы проверяем, активен ли еще новый процесс, вызывая функцию GetExitCodeProcess и передавая ей хэндл процесса, полученный при вызове CreateProcess.
Если GetExitCodeProcess возвращает FALSE, это значит, что процесс еще не был запущен, поэтому запрещаем пункт "terminate process". Если GetExitCodeProcess возвращает TRUE, мы знаем, что новый процесс уже стартовал, мы должны проверить, выполняется ли он еще. Поэтому мы сравниваем значение в ExitCode со значением STILL_ACTIVE, если они равны, процесс еще выполняется: мы должны запретить пункт меню "start process", так как мы не хотим, чтобы запустилось несколько совпадающих процессов.

case IDM_CREATE_PROCESS
if processInfo.hProcess <> 0 then
CloseHandle _
(processInfo.hProcess)
processInfo.hProcess = 0
end if
GetStartupInfo (@startInfo)
CreateProcess _
(programname, _
NULL, _
NULL, _
NULL, _
FALSE, _
NORMAL_PRIORITY_CLASS, _
NULL, _
NULL, _
@startInfo, _
@processInfo)
CloseHandle _
(processInfo.hThread)

Когда пользователь выбирает пункт "start Process", мы вначале проверяем, закрыт ли уже параметр hProcess структуры PROCESS_INFORMATION. Если это в первый раз, значение hProcess будет всегда равно нулю, так как мы определяем структуру PROCESS_INFORMATION в начале программы. Если значение параметра hProcess не равно нулю, это означает, что дочерний процесс вышел, но мы не закрыли его хэндл. Поэтому пришло время сделать это.

Мы вызываем функцию GetStartupInfo, чтобы заполнить структуру STARTUPINFO, которую передаем функцию CreateProcess. После этого мы вызываем функцию CreateProcess.
Заметьте, что я не проверил возвращаемое ей значение, потому что это усложнило бы пример. Вам следует проверять это значение. Сразу же после CreateProcess,
мы закрываем хэндл основной ветви, возвращаемой в структуре processInfo. Закрытие хэндла не означает, что мы прерываем ветвь, только то, что мы не хотим использовать хэндл для обращения к ветви из нашей программы. Если мы не закроем его, это вызовет потерю ресурсов.

case IDM_TERMINATE
GetExitCodeProcess _
(processInfo.hProcess, _
@ExitCode)
if ExitCode = STILL_ACTIVE then
Terminateprocess _
(processInfo.hprocess, _
0)
end if
CloseHandle _
(processInfo.hprocess)
processInfo.hprocess = 0

Когда пользователь выберет пункт меню "terminate Process", мы проверяем, активен ли еще новый процесс, вызвав функцию GetExitCodeProcess. Если он еще активен, мы вызываем функцию TerminateProcess, чтобы убить его. Также мы закрываем хэндл дочернего процесса, так как он больше нам не нужен.

[C] Iczelion, пер. Aquila.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 37
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Предыдущая тема Следующая тема Вернуться к началу

- Похожие темы

 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения