Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
3 семестр, WinAPI, MFC.pdf
Скачиваний:
370
Добавлен:
15.06.2014
Размер:
6.17 Mб
Скачать

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

 

int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE, PSTR pszCmdLine,

int

 

 

 

nCmdShow}

 

{

HANDLE

h

=

CreateMutex(NULL,

FALSE,

"{FA531CC1-0497-11d3-A180-

00105A276C3E}");

 

 

 

 

 

if

 

(GetLastError()

==

 

ERROR_ALREADY_EXISTS){

//

экземпляр

этого

приложения

уже

выполняется

return(0),

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

//

запущен

первый

экземпляр

данного

приложения

//

 

перед

 

выходом

 

закрываем

объект

CloseHandle(h),

return(0);

}

Дублирование описателей объектов

Последний механизм совместного использования объектов ядра несколькими процессами требует функции DuplicateHandle:

BOOL

DuplicateHandle(

HANDLE

hSourceProcessHandle,

HANDLE

hSourceHandle,

HANDLE

hTargetProcessHandle,

PHANDLE

phTargetHandle,

DWORD

dwDesiredAccess,

BOOL

bInheritHandle,

DWORD dwOptions);

 

Эта функция берет запись в таблице описателей одного процесса и создает ее копию в таблице другого. DuplicateHandle принимает несколько параметров, но на самом деле весьма незамысловата. Обычно ее применение требует наличия в системе трех разных процессов.

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

завершится с ошибкой, если Вы передадите описатели на объекты ядра любого другого типа.

Второй параметр, hSourceHandle, — описатель объекта ядра любого типа. Однако его значение специфично не для процесса, вызывающего DuplicateHandle, а для того, на который указывает описатель hSourceProcessHandle. Параметр phTargetHandle — это адрес переменной типа

HANDLE, в которой возвращается индекс записи с копией описателя из процессаисточника. Значение возвращаемого описателя специфично для процесса, определяемого параметром hTargetProcessHandle.

Предпоследние два параметра DuplicateHandle позволяют задать маску доступа и флаг наследования, устанавливаемые для данного описателя в процессеприемнике.

И, наконец, параметр dwOptions может быть 0 или любой комбинацией двух

флагов. DUPLICATE_SAME_ACCESS и DUPLICATE_CLOSE_SOURCE.

Первый флаг подсказывает DuplicateHandle: у описателя, получаемого процессомприемником, должна быть та же маска доступа, что и у описателя в процессеисточнике. Этот флаг заставляет DuplicateHandle игнорировать параметр dwDesiredAccess. Второй флаг приводит к закрытию описателя в процессеисточнике. Он позволяет процессам обмениваться объектом ядра как эстафетной палочкой. При этом счетчик объекта не меняется.

Проиллюстрируем работу функции DuplicateHandle на примере. Здесь S — это процесс-источник, имеющий доступ к какому-то объекту ядра, Т — это процессприемник, который получит доступ к тому же объекту ядра, а С — процесс катализатор, вызывающий функцию DuplicateHandle.

Таблица описателей в процессе С (см таблицу 4) содержит два индекса - 1 и 2. Описатель с первым значением идентифицирует объект ядра "процесс S", описатель со вторым значением — объект ядра "процесс Т"

 

Индекс

Указатель

на блокМаска

доступа

Флаги

(DWORD с

 

 

памяти объекта ядра (DWORD

 

битовых

флагов)

 

 

 

 

с набором

битовых

набором

 

 

 

 

 

флагов)

 

 

 

 

1

0xF0000000

 

0x????????

 

0x00000000

 

 

(объект

ядра

 

 

 

 

 

 

процесса S)

 

 

 

 

 

 

2

0xF0000010

(обьект

0x????????

 

0x00000000

 

 

ядра процесса Т)

 

 

 

 

Таблица 4. Таблица описателей в процессе С

 

 

 

Таблица 5 иллюстрирует таблицу описателей в процессе S, содержащую единственную запись со значением описателя, равным 2. Этот описатель может идентифицировать объект ядра любого типа, а не только "процесс".

ИндексУказатель на блокМаска

доступа

Флаги (DWORD с

 

памяти объекта(DWORD

 

набором

 

ядра

 

с набором битовых

битовых флагов)

 

 

 

флагов)

 

 

1

0x00000000

 

(неприменим)

 

(неприменим)

2

0xF0000020

 

0x????????

 

0x00000000

 

(объект

ядра

 

 

 

 

любого типа)

 

 

 

 

Таблица 5. Таблица описателей в процессе S

В таблице 6 показано, что именно содержит таблица описателей в процессе Т перед вызовом процессом С функции DuplicateHandle. Как видно, в ней всего одна запись со значением описателя, равным 2, а запись с индексом 1 пока пуста.

Индекс

Указатель на блок

Маска доступа (DWORD

Флаги (DWORD с

 

памяти объекта ядра

с

набором

битовых

набором

 

 

флагов)

 

битовых флагов

1

0x00000000

(неприменим)

 

(неприменим)

 

 

 

 

 

2

0xF0000030

0x????????

 

0x00000000

 

(объект ядра любого

 

 

 

 

 

типа)

 

 

 

 

Таблица 6. Таблица описателей в процессе Т перед вызовом DuplicateHandle

Если процесс С теперь вызовет DuplicateHandle так:

DuplicateHandle(1, 2, 2, &hObj, 0, TRUE, DUPLICATE_SAME_ACCESS);

то после вызова изменится только таблица описателей в процессе Т (см. таблицу 7).

Индекс

Указатель на

блок

Маска

доступа

Флаги

(DWORD

с

 

памяти объекта ядра

(DWORD

с набором

набором

битовых

 

 

 

битовых флагов)

флагов)

 

 

1

0xF0000020

 

0х????????

 

0x00000001

 

 

 

 

 

 

 

 

2

0xF0000030

 

0х????????

 

0x00000000

 

 

(объект ядра

любого

 

 

 

 

 

 

типа)

 

 

 

 

 

 

Таблица 7. Таблица описателей в процессе Т после вызова DuplicateHandle

Вторая строка таблицы описателей в процессе S скопирована в первую строку таблицы описателей в процессе Т. Функция DuplicateHandle присвоила также переменной bObj процесса С значение 1 — индекс той строки таблицы в процессе Т, в которую занесен новый описатель.

Поскольку функции DuplicateHandle передан флаг DUPLICATE_SAME_ACCESS, маска доступа для этого описателя в процессе Т идентична маске доступа в процессе S. Кроме того, данный флаг заставляет DuplicateHandle проигнорировать параметр dwDesiredAccess. Заметьте также, что система установила битовый флаг наследования, так как в параметре bInheritHandle функции DuplicateHandle мы передали TRUE.

Очевидно, Вы никогда не станете передавать в DuplicateHandle жестко зашитые значения, как это сделано в примере, просто демонстрируя работу функции. В

реальных программах значения описателей хранятся в переменных и, конечно же,

именно эти переменные передаются функциям.

Как и механизм наследования, функция DuplicateHandle тоже обладает одной

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

Представьте, что один процесс имеет доступ к объекту, к которому хочет обратиться другой процесс, или что один процесс хочет предоставить другому доступ к "своему" объекту ядра. Например, если процесс S имеет доступ к объекту ядра и Вам нужно, чтобы к этому объекту мог обращаться процесс Т, используйте DuplicateHandle так:

//

весь

приведенный

ниже

код

исполняется

процессом

S

//

создаем

объект-мьютекс,

доступный

процессу

S

HANDLE hObjProcessS = CreateMutex(NULL, FALSE, NULL);

 

 

//

открываем

описатель

объекта

ядра

"процесс

Т"

HANDLE

hProcessT = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessIdT);

HANDLE

hObjProcessT;

//

неинициализированный

описатель,

//связанный с процессом Т

//предоставляем процессу Т доступ к объекту-мьютексу

DuplicateHandle(GetCurrentProcess(),

hObjProcessS,

hProcessT,

&hObjProcessT, 0, FALSE, DUPLICATE_SAME_ACCESS);

 

//

используем

какую-нибудь форму

межпроцессной связи,

чтобы

передать

// значение описателя из hObjProcessS в процесс Т

 

 

 

//

связь

с

процессом

Т

больше

не

нужна

CloseHandle(hProcessT);

 

 

 

 

 

// если процессу S не нужен объект-мьютекс, он должен закрыть его

CloseHandle(hObjProcessS);

Вызов GetCurrentProcess возвращает псевдоописатель, который всегда идентифицирует вызывающий процесс, в данном случае — процесс S. Как только функция DuplicateHandle возвращает управление, bObjProcessT становится описателем, связанным с процессом Т и идентифицирующим тот же объект, что и описатель bObjProcessS (когда на него ссылается код процесса S). При этом процесс S ни в коем случае не должен исполнять следующий код:

//

процесс

S

никогда

не

должен

пытаться

исполнять

код,

//

 

закрывающий

 

продублированный

описатель

CloseHandle(hObjProcessT);

 

 

 

 

 

Если процесс S выполнит этот код, вызов может дать (а может и не дать) ошибку. Он будет успешен, если у процесса S случайно окажется описатель с тем же значением, что и в hObjProcessT. При этом процесс S закроет неизвестно какой объект, и что будет потом

— остается только гадать.

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

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

int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE, PSTR pszCmdLine, int nCmdShow) {

//создаем объект "проекция файла",

//его описатель разрешает доступ как для чтения, так и для записи

HANDLE hFileMapRW = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,

PAGE_READWRITE, 0, 10240, NULL);

//создаем другой описатель на тот же обьект;

//этот описатель разрешает дocтyп только для чтения