Создание нового дочернего окна
При выборе из меню File строки New создается новое дочернее MDI-окно и задача для него. Первое действие выполняется с помощью функции CreateMDIWindow, специально созданной для мультизадачных MDI-приложений. Задача создается при помощи функции CreateThread. Рассмотрим процесс создания дочернего окна подробнее.
Параметры фукнции CreateMDIWindow являются комбинацией значений, сохраняемых в структуре MDICREATESTRUCT полюс идентификатор окна Client Window. Напомним, что адрес этой структуры передается через второй параметр сообщения WM_MDICREATE, предназначенного для создания дочерних MDI-окон в однозадачных приложениях. Вот как создается такое окно в нашем приложении:
hwndChild = CreateMDIWindow(
LPSTR)szChildClassName, // класс окна
"MDI Child Window", // заголовок окна
0, // дополнительные стили
CW_USEDEFAULT, CW_USEDEFAULT, // размеры окна
CW_USEDEFAULT, CW_USEDEFAULT, // Document Window
hwndClient, // идентификатор окна Client Window
hInst, // идентификатор приложения
0); // произвольное значение
Вы можете сравнить это со способом, описанным нами в 17 томе “Библиотеки системного программиста” в разделе “Создание и уничтожение окна Document Window”.
Для каждого дочернего окна мы создаем и инициализируем структуру типа CHILD_WINDOW_TAG, содержащую такие значения, как идентификатор задачи, запущенной для окна, признак активности задачи и критическую секцию. Память для структуры мы получаем простейшим способом - с помощью функции malloc:
lpTag = malloc(sizeof(CHILD_WINDOW_TAG));
Далее в процессе инициализации этой структуры мы устанавливаем начальное знзачение для признака активности и выполняем инициализацию критической секции:
lpTag->fActive = 1;
InitializeCriticalSection(&(lpTag->csChildWindowPaint));
Это нужно сделать обязательно до запуска задачи, которая будет пользоваться критической секцией и проверять флаг активности.
Адрес структуры мы сохраняем в области данных окна, используя для этого функцию SetWindowLong:
SetWindowLong(hwndChild, GWL_USERDATA, (LONG)lpTag);
Впоследствии этот адрес будет извлечен функцией задачи.
На следующем шаге мы выполняем создание задачи для дочернего MDI-окна, которая будет заниматься рисованием произвольных эллипсов. Для запуска используется функция CreateThread:
hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)ThreadRoutine,
(LPVOID)hwndChild, 0,(LPDWORD)&dwIDThread);
В качестве третьего параметра мы передаем этой функции адрес функции задачи ThreadRoutine, а в качестве четвертого - идентификатор дочернего MDI-окна, который необходим для выполенния рисования.
Заметим, что мы могли бы передать через четвертый параметр функции CreateThread адрес структуры CHILD_WINDOW_TAG, дополнив эту структуру полем для хранения идентификатора дочернего MDI-окна. При этом функция задачи получила бы доступ ко всем необходимым ей параметрам. Однако в нашем приложении мы продемонстрировали оба способа, связанных с использованием параметра функции задачи и памяти окна.
После того как задача создана, ее идентификатор сохраняется в структуре CHILD_WINDOW_TAG:
lpTag->hThread = hThread;
В дальнейшем это значение будет использовано для управления задачей - для ее приостановки, возобновления выполнения, изменения относительного приоритета и так далее.
На заключительном шаге в заголовке дочернего MDI-окна отображается системный номер задачи, который записывается функцией CreateThread в переменную dwIDThread:
sprintf(szBuf, "Thread ID = %lX", dwIDThread);
SetWindowText(hwndChild, szBuf);
Напомним, что системный номер задачи и ее идентификатор - разные по смыслу и значению величины.