Объект ядра ос windows может наследовать только процесс потомок

Работа по теме: Основы многопоточного и параллельного программирования by Карепова Е.Д. (z-lib.org). Глава: 3.1. Объекты ядра. Предмет: Параллельное программирование. ВУЗ: СФУ.

Гл а в а 3 УПРАВЛЕНИЕ ПОТОКАМИ

СПОМОЩЬЮ ФУНКЦИЙ WinAPI

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

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

3.1. Объекты ядра

Основные понятия

Операционная система Windows позволяет оперировать несколькими типами объектов ядра, такими как процессы, потоки, проекции файлов, события, семафоры, мьютексы, каналы и т. д.

Каждый объект ядра – это блок памяти, выделенный ядром и доступный только ядру. Этот блок представляет собой структуру данных, в элементах которой содержится информация об объекте. Некоторые элементы (имя объекта, дескриптор защиты, счетчик числа пользователей и др.) присутствуют во всех объектах, но большая их часть специфична для объектов конкретного типа. Например, у объекта «процесс» есть идентификатор процесса, базовый приоритет и код завершения, а у объекта «файл» – смещение в байтах, режим разделения и режим открытия.

Для создания объекта ядра необходимо в программе вызвать функцию Win32 API. Например, CreateFileMapping() заставляет систему сформировать объект «проекция файла» (file-mapping object).

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

Каким же образом приложение может оперировать объектами ядра? Win32 API предусматривает набор функций, обрабатывающих объекты ядра

95

Г л а в а 3. Управление потоками с помощью функций WinAPI

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

Функция создания объекта ядра всегда возвращает описатель, идентифицирующий созданный объект. Описатель следует рассматривать как «непрозрачное» 32-битное значение (в программах Си это переменная типа HANDLE), которое может быть использовано любым потоком процесса. Приложение передает описатель объекта ядра Win32-функциям, сообщая системе, какой объект ядра его интересует.

Для бóльшей надежности ОС значения описателей зависят от кон-

кретного процесса. Даже если с помощью какого-либо механизма межпроцессной связи описатель объекта, созданного в процессе «1», передать потоку процесса «2», то любой вызов из процесса «2» со значением полученного описателя даст ошибку.

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

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

Таблица описателей

При инициализации процесса система создает в нем таблицу описателей, используемую только для объектов ядра [8, 34]. Сведения о структуре этой таблицы и управлении ею не задокументированы. На рис. 3.1 представлена примерная структура таблицы описателей.

Индекс

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

Маска

Флаги

объекта ядра

доступа

Рис. 3.1. Примерная структура таблицы описателей объектов ядра

При инициализации процесса таблица описателей пуста. При вызове одним из потоков процесса Win32 API функции, создающей объект ядра (например, CreateFileMapping()), ядро выделяет для этого объекта блок памяти и инициализирует его; далее ядро просматривает таблицу описате-

96

3.1. Объекты ядра

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

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

Независимо от того, как именно создан объект ядра, по окончании работы с ним его нужно закрыть вызовом Win32 API функции

CloseHandle():

BOOL CloseHandle(HANDLE hobj);

Эта функция сначала проверяет таблицу описателей, принадлежащую вызывающему процессу, чтобы убедиться, идентифицирует ли переданный ей описатель объект, к которому этот процесс действительно имеет доступ. Если переданный описатель неверен, функция возвращает FALSE, a функция GetLastError() – код ERROR_INVALID_HANDLE. Если же индекс достоверен, система получает адрес структуры данных объекта ядра и уменьшает в этой структуре счетчик числа пользователей. Как только счетчик обнулится, ядро удалит объект из памяти. Перед самым возвратом управления CloseHandle() удаляет соответствующую запись из таблицы описателей, после этого данный описатель недействителен в вызвавшем закрытие процессе, использовать его нельзя. Запись из таблицы описателей процесса удаляется независимо от того, разрушен объект ядра или нет. После вызова CloseHandle() процесс больше не получит доступ к этому объекту ядра.

Совместное использование объектов ядра несколькими процессами

Существует необходимость в совместном использовании объектов ядра потоками, исполняемыми в разных процессах. Например:

объекты «проекции файлов» (file-mapping object) позволяют двум процессам, исполняемым на одном компьютере, совместно использовать одни и те же блоки данных;

«почтовые ящики» (mailslots) и именованные каналы (named pipes) дают возможность обмениваться данными процессам, исполняемым на разных компьютерах в Сети;

97

Гл а в а 3. Управление потоками с помощью функций WinAPI

мьютексы (mutexes), семафоры (semaphores) и события (events)

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

Но описатели объектов ядра имеют смысл только в конкретном процессе, поэтому разделение объектов ядра между несколькими процессами

вWin32 – задача весьма непростая.

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

Существует три способа разделения объектов ядра.

1. Наследование. Наследование применимо, только если процессы связаны «родственными» отношениями (родительский – дочерний). Например, родительскому процессу доступны один или несколько описателей объектов ядра, и он решает, породив дочерний процесс, передать ему по наследству доступ к своим объектам ядра.

При вызове функции создания объекта в качестве одного из ее пара-

метров передается структура LPSECURITY_ATTRIBUTES, которая определяет среди прочего и наследуемость объекта. Если при создании дочернего процесса в параметрах функции CreateProcess() флаг наследования объектов blnheritHandles выставлен в значение TRUE, то этот дочерний процесс может наследовать объекты. ОС создает дочерний процесс, но не дает ему немедленно начать свою работу. Сформировав в нем, как обычно, новую (пустую) таблицу описателей, ОС считывает таблицу родительского процесса и копирует ее записи в таблицу дочернего, причем в те же позиции. Последний факт чрезвычайно важен, так как означает, что описатели будут идентичны в обоих процессах (родительском и дочернем).

Кроме того, ОС увеличивает значения счетчиков соответствующих объектов ядра, поскольку эти объекты теперь используются обоими процессами.

Сразу после возврата управления функцией CreateProcess() родительский процесс может закрыть свой описатель объекта, и это никак не отразится на способности дочернего процесса манипулировать этим объектом. Чтобы уничтожить какой-то объект ядра, его описатель должны закрыть (вызовом CloseHandle()) оба процесса – родительский и дочерний.

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

98

3.1. Объекты ядра

процесс создаст после этого новые объекты ядра с наследуемыми описателями, то эти описатели дочернему процессу уже недоступны.

2. Именованные объекты. Второй способ разделения объектов ядра процессами связан с возможностью присвоения имени объекту ядра. Именовать можно многие, но не все виды объектов. Именуются, к примеру, мьютексы, события, семафоры и проекции файлов.

В качестве имени объекта передается строка, завершающаяся нулевым символом, длиной не более 260 символов и не содержащая символов «/». Все именованные объекты разделяют общее для системы пространство имен, поэтомунельзясоздатьдваразнотипныхобъектасодинаковымименем.

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

Надо помнить, что вызов процессом Б функции CreateObject() не привел к созданию нового объекта. Процесс Б получил свой описатель, который идентифицирует уже существующий объект. Счетчик объекта, конечно же, увеличился на единицу, и теперь он не разрушится, пока его описатели не закроют оба процесса А и Б. Скорее всего, значения описателей этого объекта в обоих процессах разные, но так и должно быть: каждый процесс будет оперировать с данным объектом ядра, используя свой описатель.

Параметры защиты созданного таким образом объекта будут соответствовать параметрам, переданным процессом А.

Отметим, что если тип объекта, создаваемого процессом Б, не совпадает с уже созданным объектом с тем же именем, то система сгенерирует ошибку, и второй объект не будет создан.

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

BOOL OpenHandle(DWORD dwDesiredAccess,

BOOL blnheritHandle,

LPCTSTR IpszName);

Функция OpenHandle() просматривает единое пространство имен объектов ядра, пытаясь найти совпадение. Если объекта ядра с указанным в параметрах функции именем нет или он другого типа, Open-функции воз-

99

Г л а в а 3. Управление потоками с помощью функций WinAPI

вращают NULL. Но если объект ядра с заданным именем существует и если его тип идентичен тому, что указан, система проверяет, разрешен ли к данному объекту доступ запрошенного вида. Если доступ разрешен, таблица описателей в вызывающем процессе обновляется, и счетчик числа пользователей объекта возрастает на единицу. Опишем параметры функции.

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

Параметр BOOL blnheritHandle обозначает свойство наследования. Если значение этого параметра TRUE, то будет получен наследуемый описатель.

Параметр LPCTSTR IpszName определяет имя объекта ядра. Он всегда должен содержать адрес строки с нулевым символом в конце (передавать NULL нельзя).

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

3. Дублирование описателей объектов. Последний механизм совме-

стного использования объектов ядра несколькими процессами – создание дубликата описателя объекта:

BOOL DuplicateHandle (HANDLE hSourceProcessHandle, HANDLE hSourceHandle,

HANDLE hTargetProcessHandle, LPHANDLE IpTargetHandle, DWORD dwDesiredAccess,

BOOL PInheritHandle, DWORD dwOptions);

Эта функция создает копию записи из таблицы описателей процесса

hSourceProcessHandle в таблице процесса hTargetProcessHandle, не обязательно вызвавшего эту функцию. Опишем параметры функции.

Параметр HANDLE hSourceProcessHandle задает специфичный для вызывающего процесса описатель объекта ядра типа «процесс», который является владельцем запрашиваемого объекта.

Параметр HANDLE hSourceHandle задает описатель запрашиваемого объекта ядра любого типа, специфичного для процесса, на который указы-

вает параметр hSourceProcessHandle.

Параметр HANDLE hTargetProcessHandle задает специфичный для вызывающего процесса описатель объекта ядра типа «процесс», в таблицу которого копируется запись (процесс-приемник).

100

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

Передача прав пользования описателями может происходить несколькими путями.

  • Наследованием
  • Именованием
  • Дублированием

В этом случае при создании объекта ядра необходимо создать структуру SECURITY_ATTRIBUTES и задать параметр bInheritHandle=TRUE; После чего передать в параметр SECURITY_ATTRIBUTES функции создающей объект вышеупомянутую структуру защиты.

Однако это еще не все. Наследовать описатель может только дочерний процесс, созданный внутри текущего. При создании такового функцией CreateProcess() в параметр BOOL bInheritHandles присваивается значение TRUE, что говорит системе, скопировать описатели из таблицы родительского в таблицу дочернего процесса. Причем переписывание происходит на те же места, т.е. значение HANDLE остается неизменным.

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

Именование поддерживается не всеми типами объектов ядра. Список поддерживающих: Mutex, Event, Semaphore, WaitableTimer, FileMapping, JobObject.

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

При создании объекта в функции, поддерживающей именование объекта, присутствует параметр PCSTR pszName, который обычно равен NULL, поэтому все объекты получаются безымянными.

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

При желании обратиться к такому объекту из другого процесса, зная имя, можно создать CreateMutex() с тем же именем. В случае, если объект уже существует, система проверит права доступа и при разрешении вернет вам HANDLE, указывающий на готовый объект, либо NULL, если объект процессу недоступен. Можно вместо Create функции с таким же успехом использовать Open.

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

Я только скажу, что, просмотрев прототип требуемой функции DuplicateHandle, вы быстро поймете, как работает эта простая и вместе с тем гибкая функция.

Если будет необходимо, я отвечу на вопросы на форуме.

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

Как я говорил ранее, при всех трех методах совместного использования в процессах объектов ядра счетчик пользователей в самом объекте должен быть сведен к 0.

Асинхронные вызовы процедур

Для реализации асинхронного ввода-вывода в операционной системе предусмотрен специальный механизм, основанный на так называемых асинхронных вызовах процедур (Аsynchronous Procedure Call, APC). Это один из базовых механизмов, необходимый для нормального функционирования операционной системы.

Практика показала, что такой механизм был бы эффективен и для реализации самих приложений. Более того, для реализации асинхронного ввода-вывода с поддержкой функции завершения система уже обязана была предоставить этот механизм. Для реализации этого механизма операционная система ведет списки процедур, которые она должна вызывать в контексте данного потока, с тем ограничением, что прерывать работу занятого потока в произвольный момент времени система не должна. Поэтому для обслуживания накопившихся в очереди процедур необходимо перевести поток в специальное состояние ожидания оповещения (alertable waiting) — для этого Win32 API предусматривает специальный набор функций: например, SleepEx, WaitForSingleObjectEx и др.

Здесь и во многих примерах далее, чтобы не загромождать код примера, опущена проверка ошибок:

VOID CALLBACK ApcProc( ULONG_PTR dwData )
{
  /* ... */
}
int main( void )
{
  QueueUserAPC( ApcProc, GetCurrentThread(), 0 ); 
  /* ... */
  SleepEx( 1000, TRUE );
  return 0;
}

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

Процессы, потоки и объекты ядра

Обсуждение реализации мультипрограммирования в операционной системе неизбежно приводит к обсуждению вопросов безопасности.

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

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

Объекты ядра

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

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

Все созданные описатели объектов ядра должны удаляться с помощью функции

BOOL CloseHandle( HANDLE hKernelObject )

которая уменьшает счетчик использования объекта и уничтожает его, когда на объект никто больше не ссылается.

Доступ к защищаемым объектам в Windows задается так называемыми дескрипторами безопасности (Security Descriptor). Дескриптор содержит информацию о владельце объекта и первичной группе пользователей и два списка управления доступом (ACL, Access Control List): один список задает разрешения доступа, другой — необходимость аудита при доступе к объекту. Список содержит записи, указывающие права выполнения действий, и запреты, назначенные конкретным пользователям и группам. При доступе к защищаемым объектам для начала проверяются запреты — если для данного пользователя и группы имеется запрет доступа, то дальнейшая проверка не выполняется и попытка доступа отклоняется. Если запретов нет, то проверяются права доступа — при отсутствии разрешений доступ отклоняется. Запрет обладает более высоким «приоритетом», чем наличие разрешений — это позволяет разрешить доступ, к примеру, целой группе пользователей и выборочно запретить некоторым ее членам.

Объект, осуществляющий доступ (выполняющийся поток), обладает так называемым маркером доступа (access token). Маркер идентифицирует пользователя, от имени которого предпринимается попытка доступа, а также его привилегии и умолчания (например, стандартный ACL объектов, создаваемых этим пользователем). В Windows маркерами доступа обладают как потоки, так и процессы. С процессом связан так называемый первичный маркер доступа, который используется при создании потоков, а вот в дальнейшем поток может работать от имени какого-либо иного пользователя, используя собственный маркер воплощения (impersonation).

Процессы и потоки в Windows являются с одной стороны «представителями» пользователя, выступающими от его имени, а с другой стороны — защищаемыми объектами, при доступе к которым выполняется проверка прав, то есть они обладают одновременно и маркерами доступа, и дескрипторами безопасности.

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

Для создания большинства объектов ядра используются функции, начинающиеся на слово «Create» и возвращающие описатель созданного объекта, например функции CreateFile, CreateProcess, CreateEvent и т.д. Многие объекты при их создании могут получить собственное имя или остаться неименованными.

Любой процесс или поток может ссылаться на объекты ядра, созданные другим процессом или потоком. Для этого предусмотрено три механизма:

  • Объекты могут быть унаследованы дочерним процессом при его создании. В этом случае объекты ядра должны быть «наследуемыми», и родительский процесс должен принять меры к тому, чтобы потомок мог узнать их описатели. Возможность передавать описатель потомкам по наследованию явно указывается в большинстве функций, так или иначе создающих объекты ядра (обычно такие функции содержат аргумент » BOOL bInheritHandle «, который указывает возможность наследования).
  • Объект может иметь собственное уникальное имя — тогда можно получить описатель этого объекта по его имени. Для разных типов объектов Win32 API предоставляет набор функций, начинающийся на Open... например, OpenMutex, OpenEvent и т.д.
  • Процесс-владелец объекта может передать его описатель любому другому процессу. Для этого процесс-владелец объекта должен получить специальный описатель объекта для «экспорта» в указанный процесс. В Win32 API для этого предназначена функция DuplicateHandle, создающая для объекта, заданного описателем в контексте данного процесса, новый описатель, корректный в контексте нового процесса:
    BOOL DuplicateHandle(
      HANDLE hFromProcess, HANDLE hSourceHandle,
      HANDLE hToProcess, LPHANDLE lpResultHandle,
      DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions
    );

Существенно проследить, чтобы все создаваемые описатели закрывались вызовом функции CloseHandle, включая описатели, созданные функцией DuplicateHandle. Хорошая практика при разработке приложений — проводить мониторинг выделяемых описателей и количества объектов в процессе (например, с помощью таких стандартных средств как менеджер задач или оснастка «производительность» панели управления).

1. Что такое объект ядра?

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

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

c)Скопировать объект использование подсчета подсчета:

Владелец объекта ядра является ядром операционной системы, а не процессом. Следовательно, жизненный цикл объекта ядра может быть длиннее, чем процесс его создания. Операционная система воспринимается членами подсчета использования в объекте ядра, чтобы воспринимать, сколько процессов в настоящее время использует этот объект ядра. Когда этот счетчик объекта ядра0Когда операционная система разрушена только операционной системой.

d)Безопасность объекта ядра:

1) Все функции, используемые для создания объектов ядра, почти указывают на одинSECURITY_ATTRIBUTES Используется для защиты объекта ядра, если этот параметр передается вNULLЗатем используйте атрибут безопасности по умолчанию текущего токена безопасности процесса.SECURITY_ATTRIBUTESсерединаlpSecurityDescriptorУчаствовать в одномSecurity_descriptor Security Descriptor Structure, которая используется для установки атрибутов безопасности объекта ядра.

2) Для объектов, которые необходимо получить с помощью логотипа доступа к безопасности, вам следует использовать логотип доступа для доступа к нему вместоALL_ACCESS

3) Методы различения объектов ядра и обычных объектов:

Почти функция создания объекта ядра имеет параметры указанной информации атрибута безопасности.

2. Таблица обработки объектов обработки

a) Каждый процесс поддерживает таблицу обработки объектов ядра. Каждый элемент в таблице представляет собой структуру данных. Члены этой структуры данных содержат реальный адрес объекта ядра и разрешения доступа.Руководство, удерживаемое кодом пользователя (эта ручка используется для всех пользовательских потоков в этом процессе, но он может реализовать объекты ядра по перекрестному борьбе через некоторый механизм), на самом деле является значением индекса таблицы объектов ядра этой ручки объекта ядра. Анкет Следовательно, когда объект ядра выключается, переменная не очищена, а таблица с помощью объекта ядра переоценивает объект ядра в этой позиции индекса.BUGСущность В других процессах доступ к объекту ядра этой ручки, на самом деле, значение индекса таблицы направления объектов ядра, соответствующая другим процессам, также будет вызватьBUG

b)  При вызове функции для создания объекта ядра, если вызов сбой, возвращаемое значение может бытьЛибоINVALID_HANDLE_VALUE-1)。следовательноВозвращающаяся стоимость единого суждения0ЛибоINVALID_HANDLE_VALUEэто не правильно.

c) Независимо от того, как создан объект ядра, вы должны позвонитьCloseHandleСистеме показано, что этот объект был завершен.CloseHandleПроверьте таблицу обработки ядра процесса внутренне, чтобы проверить, является ли грандиозное действительное. Если он действителен, адрес структуры данных данных объекта ядра индексируется и минус счетчик в структуре. Если счетчик используется0Ядро операционной системы уничтожило объект ядра.CloseHandle Перед возвращением функция также очистит соответствующие элементы записи в таблице ручки объекта процесса ядра. передачаCloseHandleПосле этого, даже если объект ядра все еще существует, этот процесс рассматривает ручку как недействительную. Следовательно, этот процесс не может быть использован.использоватьCloseHandleПосле этого ручка должна быть установлена ​​наNULL

D) Когда процесс выходит, все объекты будут выпущены. Система гарантирует, что после окончания процесса ничего не останется. (Объект ядра определяет, следует ли перерабатывать ресурсы в соответствии с счетчиком в объекте ядра. Если счетчик равен 0Затем отпустите объект, иначе он просто уменьшит счетчик до операции

3. Объект ядра Cross -Borderding

а) Хандикап для ручки объекта

я. Только когда отношения между отцом и сыном существуют между процессами, может наследовать объект. И только ручка может наследовать, сам объект ядра не может наследовать.

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

Iii.

1. Инициализируйте одинSECURITY_ATTRIBUTES Структура, вкладывая членовbInheritHandle Назначен какTRUEЧтобы показать, что объект ядра может наследовать и передать функцию создания основного объекта. ИспользоватьSetHandleInformationУстановите логотип наследования для рукоятки объекта ядра. Есть один, соответствующий одномуGetHandleInformation, Логотип наследства для возврата ручки.

2. ПозвонитеCreateProcessСоздайте дочерний процесс и будетCreateProcessсерединаbInheritHandlesПараметр установлен наTRUEСущность Это показывает, что дочерний процесс будет наследовать направление объекта ядра, которая может быть унаследована в родительском процессе.

3. Заполните приведенные выше два шага, операционная система унаследован ручка, которая может быть унаследована в родительском процессе в процесс ребенка, а значение ручки и родительский процесс сохраняются. но,Подпроцесс не знает, что он унаследовал ручку родительского процесса.Следовательно, если дочерний процесс хочет получить ручку унаследована от родительского процесса, она должна быть получена с помощью некоторого механизма.

а) Передайте параметры командной строки в процесс ребенка

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

c) Унаследованное значение направления объекта ядра вводится из родительского процесса с помощью других технологий коммуникации процесса.

Iv. Логотип изменения ручки

1SetHandleInformationВы можете изменить признаки наследования направления объекта ядра

а) Первый параметр идентифицирует эффективную ручку объекта ядра

б) Второй параметрdwMaskРасскажите функции, какой или какой логотип хочет изменить

в) третий параметрdwFlagsУкажите желание установить логотип, почему.

2. черезGetHandleInformation Может получить текущий знак ручки объекта ядра

Исходный код процесса отца:

 1 #define _CRT_SECURE_NO_DEPRECATE
 2 #include <iostream>
 3 #include <windows.h>
 4 using namespace std;
 5 
 6 DWORD _stdcall Thread(LPVOID param)
 7 {
 8     while (1)
 9     {
10         cout << "loop" << endl;
11         Sleep(500);
12     }
13     return 0;
14 }
15 
16 int AnsiToUnicode (char *str_ansi, wchar_t *str_wide, unsigned int chcount)
17 {
18     return MultiByteToWideChar(CP_ACP, 0, str_ansi, -1, str_wide, chcount);
19 }
20 
21 int main(void)
22 {
23     /*
24     SECURITY_ATTRIBUTES sa;
25     sa.nLength = sizeof(sa);
26     sa.bInheritHandle = TRUE;
27     sa.lpSecurityDescriptor = NULL;*/
28 
29     HANDLE hThread = CreateThread (NULL, 0, Thread, NULL, 0, NULL);
30     //CloseHandle(hThread);        //Если ручка закрыта, дочерний процесс не сможет получить доступ к объекту ядра родительского процесса
31 
32     SetHandleInformation (hThread, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
33 
34     Sleep(1000);
35 
36 
37     STARTUPINFO info = {0};
38     PROCESS_INFORMATION pi = {0};
39     char comline[256];
40     TCHAR p[256];
41 
42     sprintf(comline, "%d", hThread);
43     AnsiToUnicode(comline, p, sizeof(p));
44 
45     SetEnvironmentVariable(L"hThread", p);            //Чтобы добавить переменные окружающей среды перед созданием дочернего процесса, в противном случае детский процесс не будет наследовать переменные среды родительского процесса
46     
47     CreateProcess (L"..\debug\child.exe", p, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &pi);
48     CloseHandle(pi.hProcess);
49     CloseHandle(pi.hThread);
50 
51 
52     cin.get();
53     return 0;
54 }

Исходный код:

 1 #include <iostream>
 2 #include <windows.h>
 3 using namespace std;
 4 
 5 int UnicodeToAnsi(wchar_t *str_wide, char *str_ansi, unsigned int cbsize)
 6 {
 7     return WideCharToMultiByte(CP_ACP, 0, str_wide, -1, str_ansi, cbsize, NULL, NULL);
 8 }
 9 
10 int main (int argv, char ** argc)
11 {
12 
13 
14     //  Командная строка получает ручку наследования.
15     /*
16     HANDLE hThread = (HANDLE)atol(argc[0]);  
17     cout << "get kernal handle: " << argc[0] << endl;
18     TerminateThread(hThread, 0);
19     cout << "kill my parent process thread " << endl;
20     */
21 
22     
23     HANDLE hThread;
24     TCHAR buf[256] = {0};
25     char str[256] = {0};
26     GetEnvironmentVariable(L"hThread", buf, sizeof(buf));
27     UnicodeToAnsi(buf, str, sizeof(str));
28     cout << str << endl;
29     hThread = (HANDLE) atoi(str);
30     TerminateThread(hThread, 0);
31     
32 
33     cin.get();
34     return 0;
35 }

б) Называть объект

Я. Большинство объектов ядра могут быть названы, и общий объект может быть разделен путем названия объекта ядра. Как правило, функция создания объекта ядра, которую можно назвать, находится в последнем параметреpszNameСущность Это может назвать объект ядра через него. Если этот параметр передается вNULLСоздание анонимного объекта ядра. Когда обмен реализован для объектов, может ли он наследовать, не является необходимым условием.

ii. Все объекты ядра имеют одинаковое пространство имени, даже если их типы не одинаковы.

Iii. Когда процесс вызываетCreate*  При создании функции объекта ядра, если он уже существуетpszNameИмя объекта ядра указывает, что параметр будет игнорировать другие параметры. Если нетpszNameИмя объекта ядра указывает параметр, и другие параметры будут переданы.

Iv. За исключениемCreate* Все еще может использоватьOpen* Откройте общий объект ядра.Open*изpszName Параметры не могут быть переданы вNULLCreate*а такжеOpen*Основное различие между функцией: если объект не существует,Create*Создаст его,Open*Неудача закончится с отказом вызова.

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

vi. Терминальная служба пространство

1. На компьютере, который работает в службе терминала, существует множество пространств для именования для объектов ядра, одним из которых является глобальное пространство имен. Обозначения ядра, к которым можно получить доступ ко всем клиентам, помещаются в это пространство именования, и это пространство именования в основном используется в системе. И на каждом клиентском сеансе есть свое место именования. Это заставляет одну и ту же программу избегать взаимного вмешательства в разные картины. Даже если имя объекта одинаково.

2. Объект ядра именования услуг всегда должен быть расположен в глобальном пространстве именования. По умолчанию объект ядра именования приложения находится в пространстве имени текущего сеанса. Если вы хотите позволить ему присоединиться к глобальному пространству именования, вы можете добавить «Global » префикс в его имени. Поместите объект ядра именования в текущий сеанс. ». Session <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<sСессияid>«Ограничьте объекты ядра именования могут только работать. Обратите внимание, что эти префиксы различаются.

Терминал Служба Имя ПРИМЕР ПРИМЕР:

 1 #include <iostream>
 2 #include <windows.h>
 3 using namespace std;
 4 
 5 int main (void)
 6 {
 7     //  Создайте глобальный доступный Mutex
 8     HANDLE hMutex;
 9     hMutex = CreateMutex (NULL, FALSE, L"Global\Mutex");
10     if (GetLastError() == ERROR_ALREADY_EXISTS)
11     {
12         cout << "this is old mutex" << endl;
13     }
14     else if (NULL == hMutex)
15     {
16         
17         cout << "create object error" << endl;
18     }
19     else
20         cout << "this is new mutex" << endl;
21     
22     cin.get();
23     return 0;
24 }

vii. Частное пространство имени

1. Чтобы предотвратить имя общего объекта, можно использовать частное пространство имен. То есть пользовательский, наиболее украшенный названием ядра именования, защищен. Используя частное пространство именования, серверный процесс, ответственный за создание объекта ядра, будет определять дескриптор границ для защиты частного пространства собственности.

2. Шаги по созданию частного пространства именования:

а) Создать граничный дескриптор CreateBoundaryDescriptor

б) создатьSIDЕдинственное число пользователя, групповой и компьютерной учетной записи) CreateWellSIDSID

в)SIDИсправлено с дескриптором границы.AddSIDToBoundaryDescriptor

г) Инициализировать дескриптор безопасности//Использование книгиConvertStringSecurityDescriptorToSecurityDescriptor

e) Создать частное именованное пространство CreatePrivingNamespace

3. Примечание, CreatePrivateNamespaceа также OpenPrivateNamespace возвращатьсяHANDLE Не ручка объекта ядра, его следует использоватьClosePrivateNamspaceВыходи из псевдо -ручки, которую они возвращаются.

4. Если частное пространство именования не хочет упоминаться другими процессами после закрытия процесса, вам следует позвонитьClosePrivateNamespaceИ пройти во втором параметреPRIVATE_NAMESPACE_FLAG_DESTROY

5. Граница будет прекращена или вызвана в процессеDeleteBoundaryDescriptorЗакройте его по делу.

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

7. Имя, которое мы указали для частного названного пространства, является псевдонимом, видным только в процессе. Другие процессы(Даже тот же процесс)Может открыть одно и то же частное пространство и указать для него другие псевдонимы.

Пример индивидуального имени пространства:

 1 #include <windows.h>
 2 #include <sddl.h>
 3 #include <strsafe.h>
 4 #include <iostream>
 5 using namespace std;
 6 
 7 
 8 
 9 void CheckInstances()
10 {
11 
12     BOOL bIsNamespaceOpen = FALSE;
13 
14     //Создать дескриптор границ
15     HANDLE hBoundary = CreateBoundaryDescriptor(L"my_private_name", 0);
16 
17 
18     //Создайте SID
19     BYTE localAdminSID[SECURITY_MAX_SID_SIZE];
20     DWORD cbSID = sizeof(localAdminSID);
21     if ( !CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, localAdminSID, &cbSID) )
22     {
23         cout << "CreateWellKnownSID error : " << GetLastError() << endl;
24         return ;
25     }
26 
27     //Связывать SID с дескриптором границ
28     if ( !AddSIDToBoundaryDescriptor (&hBoundary, localAdminSID) )
29     {
30         cout << "AddSIDToBoundaryDescriptor error : " << GetLastError() << endl; 
31     }
32 
33     //Преобразовать строку дескриптора безопасности в дескриптор безопасности
34     SECURITY_ATTRIBUTES sa;
35     sa.nLength = sizeof(sa);
36     sa.bInheritHandle = FALSE;
37     if ( !ConvertStringSecurityDescriptorToSecurityDescriptor (L"D:(A;;GA;;;BA)", SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL) )
38     {
39         cout << "ConvertStringSecurityDescriptorToSecurityDescriptor error : " << GetLastError() << endl;
40         return ;
41     }
42 
43     //Создайте запатентованное пространство имени
44     TCHAR szNamespace[256] = L"my_private_name";
45     HANDLE hNamespace = CreatePrivateNamespace (&sa, hBoundary, szNamespace);
46     LocalFree (sa.lpSecurityDescriptor);
47     DWORD dwError = GetLastError();
48 
49     if (hNamespace == NULL)
50         if (dwError == ERROR_ALREADY_EXISTS)
51         {
52             //  Если это пространство для именования существует, откройте это пространство имени
53             cout << "CreatePrivateNamespace failed : " << GetLastError() << endl;
54             hNamespace = OpenPrivateNamespace (hBoundary, szNamespace);
55 
56             bIsNamespaceOpen = TRUE;
57         }
58 
59 
60     TCHAR szMutex[256];
61     StringCchPrintf (szMutex, _countof(szMutex), L"%s\%s", szNamespace, L"MyMutex");
62 
63     HANDLE hMutex = CreateMutex (NULL, FALSE, szMutex);
64     if (GetLastError () == ERROR_ALREADY_EXISTS)
65     {
66         cout << "Этот объект ядра уже существует" << endl;
67     }
68     else if (hMutex == NULL)
69     {
70         cout << "Создайте ошибку" << endl;
71     }
72     else
73     {
74         cout << "Создание объекта подражания успешно" << endl;
75     }
76 
77 
78     if (hNamespace != NULL)
79     {
80         if (bIsNamespaceOpen)
81             ClosePrivateNamespace (hNamespace, PRIVATE_NAMESPACE_FLAG_DESTROY);
82         else
83             ClosePrivateNamespace (hNamespace, PRIVATE_NAMESPACE_FLAG_DESTROY);
84     }
85     
86     DeleteBoundaryDescriptor (hBoundary);
87 
88     CloseHandle (hMutex);
89 }
90 
91 int main (void)
92 {
93 
94     CheckInstances();
95 
96     while (1);
97     return 0;
98 }

в). ИспользуйтеDuplicateHandle Вы можете скопировать ручку объекта

1. DuplicateHandleПолучите элемент записи в таблице обработки процессов, а затем создайте копию этого элемента записи в таблице ручки другого процесса.

2. DuplicateHandle серединаdwOptionsПараметры могут быть указаныDUPLICATE_SAME_ACCESSа такжеDUPLICATE_CLOSE_SOURCEЗнак. Если вы указалиDUPLICATE_SAME_ACCESSЗнак будет надеяться, что целевой дескриптор имеет тот же крышку доступа, что и исходный процесс. Если вы указалиDUPLICATE_CLOSE_SOURCEЛоготип закроет ручку исходного процесса. С помощью этого знака количество объектов ядра не будет затронуто.

3. DuplicateHandle Как и наследование, целевой процесс не знает, что теперь он может получить доступ к новому объекту ядра, поэтому исходный процесс каким -то образом уведомит целевой процесс. В отличие от наследования, исходный процесс не может использовать параметры командной строки или изменять переменные среды целевого процесса.

4. Можно использоватьDuplicateHandleИзменить разрешения на доступ к объекту ядра

5. Вы не должны использовать функцию близкого рук, чтобы закрыть дескриптор, возвращаемый через параметр phtargethandle.

Дублировать три примера процесса:

Среди них процесс S является исходным процессом, процесс T является целевым процессом.

S Процесс исходный код:

 1 #define _CRT_SECURE_NO_DEPRECATE
 2 #include <iostream>
 3 #include <windows.h>
 4 using namespace std;
 5 
 6 
 7 int AnsiToUnicode (char *str_ansi, wchar_t *str_wide, unsigned int chcount);
 8 DWORD _stdcall Thread(LPVOID param);
 9 
10 int main(void)
11 {
12     cout << "Я процесс s" << endl;
13 
14 
15     HANDLE hThread = CreateThread (NULL, 0, Thread, NULL, 0, NULL);
16     Sleep(1000);
17 
18     //  Добавьте значение ручки в блок среды
19     char comline[256];
20     TCHAR p[256];
21     sprintf(comline, "%d", hThread);
22     AnsiToUnicode(comline, p, sizeof(p));
23     SetEnvironmentVariable(L"hThread", p);    
24 
25 
26     //  Уточните ручку резьбы к процессу C.
27     STARTUPINFO info = {0};
28     PROCESS_INFORMATION pi = {0};
29     CreateProcess (L"..\debug\C.exe", NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &pi);
30 
31 
32     CloseHandle(pi.hProcess);
33     CloseHandle(pi.hThread);
34     cin.get();
35     return 0;
36 }
37 
38 
39 DWORD _stdcall Thread(LPVOID param)
40 {
41     while (1)
42     {
43         Sleep(500);
44         cout << "loop" << endl;
45     }
46     return 0;
47 }
48 
49 int AnsiToUnicode (char *str_ansi, wchar_t *str_wide, unsigned int chcount)
50 {
51     return MultiByteToWideChar(CP_ACP, 0, str_ansi, -1, str_wide, chcount);
52 }

T Процесс исходный код:

 1 #include <iostream>
 2 #include <Windows.h>
 3 using namespace std;
 4 
 5 int main (void)
 6 {
 7     cout << "Я процесс t" << endl;
 8 
 9     HANDLE hThread;
10 
11     cout << "Пожалуйста, введите значение ручки объекта ядра, я закрою потоки процесса S:" << endl;
12     while (cin >> hThread)
13         TerminateThread(hThread, 0);
14 
15     cin.get();
16     return 0;
17 }

C Процесс исходный код:

 1 #include <iostream>
 2 #include <windows.h>
 3 #include <Tlhelp32.h>
 4 using namespace std;
 5 
 6 int UnicodeToAnsi(wchar_t *str_wide, char *str_ansi, unsigned int cbsize);
 7 HANDLE GetProcessHandle (LPCTSTR szProceName);
 8 
 9 int main (void)
10 {
11     cout << "Я процесс C" << endl;
12 
13 
14     //Получить ручку объекта резьбы процесса S процесса S
15     HANDLE hThread;
16     TCHAR buf[256] = {0};
17     char str[256] = {0};
18 
19     GetEnvironmentVariable(L"hThread", buf, sizeof(buf));
20     UnicodeToAnsi(buf, str, sizeof(str));
21     hThread = (HANDLE) atoi(str);
22     cout << "Получить объект ядра потока процесса из процесса S: " << hThread << endl;
23 
24 
25     //  Ручка репликации
26     HANDLE hobj;
27     HANDLE hSProcess, hTProcess;
28     hSProcess = GetProcessHandle(L"S.exe");
29     hTProcess =  GetProcessHandle(L"T.exe");
30     if (TRUE == DuplicateHandle (hSProcess, hThread, hTProcess, &hobj, 0, TRUE, DUPLICATE_SAME_ACCESS) )
31         cout << "Справиться: " <<  hobj << "  Копия успешно может теперь управлять объектом ядра процесса S в процессе T!" << endl;
32 
33     
34     CloseHandle (hSProcess);
35     CloseHandle (hTProcess);
36 
37     cin.get();
38     return 0;
39 }
40 
41 
42 HANDLE GetProcessHandle (LPCTSTR szProceName)
43 {
44     HANDLE hSanpshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
45     if (hSanpshot == INVALID_HANDLE_VALUE  || hSanpshot == NULL)
46     {
47         return NULL;
48     }
49 
50     PROCESSENTRY32 pe;
51     BOOL ok;
52     pe.dwSize = sizeof(pe);
53     ok = Process32First (hSanpshot, &pe);
54     if ( !ok )
55         return NULL;
56 
57     do{
58         if ( 0 == wcscmp( pe.szExeFile , szProceName) )
59         {
60             return OpenProcess (PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
61         }
62         ok = Process32Next (hSanpshot, &pe);
63     }while (ok);
64 
65     return NULL;
66 }
67 
68 int UnicodeToAnsi(wchar_t *str_wide, char *str_ansi, unsigned int cbsize)
69 {
70     return WideCharToMultiByte(CP_ACP, 0, str_wide, -1, str_ansi, cbsize, NULL, NULL);
71 }

Дублировать два примера процесса

Процесс S имеет объект ядра, а дескриптор объекта ядра в процессе S копируется в таблицу обработки T -процесса.

S Процесс исходный код:

 1 #include <iostream>
 2 #include <windows.h>
 3 #include <process.h>
 4 #include <TlHelp32.h>
 5 
 6 using namespace std;
 7 
 8 
 9 unsigned __stdcall thread (void * lpPragma);
10 HANDLE GetProcessHandle(LPCTSTR szName);
11 
12 
13 int main (void)
14 {
15 
16  
17     HANDLE hThread;
18     hThread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
19     cout << "my thread handle: " << hThread << endl;
20 
21 
22 
23     HANDLE hTarget;
24     if (DuplicateHandle (GetCurrentProcess(), hThread, GetProcessHandle(L"Target.exe"), &hTarget, 0, FALSE, DUPLICATE_SAME_ACCESS ) )
25         cout << "Ручка успешно воспроизведена, и его значение ручки составляет:" << hTarget << endl;
26          
27 
28 
29     cin.get();
30     return 0;
31 }
32 
33 
34 unsigned __stdcall thread (void * lpPragma)
35 {
36     while (1)
37     {
38         Sleep (500);
39         cout << "terminal me" << endl;
40     }
41 
42     return 0;
43 }
44 
45 HANDLE GetProcessHandle(LPCTSTR szName)
46 {
47     HANDLE hSanpshot;
48     hSanpshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
49     if ( INVALID_HANDLE_VALUE == hSanpshot )
50     {
51         return NULL;
52     }
53 
54     PROCESSENTRY32 pe;
55     BOOL bOk;
56     pe.dwSize = sizeof(pe);
57 
58     bOk = Process32First (hSanpshot, &pe);
59     if (!bOk)
60         return NULL;
61 
62     do {
63         if ( !wcscmp (pe.szExeFile, szName) )
64         {
65             return OpenProcess (PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
66         }
67         bOk = Process32Next (hSanpshot, &pe);
68     }while (bOk);
69 
70     return NULL;
71 }

T Процесс исходный код:

#include <iostream>
#include <windows.h>
#include <stdlib.h>
#include <process.h>
using namespace std;

unsigned __stdcall thread (void * lpPragma);

int main (void)
{
	HANDLE hRecv;


 
	HANDLE hThread;
	hThread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);

	cout << "my thread handle: " << hThread << endl;
	 Cout << "Пожалуйста, введите копию копируемой ручки:" << endl;
	cin >> hRecv;


	TerminateThread(hRecv, 0);




	while (1);
	return 0;
}


unsigned __stdcall thread (void * lpPragma)
{
	while (1)
	{
	
		Sleep (1500);
		MessageBox(NULL, L"run", L"", 0);
	}

	return 0;
}

Перепечатано по адресу: https://www.cnblogs.com/jer-/archive/2013/05/18/30855561.html

Предложите, как улучшить StudyLib

(Для жалоб на нарушения авторских прав, используйте

другую форму
)

Ваш е-мэйл

Заполните, если хотите получить ответ

Оцените наш проект

1

2

3

4

5

Понравилась статья? Поделить с друзьями:
  • Объект ядра ос windows может наследовать только мьютекс
  • Объект папка в windows соответствует понятию
  • Объект доступа в системе безопасности windows
  • Объект windows предназначенный для объединения файлов в группы
  • Объединить физические диски в один логический windows 7