Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

//экземпляр этого приложения уже выполняется return(0),

}

//запущен первый экземпляр данного приложения

//перед выходом закрываем объект

CloseHandle(h),

return(0);

}

Пространства имен Terminal Server

Terminal Server несколько меняет описанный выше сценарий На машине с Terminal Server существует множество пространств имен для объектов ядра. Объекты, которые должны быть доступны всем клиентам, используют одно глобальное пространство имен. (Такие объекты, как правило, связаны с сервисами, предоставляемыми клиентским программам.) В каждом клиентском сеансе формируется свое пространство имен, чтобы исключить конфликты между несколькими сеансами, в которых запускается одно и то же приложение Ни из какого сеанса нельзя получить доступ к объектам другого сеанса, даже если у их объектов идентичные имена.

Именованные объекты ядра, относящиеся к какому-либо сервису, всегда находятся в глобальном пространстве имен, а аналогичный объект, связанный с приложением, Terminal Server по умолчанию помещает в пространство имен киентского сеанca. Однако и его можно перевести в глобальное пространство имен, поставив перед именем объекта префикс "Global\", как в примере ниже.

HANDLE h = CreateEvenL(NULL, FALSE, FALSE, "Global\\MyName");

Если Вы хотите явно указать, что объект ядра должен находиться в пространстве имен клиентского сеанса, используйте префикс "Local\":

HANDLE h = CreateEvent(NULL, FALSE, FALSE, "Local\\MyName");

Microsoft рассматривает префиксы Global и Local как зарезервированные ключевые слова, которые не должны встречаться в самих именах объектов. К числу таких слов Microsoft относит и Session, хотя на сегодняшний день оно не связано ни с какой функциональностью. Также обратите внимание на две вещи, все эти ключевые слова чувствительны к регистру букв и игнорируются, если компьютер работает без Terminal

Server.

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

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

BOOL DuplicateHandle(

HANDLE hSourceProcessHandle,

HANDLE hSourceHandle,

HANDLE hTargetProcessHandle,

PHANDLE phTargetHandle,

DWORD dwDesiredAccess,

BOOL bInheritHandle,

DWORD dwOptions);

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

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

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

Предпоследние два параметра DuplicateHandle позволяют задать маску доступа и флаг наследования, устанавливаемые для данного описателя в процессе-приемнике. И, наконец, параметр dwOptions может быть 0 или любой комбинацией двух флагов. DUPLICATE_SAME_ACCESS и DUPLICATE_CLOSE_SOURCE

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

Второй флаг приводит к закрытию описателя в процессе-источнике. Он позволяет процессам обмениваться объектом ядра как эстафетной палочкой При этом счетчик объекта не меняется.

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

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

Индекс

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

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

Флаги (DWORD с

 

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

с набором битовых флагов)

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

 

 

 

 

1

0xF0000000

0x????????

0x00000000

 

(объект ядра процесса S)

 

 

 

 

 

 

2

0xF0000010 (обьект ядра

0x????????

0x00000000

 

процесса Т)

 

 

 

 

 

 

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

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

Индекс

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

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

Флаги (DWORD с

 

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

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

набором

 

 

флагов)

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

1

0x00000000

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

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

 

 

 

 

2

0xF0000020

0x????????

0x00000000

 

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

 

 

 

типа)

 

 

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

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

Индекс

Указатель на блок памяти

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

Флаги (DWORD с набором

 

объекта ядра

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

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

 

 

 

 

1

0x00000000

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

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

 

 

 

 

2

0xF0000030

0x????????

0x00000000

 

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

 

 

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

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

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

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

Индекс

Указатель на блок памяти

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

Флаги (DWORD с набором

 

объекта ядра

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

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

 

 

 

 

1

0xF0000020

0х????????

0x00000001

 

 

 

 

2

0xF0000030

0х????????

0x00000000

 

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

 

 

 

 

 

 

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

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

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

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

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

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

Я рассказал Вам о функции DuplicateHandle в самом общем виде. Надеюсь, Вы увидели, насколько она гибка. Но эта функция редко используется в ситуациях, требующих участия трех разных процессов Обычно ее вызывают применительно к двум процессам. Представьте, что один процесс имеет доступ к объекту, к которому хочет обратиться другой процесс, или что один процесс хочст предоставить другому доступ к "своему" объекту ядра. Например, если процесс 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);

//используем какую-нибудь форму межпроцессной связи, чтобы передать

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

//связь с процессом Т больше не нужна

CloseHandle(hProcessT),

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

CloseHandle(hObjProcessS);

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