加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Windows > 正文

winapi – 如何子类化win32控件并保持与旧版本的comctl32.dll的

发布时间:2020-12-14 05:28:39 所属栏目:Windows 来源:网络整理
导读:通用控件的6.0版(comctl32.dll)实现了一种新方法,用于子类化旧版 Windows上不可用的控件.实现子类化的最佳方法是什么,以便它可以在支持任一版本的公共控件库的系统上运行? 解决方法 首先,有一个 article on MSDN讨论了在您应该熟悉的版本6.0和之前的子类控
通用控件的6.0版(comctl32.dll)实现了一种新方法,用于子类化旧版 Windows上不可用的控件.实现子类化的最佳方法是什么,以便它可以在支持任一版本的公共控件库的系统上运行?

解决方法

首先,有一个 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或更高版本上运行,并且会导致应用程序崩溃.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读