2012-03-17 2 views
4

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

+3

0,15% компьютеров Windows требуют кода, подобного этому. Их владельцы не покупают программное обеспечение. –

+0

В соответствии с [документацией для 'SetWindowSubclass'] (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762102.aspx) эта функция доступна для Comctl32.dll версии 5.8 или новее , Это неправильно, или это на самом деле не требует от вас выбора в v6? (Я не могу вспомнить, что далеко назад.) –

+0

Если вы ориентируетесь на Windows 95-2000, эта функция не будет доступна. –

ответ

4

Во-первых, существует article on 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, например, в следующей статье доктор Доббс на Writing Windows Custom Controls, то вам нужно будет использовать эти блоки кода условно, когда comctl32.dll < 6, потому что они не будут работать с версией 6 или выше и вызовет сбой приложения.

Смежные вопросы