2009-12-14 5 views
2

Я пишу (неуправляемый) класс C++ для обертывания Windows PropertySheet. По сути, что-то вроде этого:Обтекание PropertySheet; как обрабатывать обратные вызовы?

class PropSheet { 
    PROPSHEETHEADER d_header; 
    public: 
     PropSheet(/* parameters */); 
     INT_PTR show(); 
    private: 
     static int CALLBACK *propSheetProc(HWND hwnd, UINT msg, LPARAM lParam); 
}; 

Конструктор просто инициализирует d_header члена:

PropSheet::PropSheet(/* parameters */) { 
    d_header.dwSize = sizeof(PROPSHEETHEADER); 
    d_header.dwFlags = PSH_USECALLBACK; 
    // ... 
    d_header.pfnCallback = &propSheetProc; 
    // ... 
} 

После чего я могу показать его, покадрово, с:

INT_PTR PropSheet::show() { 
    return PropertySheet(&d_header); 
} 

Теперь проблема , потому что обратный вызов является статическим, что он не может получить доступ к классу-оболочке. Если это было обычное окно, с WindowProc вместо PropSheetProc, я мог бы добавить некоторые дополнительные данные в окно, используя cbWndExtra в WNDCLASS, в котором я мог бы сохранить указатель обратно к обертке, например, в this article. Но листы свойств не предлагают эту функциональность.

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

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

Есть ли у кого-нибудь лучшее представление, как обойти это?

ответ

1

Как показывает лист свойств модальны, вы должны иметь возможность использовать родительское окно (т.е. его ручку) лист собственности на карту к примеру, с помощью ::GetParent() от параметра PropSheetProc()hwndDlg.

+0

Увы, у него нет родителя. Кроме того, та же проблема возникла бы, если родительский породил несколько листов свойств.(Редкий, я знаю, но это может произойти.) – Thomas

+0

Или вы предлагаете, чтобы я использовал 'PropSheet *' для 'HWND'? Разве это не заставит голову Windows взорваться, если она попытается получить доступ к 'HWND'? – Thomas

+0

Я что-то не понимаю? Вы устанавливаете (или должны устанавливать) родительское окно на 'PROPSHEETHEADER' (т. Е.' HwndParent'). В 'PropSheetProc()' вы получаете дескриптор диалога в качестве первого параметра, на который вы можете вызвать 'GetParent()'. –

0

Удивительный, еще один API Win32, который использует обратные вызовы без пользовательского контекстного параметра. Увы, это не единственный. например CreateWindow - это плохо (он дает пользовательский контекст, но этот контекст недоступен для первых нескольких оконных сообщений), SetWindowsHookEx еще хуже (никакого контекста вообще).

Единственное «решение», которое является универсальным и эффективным, состоит в том, чтобы испускать небольшой кусок исполняемого кода с «этим» указателем жестко закодированным. Что-то вроде этого: http://episteme.arstechnica.com/eve/forums/a/tpc/f/6330927813/m/848000817831?r=848000817831#848000817831

Это ужасно.

+0

CreateWindow() имеет параметр lParam, который позволяет передать пользовательское значение в новое окно. Это значение доступно в структуре CREATESTRUCT сообщения WM_CREATE, которое затем можно скопировать в HWND, например, с помощью SetWindowLong (GWL_USERDATA) или SetProp() для использования в последующих сообщениях. –

+0

Было бы замечательно, если бы вы получили первое сообщение WM_CREATE. Это не так. – DrPizza

+0

Ничего себе, это один хак, о котором я никогда не думал. Но я не смею спускаться по этой дороге. – Thomas

0

Структура PROPSHEETPAGE имеет поле lParam, доступное для обратных вызовов. В вашем PROPSHEETHEADER вы можете включить флаг PSH_PROPSHEETPAGE, чтобы передать массив элементов PROPSHEETPAGE, описывающих ваши страницы, или опустить флаг для передачи массива предварительно выделенных дескрипторов HPROPSHEETPAGE (что означает использование CreatePropertySheetPage() и, таким образом, использование PROPSHEETPAGE).

+0

В соответствии с документацией для 'PropSheetProc()', 'lParam' содержит 0, например. 'PSCB_BUTTONPRESSED'. –

+1

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

+0

Что вам нужно сделать в обратном вызове листа, который не может обрабатываться обратными вызовами отдельной страницы? –

0

Вы уже признались: «Я не могу выполнить код между созданием и уничтожением фактического окна». Кажется, что глобальная переменная не была бы ужасным взломом.

0

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