winapi - 如何子类化 win32 控件并保持与旧版本 comctl32.dll 的兼容性?

标签 winapi controls subclass wndproc comctl32

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

最佳答案

首先,有一个 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 来检测加载了哪个版本的公共(public)控制库。文件版本信息。这可以通过使用 GetModuleFileNameGetFileVersionInfo 来完成。

此外,如果您在 comctl32.dll 6.0 的子类回调中使用 SetWindowWord/GetWindowWord,例如以下 Dr. Dobbs 文章中的 Writing Windows Custom Controls ,那么当 comctl32.dll < 6 时,您将需要有条件地使用这些代码块,因为它们在版本 6 或更高版本上不起作用,并会导致您的应用程序崩溃。

关于winapi - 如何子类化 win32 控件并保持与旧版本 comctl32.dll 的兼容性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9747415/

相关文章:

wpf - 在 WPF 中使用 ObjectAnimationUsingKeyFrames 对 Control 的 'Style' 属性进行动画处理

java - 在Java中,在一个静态方法内部,如何判断调用了哪个类的静态方法?

swift - 使父类(super class)的初始值设定项返回特定的子类?

c++ - MFC 应用程序在发出终止信号的线程中挂起

c++ - 为什么我会得到对任何与打印相关的函数 win32 的 undefined reference ?

.net - 如何将 dll 永久添加到 .net 工具箱中?

java - Sprite 旋转的延迟

java - 当子类对象存储在父类(super class)数组中时,如何调用子类方法?

c++ - 为什么在 win32 中有不同的 TEXT like macros for same thing?

c++ - printf 的浮点格式标志 (%f) 仅适用于英文数字格式