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

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

Еще один прием: родительский процесс добавляет в свой блок переменных

окружения новую переменную. Она должна быть "узнаваема" дочерним процессом и содержать значение наследуемого описателя объекта ядра. Далее родительский процесс создает дочерний, тот наследует переменные окружения родительского процесса и, вызвав GetEnvironmentVariable, получает нужный описатель. Такой прием особенно хорош, когда дочерний процесс тоже порождает процессы, — ведь все переменные окружения вновь наследуются.

Именованные объекты

Следующий способ, позволяющий нескольким процессам совместно

использовать одни и те же объекты ядра, связан с именованием этих объектов.

Именование допускают многие (но не все) объекты ядра. Например, следующие функции создают именованные объекты ядра.

HANDLE

CreateMutex(

PSECURITY_ATTRIBUTES

psa,

BOOL

bInitialOwner,

PCTSTR pszName);

 

HANDLЕ

CreateEvent(

PSECURITY_ATTRIBUTES

psa,

BOOL

bManualReset,

BOOL

bInitialState,

PCTSTR pszName);

 

HANDLE

CreateSemaphore(

PSECURITY_ATTRIBUTES

psa,

LONG

lInitialCount,

LONG

lMaximumCount,

PCTSTR pszName);

 

HANDLE

CreateWaitableTimer(

PSECURITY_ATTRIBUTES

psa,

BOOL

bManualReset,

PCTSTR pszName);

 

HANDLE

CreateFileMapping(

HANDLE

hFile,

PSECURITY_ATTRIBUTES

psa,

DWORD

flProtect,

DWORD

dwMaximumSizeHigh,

DWORD

dwMaximumSizeLow,

PCTSTR pszName);

 

HANDLE

CreateJobObject(

PSECURITY_ATTRIBUTES

psa,

PCTSTR pszName);

 

Последний параметр, pszName, у всех этих функций одинаков. Передавая в нем NULL, Вы создаете безымянный (анонимный) объект ядра. В этом случае Вы можете разделять объект между процессами либо через наследование (см. предыдущий раздел), либо с помощью DuplicateHandle (см. следующий раздел). А чтобы разделять объект по имени, Вы должны присвоить ему какое-нибудь имя. Тогда вместо NULL в параметре pszName нужно передать адрес строки с именем, завершаемой нулевым символом. Имя может быть длиной до MAX_PATH знаков (это значение определено как 260). К сожалению, Microsoft ничего не сообщает о правилах именования объектов ядра. Например, создавая объект с именем JeffObj, Вы никак не застрахованы от того, что в системе еще нет объекта ядра с таким именем. И что хуже, все эти объекты делят единое пространство имен.

Теперь, когда Вы научились именовать объекты, рассмотрим, как разделять их между процессами по именам. Допустим, после запуска процесса А вызывается функция:

HANDLE hMutexProcessA = CreateMutex(NULL, FALSE, "JeffMutex");

Этот вызов заставляет систему создать новенький, как с иголочки, объект ядра "мьютекс" и присвоить ему имя JeffMutex. Заметьте, что описатель hMutexProcessA в процессе А не является наследуемым, — он и не должен быть таковым при простом именовании объектов.

Спустя какое-то время некий процесс порождает процесс В. Необязательно, чтобы последний был дочерним от процесса А; он может быть порожден Explorer или любым другим приложением. (В этом, кстати, и состоит преимущество механизма именования объектов перед наследованием.) Когда процесс В приступает к работе, исполняется код:

HANDLE hMutexProcessB = CreateMutex(NULL, FALSE, "JeffMutex");

При этом вызове система сначала проверяет, не существует ли уже объект ядра с таким именем. Если да, то ядро проверяет тип этого объекта. Поскольку мы пытаемся создать мьютекс и его имя тоже JeffMutex, система проверяет права доступа вызывающего

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

Однако, хотя процесс В успешно вызвал CreateMutex, новый объект-мьютекс он не создал. Вместо этого он получил свой описатель существующего объекта-мьютекса. Счетчик объекта, конечно же, увеличился на 1, и теперь этот объект не разрушится, пока его описатели не закроют оба процесса — А и В. Заметьте, что значения описателей объекта в обоих процессах скорее всего разные, но так и должно быть, каждый процесс будет оперировать с данным объектом ядра, используя свой описатель.

Вызывая CreateMutex, процесс В передает ей атрибуты защиты и второй параметр. Так вот, эти параметры игнорируются, если объект с указанным именем уже существует! Приложение может определить, что оно делает: создает новый объект ядра или просто открывает уже существующий, — вызвав GetLastError сразу же после вызова одной из

Create-функций:

 

HANDLE

hMutex

=

CreateMutex(&sa,

FALSE,

"JeffObj");

if

(GetLastError()

==

ERROR_ALREADY_EXISTS)

{

// открыт описатель существующего объекта sa.lpSecurityDescriptor и второй параметр

(FALSE)

 

игнорируются

}

else

{

// создан совершенно новый объект sa.lpSecurityDescriptor и второй параметр (FALSE)

используются

при

создании

объекта

}

 

 

 

Есть и другой способ разделения объектов по именам. Вместо вызова Createфункции процесс может обратиться к одной из следующих Open-функций:

HANDLE

OpenMutex(

DWORD

dwDesiredAccess,

BOOL

bInheritHandle,

PCTSTR pszName);

 

HANDLE

OpenEvent(

DWORD

dwDesiredAccess,

BOOL

bInheritHandle,

PCTSTR pszName);

 

HANDLE

OpenSemaphore(

DWORD

dwDesiredAccess,

BOOL

bInheritHandle,

PCTSTR pszName),

 

HANDLE

OpenWaitableTimer(

DWORD

dwDesiredAccess,

BOOL

bInheritHandle,

PCTSTR pszName);

 

HANDLE

OpenFileMapping(

DWORD

dwDesiredAccess,

BOOL

bInheritHandle,

PCTSTR pszName);

 

HANDLE

OpenJob0bject(

DWORD

dwDesiredAccess,

BOOL

bInheritHandle,

PCTSTR pszName);

 

Заметим: все эти функции имеют один прототип. Последний параметр, pszName, определяет имя объекта ядра. В нем нельзя передать NULL — только адрес строки с нулевым символом в конце Эти функции просматривают единое пространство имен объектов ядра, пытаясь найти совпадение. Если объекта ядра с указанным именем нет,

функции возвращают NULL, a GetLastError — код 2 (ERROR_FILE_NOT_FOUND). Но если объект ядра с заданным именем существует и если его тип идентичен тому, что Вы указали, система проверяет, разрешен ли к данному объекту доступ запрошенного вида (через параметр dwDesiredAccess). Если такой вид доступа разрешен, таблица описателей в вызывающем процессе обновляется, и счетчик числа пользователей объекта возрастает на 1. Если Вы присвоили параметру bInheritHandle значение TRUE, то получите наследуемый описатель.

Главное отличие между вызовом Create- и Open-функций в том, что при отсутствии указанного объекта Create-функция создает его, а Open-функция просто уведомляет об ошибке.

Как было сказано раньше, Microsoft ничего не сообщает о правилах именования объектов ядра. Но представьте себе, что пользователь запускает две программы от разных компаний и каждая программа пытается создать объект с именем "MyObject". Ничего хорошего из этого не выйдет. Чтобы избежать такой ситуации, лучше создавать GUID и использовать его строковое представление как имя объекта.

Именованные объекты часто применяются для того, чтобы не допустить запуска нескольких экземпляров одного приложения. Для этого Вы просто

вызываете одну из Create-функций в своей функции main или WinMain и создаете некий именованный объект. Какой именно — не имеет ни малейшего значения. Сразу после Create-функции Вы должны вызвать GetLastError. Если она вернет ERROR_ALREADY_EXISTS, значит, один экземпляр Вашего приложения уже