Winapi 如何对win32控件进行子类化并保持与旧版本的comctl32.dll的兼容性?

Winapi 如何对win32控件进行子类化并保持与旧版本的comctl32.dll的兼容性?,winapi,controls,subclass,wndproc,comctl32,Winapi,Controls,Subclass,Wndproc,Comctl32,6.0版的通用控件(comctl32.dll)实现了一种新的控件子类化方法,这种方法在旧版本的Windows上不可用。实现子类化的最佳方法是什么,以使其在支持公共控件库任一版本的系统上工作?首先,有一个讨论版本6.0和之前版本之间控件子类化发生的变化的示例,您应该熟悉 保持向后兼容性的最佳方法是为控件子类化创建包装函数。这将要求您动态加载comctl32.dll版本6上控件子类化所需的函数。下面是一个如何做到这一点的粗略示例 typedef BOOL (WINAPI *LPFN_SETWINDO

6.0版的通用控件(comctl32.dll)实现了一种新的控件子类化方法,这种方法在旧版本的Windows上不可用。实现子类化的最佳方法是什么,以使其在支持公共控件库任一版本的系统上工作?

首先,有一个讨论版本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);
}
唯一的其他例子可以在中找到。它的一个缺点是,它使用WindowProc作为子类ID,如果使用相同的WindowProc函数对对话框上的多个控件进行子类化,则会崩溃。在上面的示例中,我们分配了一个名为subclass struct的新内存结构,并将其地址作为子类ID传递,从而确保子类中控件的每个实例都具有唯一的子类ID

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

此外,如果您在comctl32.dll 6.0的子类回调中使用SetWindowWord/GetWindowWord,例如下面的Dr.Dobbs关于
,则当comctl32.dll<6时,您需要有条件地使用这些代码块,因为它们在版本6或更高版本上无法工作,并会导致应用程序崩溃。

0.15%的Windows计算机需要这样的代码。他们的所有者不购买软件。根据,该功能可用于Comctl32.dll,版本5.8或更高版本。这是错误的,还是实际上并不要求您选择v6?(我不记得很久以前的事了。)如果你的目标是Windows95-2000,那么这个功能将不可用。啊,我就是这么想的。事实上,它可以在您可能仍然合理支持的任何Windows版本上使用,只是没有按名称记录或导出。Comctl32.dll版本4.72(随Win 98一起提供)按顺序将其导出。我不认为这样使用它们有什么害处,因为那些旧版本的Windows不会改变。