Проблема дизайна в оболочке Windows Window API - PullRequest
0 голосов
/ 03 февраля 2011

Я пишу объектно-ориентированную оболочку API окна для Windows в D, и у меня возникла проблема (не зависящая от языка).

Windows требует, чтобы все окна были предварительно зарегистрированы вRegisterClass;расширение существующего класса требует замены оконной процедуры.Кроме того, кажется, что есть два вида оконных дескрипторов: HWND s, которые должны быть удалены (через DestroyWindow), и HWND s, которые не (например, из других приложений).

Я создал класс Window, и пока я просто обертываю ShowWindow, UpdateWindow, FindWindow и другие подобные методы, все хорошо и здорово.Но как только я пытаюсь добавить конструктор с параметром className в мой класс, который вызывает CreateWindow, я сталкиваюсь с проблемой:

  1. Указанный className должен быть уже зарегистрированна RegisterClass.
  2. Чтобы зарегистрировать новый класс окна, мне нужно было бы сделать мои подклассы из Window так или иначе вызвать RegisterClass, прежде чем пытаться создать новое окно, прямо или косвенно.
  3. Для того, чтобы мой дизайн (и наследование) имел смысл, мне нужно убедиться, что для любого данного подкласса Window все экземпляры фактически являются экземплярами одного и того же класса окна;а именно, что все className из определенного подкласса идентичны.(Это потому, что оконные процедуры для всех экземпляров определенного оконного класса должны быть одинаковыми.)

Проблема в том, что нет способа иметь метод abstract static (для Windowчтобы иметь возможность запрашивать у подклассов информацию об их классе и регистрировать их один раз), и поэтому я вынужден сказать что-то вроде CreateWindow(this.className, ...), чтобы создать новое окно, которое легко становится проблематичным, если мои подклассы не уважаютЭто правило и дает мне другое имя класса для каждого экземпляра окна.

Кроме того, мне нужно однозначное сопоставление между полем WNDCLASS.lpfnWndProc и методом моего подкласса Window (переопределенным) WndProc.,Однако это не совсем работает, если я вынужден получать указатель на метод для каждого экземпляра, поскольку он нарушает весь дизайн ООП и портит все.

Хотя я могу применять эту согласованность во время выполнения, она немного уродлива и поэтому не является хорошим решением.

Итак, если вкратце, у кого-нибудь есть идеи элегантного решения дляпроблема создания abstract static метода?Я думаю о некоторых шаблонах дизайна, таких как Фабрика и все такое, но я не уверен, подходят ли они здесь ... если кто-то думает, что они могли бы, я был бы очень признателен за небольшое объяснение того, как это вписалось бы в дизайн.

Спасибо!

Ответы [ 3 ]

2 голосов
/ 29 июля 2012

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

Базовый класс регистрирует свой класс с помощью процедуры статического окна.Затем процедура статического окна вызывает процедуру виртуального окна.Многие люди пропускают параметр HWND в виртуальной версии, поскольку его можно получить из указателя this, но я оставлю его только для упрощения истории.

class Window
{
public:
    virtual LRESULT WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    { return DefWindowProc(hwnd, uMsg, wParam, lParam); }
private:
    static LRESULT CALLBACK StaticWndProc(
        HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_NCCCREATE) {
            SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(
                 reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams);
        }
        Window *pThis = reinterpret_cast<Window*>(
                                     GetWindowLongPtr(hwnd, GWLP_USERDATA));
        LRESULT lres = pThis ? pThis->WndProc(hwnd, uMsg, wParam, lParam)
                             : DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
};

Переопределение производных классов Window::WndProc.

0 голосов
/ 03 февраля 2011

Ух ты, я, конечно, этого не ожидал ...

Я искал фразу в Google x86 thunks.И, как оказалось, я был не первым, кто столкнулся с этой проблемой (сюрприз!).

Первым хитом была точная задача и решение на этот вопрос о Window Procs, хотя это не имело никакого отношения к моему запросу (или, по крайней мере, очень мало, чтобы сделать напрямую) ... надеюсь, что кто-то еще найдет его полезным.

0 голосов
/ 03 февраля 2011

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

Как правило, вы используете функцию RegisterClass для установки по умолчанию оконный процесс для окон этого класса.Когда вы создаете новое окно, которое использует класс, но вы хотите, чтобы оно переопределило обработку по умолчанию, вы создаете окно и затем вызываете SetWindowLongPtr(hwnd, GWL_WNDPROC, newWndProc), где newWndProc - указатель на proc окна нового окна.

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

Чтобы создать окно с новым именем класса, пусть конструктор скопирует информацию о классе длянаследуемый класс и создайте новый класс, идентичный этому, за исключением имени.Затем предоставьте метод, который позволит клиенту изменять специфичные для класса вещи.Интересующие вас функции API: GetClassInfoEx и SetClassLong, SetClassWord или SetClassLongPtr.

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

Для этого вам не нужно ничего подобного abstract static методу.Все, что вам нужно, это ручка окна.Вы можете получить имя класса, вызвав GetClassName с дескриптором окна, а затем позвонив GetClassInfoEx с возвращенным именем класса.

...