如何继承win32控件并保持与旧版本comctl32.dll的兼容性?

问题描述:

通用控件(comctl32.dll)的6.0版实现了旧版Windows上无法使用的子类化控件的新方法。实现子类化的最佳方式是什么,以便它可以在支持通用控件库版本的系统上工作?如何继承win32控件并保持与旧版本comctl32.dll的兼容性?

+3

0.15%的Windows机器需要这样的代码。他们的所有者不购买软件。 – 2012-03-17 05:20:56

+0

根据[SetWindowSubclass']文档(http://msdn.microsoft.com/en-us/library/windows/desktop/bb762102.aspx),该功能可用于Comctl32.dll,版本5.8或更高版本。这是错误的,还是这实际上并不需要你选择v6? (我不记得那么久了。) – 2012-03-17 05:32:11

+0

如果您的目标是Windows 95-2000,那么该功能将无法使用。 – 2012-03-17 05:33:41

首先,有一个article on MSDN讨论了6.0版和之前的子类化控件中您应该熟悉的更改。

维持向后兼容性的最佳方式是为子类控件创建包装函数。这将需要您动态加载comctl32.dll版本6上的子类控件所需的函数。这是一个如何完成的粗略例子。

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作为子类ID,如果您在具有相同WindowProc函数的对话框中继承了多个控件,则会崩溃。在上面的例子中,我们分配了一个名为SubclassStruct的新内存结构,并将其地址作为子类ID传递,以保证子类的每个控件实例都具有唯一的子类ID。

如果您在多个应用程序中使用子类函数,有些使用comctl32.dll < 6,而有些使用comctl32.dll> = 6,则可以通过获取comctl32来检测通用控件库的哪个版本被加载。 dll的文件版本信息。这可以通过使用GetModuleFileNameGetFileVersionInfo来完成。

另外,如果你在子类中的回调上 Writing Windows Custom Controls使用SetWindowWord/GetWindowWord与COMCTL32.DLL 6.0,如下面的布斯博士的文章中,那么你需要的时候COMCTL32.DLL有条件使用这些代码块< 6,因为它们不能在版本6或更高版本上运行,并且会导致应用程序崩溃。