Вы можете расширить структуру PROPSHEETPAGE своими собственными дополнительными данными

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

Малоизвестная и еще реже используемая возможность окна свойств оболочки, которая заключается в том, что вы можете прикрепить собственные данные в конец структуры PROPSHEETPAGE, а оболочка операционной системы (shell) будет передавать их вам. Правда, она передает их при помощи операции memcpy и уничтожает их, просто освобождая память, занятую этими данными, поэтому единственное, что вы можете прикрепить к этой структуре, лишь простые типы данных (plain old data). (Однако вы можете получить возможность «конструировать» и «деконструировать» эти данные, если зарегистрируете обратный вызов типа PropSheetPageProc, в котором вы сможете изменять ваши дополнительные данные и поле lParam структуры PROPSHEETPAGE).

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

#include <windows.h> #include <prsht.h> HINSTANCE g_hinst; struct ITEMPROPSHEETPAGE : public PROPSHEETPAGE {   int cWidgets;   TCHAR szItemName[100]; };

ITEMPROPSHEETPAGE — это наша собственная структура, которая присоединяет наши дополнительные данные (целое число и строку) к стандартной структуре PROPSHEETPAGE. Эту структуру будет использовать наша вкладка окна свойств.

INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) {   switch (uiMsg) {   case WM_INITDIALOG:     {       ITEMPROPSHEETPAGE *ppsp =         reinterpret_cast<ITEMPROPSHEETPAGE*>(lParam));       SetDlgItemText(hwnd, 100, ppsp->szItemName);       SetDlgItemInt(hwnd, 101, ppsp->cWidgets, FALSE);     }     return TRUE;   }   return FALSE; }

Значение lParam, переданное в обработчик сообщения WM_INITDIALOG, является указателем на структуру PROPSHEETPAGE, управляемую оболочкой. Поскольку мы ассоциировали эту процедуру диалогового окна со структурой ITEMPROPSHEETPAGE, мы можем выполнить приведение к расширенной структуре, чтобы получить наши дополнительные данные (которые оболочка благополучно скопировала при помощи операции memcpy из нашего экземпляра в экземпляр, управляемый оболочкой).

HPROPSHEETPAGE CreateItemPropertySheetPage(         int cWidgets, PCTSTR pszItemName) {   ITEMPROPSHEETPAGE psp;   ZeroMemory(&psp, sizeof(psp));   psp.dwSize = sizeof(psp);   psp.hInstance = g_hinst;   psp.pszTemplate = MAKEINTRESOURCE(1);   psp.pfnDlgProc = DlgProc;   psp.cWidgets = cWidgets;   lstrcpyn(psp.szItemName, pszItemName, 100);   return CreatePropertySheetPage(&psp); }

Здесь мы ассоциируем процедуру DlgProc со структурой ITEMPROPSHEETPAGE. Для того чтобы подчеркнуть, что указатель, передаваемый в DlgProc, является копией структуры ITEMPROPSHEETPAGE, которая использовалась для создания вкладки окна свойств, я вынес код ее создания в отдельную функцию. Таким образом, содержимое структуры ITEMPROPSHEETPAGE, находящееся в стеке, выходит за пределы видимости, и становится очевидным, что экземпляр, переданный в DlgProc, не тот же самый экземпляр, который мы передали в функцию CreatePropertySheetPage.

Обратите внимание, что вы должны установить поле dwSize базовой структуры PROPSHEETPAGE равным сумме размеров структуры PROPSHEETPAGE и ваших дополнительных данных. Другими словами, вы должны установить это поле равным размеру вашей структуры ITEMPROPSHEETPAGE.

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,                    LPSTR lpCmdLine, int nCmdShow) {   g_hinst = hinst;   HPROPSHEETPAGE hpage =       CreateItemPropertySheetPage(42, TEXT("Elmo"));   if (hpage) {     PROPSHEETHEADER psh = { 0 };     psh.dwSize = sizeof(psh);     psh.dwFlags = PSH_DEFAULT;     psh.hInstance = hinst;     psh.pszCaption = TEXT("Item Properties");     psh.nPages = 1;     psh.phpage = &hpage;     PropertySheet(&psh);   }   return 0; }

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

Если вы предпочитаете использовать флаг PSH_PROPSHEETPAGE, тогда вышеприведенный код должен быть переписан в таком виде:

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,                    LPSTR lpCmdLine, int nCmdShow) {   ITEMPROPSHEETPAGE psp;   ZeroMemory(&psp, sizeof(psp));   psp.dwSize = sizeof(psp);   psp.hInstance = g_hinst;   psp.pszTemplate = MAKEINTRESOURCE(1);   psp.pfnDlgProc = DlgProc;   psp.cWidgets = cWidgets;   lstrcpyn(psp.szItemName, pszItemName, 100);   PROPSHEETHEADER psh = { 0 };   psh.dwSize = sizeof(psh);   psh.dwFlags = PSH_PROPSHEETPAGE;   psh.hInstance = hinst;   psh.pszCaption = TEXT("Item Properties");   psh.nPages = 1;   psh.ppsp = &psp;   PropertySheet(&psh);   return 0; }

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

И, наконец, шаблон диалогового окна. Еще скучнее.

1 DIALOG 0, 0, PROP_SM_CXDLG, PROP_SM_CYDLG STYLE WS_CAPTION | WS_SYSMENU CAPTION "General" FONT 8, "MS Shell Dlg" BEGIN     LTEXT "Name:",-1,7,11,42,14     LTEXT "",100,56,11,164,14     LTEXT "Widgets:",-1,7,38,42,14     LTEXT "",101,56,38,164,14 END

Вот и все. Прикрепление своих собственных данных в конец структуры PROPSHEETPAGE является более эффективной альтернативой вместо попытки запихнуть все в единственный параметр lParam.

Упражнение: посмотрите, как со временем менялся размер структуры PROPSHEETPAGE. К примеру, изначальная версия структуры PROPSHEETPAGE заканчивалась полем pcRefParent. В Windows 2000 добавилось два новых поля: pszHeaderTitle и pszHeaderSubTitle. Windows XP добавила еще одно поле: hActCtx. Представьте, что вы написали программу для Windows 95 с использованием данной техники. Как оболочка узнает, что настоящие дополнительные данные начинаются с поля cWidgets, а не с pszHeaderTitle?