Как создать подкласс для элемента управления win32 и обеспечить совместимость со старыми версиями comctl32.dll? - PullRequest
4 голосов
/ 17 марта 2012

Версия 6.0 общих элементов управления (comctl32.dll) реализует новый подход к элементам управления подклассами, который недоступен в более старых версиях Windows. Каков наилучший способ реализации подкласса, чтобы он работал в системах, которые поддерживают любую версию библиотеки общих элементов управления?

1 Ответ

4 голосов
/ 17 марта 2012

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

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

typedef BOOL (WINAPI *LPFN_SETWINDOWSUBCLASS)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
typedef LRESULT (WINAPI *LPFN_DEFSUBCLASSPROC)(HWND, UINT, WPARAM, LPARAM);
typedef BOOL (WINAPI *LPFN_REMOVEWINDOWSUBCLASS)(HWND, SUBCLASSPROC, UINT_PTR);
typedef BOOL (WINAPI *LPFN_INITCOMMONCONTROLSEX)(LPINITCOMMONCONTROLSEX);

typedef struct SubclassStruct {
    WNDPROC Proc;
} SubclassStruct;

LPFN_SETWINDOWSUBCLASS      SetWindowSubclassPtr = NULL;
LPFN_REMOVEWINDOWSUBCLASS   RemoveWindowSubclassPtr = NULL;
LPFN_DEFSUBCLASSPROC        DefSubclassProcPtr = NULL;
LPFN_INITCOMMONCONTROLSEX   InitCommonControlsExPtr = NULL;

HMODULE ComCtlModule = NULL;

int Subclasser_Init(void)
{
    INITCOMMONCONTROLSEX CommonCtrlEx = {0};


    ComCtlModule = LoadLibrary("comctl32.dll");
    if (ComCtlModule == NULL) 
        return FALSE;

    SetWindowSubclassPtr = (LPFN_SETWINDOWSUBCLASS)GetProcAddress(ComCtlModule, "SetWindowSubclass");
    RemoveWindowSubclassPtr = (LPFN_REMOVEWINDOWSUBCLASS)GetProcAddress(ComCtlModule, "RemoveWindowSubclass");
    DefSubclassProcPtr = (LPFN_DEFSUBCLASSPROC)GetProcAddress(ComCtlModule, "DefSubclassProc");
    InitCommonControlsExPtr = (LPFN_INITCOMMONCONTROLSEX)GetProcAddress(ComCtlModule, "InitCommonControlsEx");

    if (InitCommonControlsExPtr != NULL)
    {
        CommonCtrlEx.dwSize = sizeof(CommonCtrlEx);
        InitCommonControlsExPtr(&CommonCtrlEx);
    }

    return TRUE;
}

int Subclasser_Uninit(void)
{
    if (ComCtlModule != NULL)
        FreeLibrary(ComCtlModule);
    return TRUE;
}

LRESULT CALLBACK Subclasser_SharedSubclassProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam, UINT_PTR SubclassId, DWORD_PTR RefData)
{
    SubclassStruct *Subclass = (SubclassStruct *)SubclassId;
    return CallWindowProc(Subclass->Proc, hWnd, Message, wParam, lParam);
}

int Subclasser_SetProc(HWND hWnd, WNDPROC Proc, WNDPROC *OriginalProc, void *Param)
{
    SubclassStruct *Subclass = NULL;
    int Result = TRUE;



    SetLastError(0);
    if (SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(UINT_PTR)Param) == 0)
    {
        if (GetLastError() > 0)
            return FALSE;
    }

    if (SetWindowSubclassPtr!= NULL) 
    {
        Subclass = (SubclassStruct*)malloc(sizeof(SubclassStruct));
        Subclass->Proc = Proc;
        *OriginalProc = (WNDPROC)Subclass;
        Result = SetWindowSubclassPtr(hWnd, Subclasser_SharedSubclassProc, (UINT_PTR)Subclass, NULL);
    }
    else
    {
        *OriginalProc = (WNDPROC)(void *)GetWindowLongPtr(hWnd, GWLP_WNDPROC);

        SetLastError(0);
        if (SetWindowLongPtr(hWnd, GWLP_WNDPROC, (__int3264)(intptr)Proc) == 0)
        {
            if (GetLastError() > 0)
                Result = FALSE;
        }
    }

    if (Result == FALSE)
        return FALSE;

    return TRUE;
}

int Subclasser_UnsetProc(HWND hWnd, WNDPROC Proc, WNDPROC *OriginalProc)
{
    SubclassStruct *Subclass = NULL;
    int Result = TRUE;


    if (RemoveWindowSubclassPtr != NULL)
    {
        if (*OriginalProc != NULL)
        {
            Subclass = (SubclassStruct *)*OriginalProc;
            Proc = Subclass->Proc;
        }

        Result = RemoveWindowSubclassPtr(hWnd, Subclasser_SharedSubclassProc, (UINT_PTR)Subclass);
        free(Subclass);
    }
    else
    {
        SetLastError(0);
        if (SetWindowLongPtr(hWnd, GWLP_WNDPROC, (__int3264)(UINT_PTR)*OriginalProc) == 0)
        {
            if (GetLastError() > 0)
                Result = FALSE;
        }
    }

    SetLastError(0);
    if (SetWindowLongPtr(hWnd, GWLP_USERDATA, 0) == 0)
    {
        if (GetLastError() > 0)
            Result = FALSE;
    }

    *OriginalProc = NULL;

    if (Result == FALSE)
        return FALSE;

    return TRUE;
}

LRESULT Subclasser_DefProc(WNDPROC OriginalProc, HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    if (OriginalProc == NULL)
        return DefWindowProc(hWnd, Message, wParam, lParam);
    if (DefSubclassProcPtr != NULL)
        return DefSubclassProcPtr(hWnd, Message, wParam, lParam);
    return CallWindowProc(OriginalProc, hWnd, Message, wParam, lParam);
}

Единственный другой пример можно найти в OpenJDK . Единственный недостаток этого метода заключается в том, что он использует WindowProc в качестве идентификатора подкласса, который дает сбой, если вы подклассируете более одного элемента управления в диалоговом окне с одной и той же функцией WindowProc. В приведенном выше примере мы выделяем новую структуру памяти с именем SubclassStruct и передаем ее адрес в качестве идентификатора подкласса, который гарантирует, что каждый экземпляр элемента управления, который вы подклассом, будет иметь уникальный идентификатор подкласса.

Если вы используете функции создания подклассов в нескольких приложениях, некоторые из которых используют comctl32.dll <6, а другие используют comctl32.dll> = 6, вы можете определить, какая версия библиотеки общего элемента управления была загружена, получив файл comctl32.dll. информация о версии файла. Это можно сделать с помощью GetModuleFileName и GetFileVersionInfo.

Кроме того, если вы используете SetWindowWord / GetWindowWord в обратных вызовах подкласса с comctl32.dll 6.0, как, например, в следующей статье доктора Доббса о Написание пользовательских элементов управления Windows , тогда вам нужно будет условно использовать эти блоки кода, когда comctl32.dll <6, потому что они не будут работать в версии 6 или выше и вызовут сбой вашего приложения. </p>

...