解决方法
首先,有一个
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,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 *OriginalProc) { SubclassStruct *Subclass = NULL; int Result = TRUE; if (RemoveWindowSubclassPtr != NULL) { if (*OriginalProc != NULL) { Subclass = (SubclassStruct *)*OriginalProc; Proc = Subclass->Proc; } Result = RemoveWindowSubclassPtr(hWnd,(UINT_PTR)Subclass); free(Subclass); } else { SetLastError(0); if (SetWindowLongPtr(hWnd,(__int3264)(UINT_PTR)*OriginalProc) == 0) { if (GetLastError() > 0) Result = FALSE; } } SetLastError(0); if (SetWindowLongPtr(hWnd,0) == 0) { if (GetLastError() > 0) Result = FALSE; } *OriginalProc = NULL; if (Result == FALSE) return FALSE; return TRUE; } LRESULT Subclasser_DefProc(WNDPROC OriginalProc,HWND hWnd,LPARAM lParam) { if (OriginalProc == NULL) return DefWindowProc(hWnd,lParam); if (DefSubclassProcPtr != NULL) return DefSubclassProcPtr(hWnd,lParam); return CallWindowProc(OriginalProc,lParam); }
唯一的另一个例子可以在OpenJDK中找到.它的一个缺点是它使用WindowProc作为子类ID,如果你在具有相同WindowProc函数的对话框上继承多个控件,它将崩溃.在上面的示例中,我们分配一个名为SubclassStruct的新内存结构,并将其地址作为子类ID传递,以保证子类控件的每个实例都具有唯一的子类ID.
如果您在多个应用程序中使用子类化函数,则有些使用comctl32.dll< 6和一些使用comctl32.dll> = 6,你可以通过获取comctl32.dll的文件版本信息来检测加载了哪个版本的公共控件库.这可以通过使用GetModuleFileName和GetFileVersionInfo来完成.
另外,如果你在comctl32.dll 6.0的子类回调中使用SetWindowWord / GetWindowWord,例如在下面的Dobbs博士文章中
Writing Windows Custom Controls,那么当comctl32.dll<时,你需要有条件地使用这些代码块. 6,因为它们无法在版本6或更高版本上运行,并且会导致应用程序崩溃.