头上的这篇贴子:[专题]关于换肤、子类化,征求解决方案,已经找不到了,哪位保存了的请帮传上来,谢谢

解决方案 »

  1.   

    对于应用程序的换肤及子类化。下面是我尝试过一些方法,
    以在CAboutDlg中子类化其中的Button为例:
    第一种:直接用现成的类
    1.自己写一个类class CButtonXP : public CButton{/*...*/}
      用MessageMap处理感兴趣的消息。
    2.用CButtonXP代替CButton来声明变量m_btn;3.在void CAboutDlg::DoDataExchange(CDataExchange* pDX)中加上一句:
      DDX_Control(pDX, IDB_BUTTON1, m_edit);
      或者在InitDialog()中加上
      m_btn.SubclassDlgItem(IDB_BUTTON1, this);
      这两种效果差不多的。第二种:在Hook中使用现成的类。
    1.自己写一个类class CButtonXP : public CButton{/*...*/}
      用MessageMap处理感兴趣的消息。
    2,用g_hWndProcHook =  ::SetWindowsHookEx(WH_CALLWNDPROC,
              WndProcHook, NULL, ::GetCurrentThreadId());安装一个钩子
    3. 在WndProcHook中处理窗口创建和销毁的消息
    LRESULT CALLBACK WndProcHook(int code, WPARAM wParam, LPARAM lParam)
    {
        if (code == HC_ACTION)
        {
            switch (((CWPSTRUCT*) lParam)->message)
            {
                case WM_CREATE:
                    BeginSubclassing(((CWPSTRUCT*) lParam)->hwnd);
                    break;            case WM_NCDESTROY:
                    // TODO: clear subclass info.
                    EndSubclassing(((CWPSTRUCT*) lParam)->hwnd);
                    break;            default:
                    break;
            }
        }    return CallNextHookEx(g_hWndProcHook, code, wParam, lParam);
    }
    4. 在BeginSubclassing中用GetClassName得到类名,例如"Button",然后用CButtonXP类进行子类化.
    CButtonXP pButton = new CButtonXP 
    VERIFY(pButton ->SubclassWindow(hWnd));第三种 在Hook中使用窗口过程。
    1. 自己写一个按钮的窗口过程
    WNDPROC oldProc;
    LRESULT CALLBACK ProcButton(
        HWND    hWnd,
        UINT    uMsg,
        WPARAM  wParam,
        LPARAM  lParam)
    {
        ASSERT(oldProc != 0);
        if (oldProc == 0) return TRUE;
        switch (uMsg)
        {
            case WM_ERASEBKGND:
                break;
            //......
            default:
                break;
        }    return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
    }
    2.同第二种
    3.同第二种
    4.在BeginSubclassing中得到类名后,用SetWindowLong的方式子类化
        oldProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);
        SetWindowLong(hWnd, GWL_WNDPROC, (LONG) ProcButton);第四种:不用Hook
    在一个对话框的OnInitDialog中枚举它的所有子窗体
    例如用下面两句来实现
    hWnd=GetWindow(hDlg,GW_CHILD); 
    hWnd=GetWindow(hWnd,GW_HWNDNEXT);
    对每个子窗体进行子类化处理,处理过程同第二种与第三种。
      
    第五种:如果是在XP下运行,可以使用manifest,也就是如下的一个XML文件
    <?xml  version="1.0"  encoding="UTF-8"  standalone="yes"?>  
    <assembly  xmlns="urn:schemas-microsoft-com:asm.v1"  manifestVersion="1.0">  
    <assemblyIdentity  
           name="Microsoft.Windows.XXXX"  
           processorArchitecture="x86"  
           version="5.1.0.0"  
           type="win32"/>  
    <description>Windows  Shell</description>  
    <dependency>  
           <dependentAssembly>  
                   <assemblyIdentity  
                           type="win32"  
                           name="Microsoft.Windows.Common-Controls"  
                           version="6.0.0.0"  
                           processorArchitecture="x86"  
                           publicKeyToken="6595b64144ccf1df"  
                           language="*"  
                   />  
           </dependentAssembly>  
    </dependency>  
    </assembly>  
    把它存为 应用程序名.manifest,放到和应用程序对应的目录下
    或者把它作为资源类型为24的资源编译进应用程序中。
    这样程序在XP下就自动拥有了XP的风格。第六种:使用第三方的库skinmagic等实现换肤。
    第七种:用第三方应用程序给整个windows换肤。以上七种方式各有优缺点。我在使用过程中也遇到不少问题,现在一一道来,希望和大家共同解决问题。
    先排除几种不准备深入探讨的方式:
    第五种manifest方式 最快速和简洁,但是功能有限,存在严重的平台限制,
    不过好处在于应用程序可以和windows共一种风格。
    第六种skinmagic方式 使用起来很简单,定制性也不错,如果他肯开源,并且能在贴图之外提供超强的自绘方式,
    那么可以称得上是换肤功能的终结者,否则,对开发者来说,意义并不大
    第七种, 属于自娱性质的,也就不多说了。
    第一种, 直接使用现成的类,属于很常见的一种用法,一般来说使用上不会出什么问题,缺点就不说了,
    如果这种方式让我满意,我就不必发这篇帖子了。下面看看第二三四种:
    第二种是用HOOK+窗口类,实现起来比较方便,和做一个自绘控件的工作量其实是一样的。
    第三种是用HOOK+窗口过程,实现起来比较麻烦,需要自己处理一堆switch case, 自己转换消息参数,
    自己找地方维护一堆状态变量,工作量很大。
    第四种不用HOOK的方式,有个缺点:对被换肤的程序的源代码的修改比较多。当然,直接到进程中去找窗口句柄,
    然后子类化那么就不用源代码了,不过这样的话还不如用HOOK呢。
    实际上,HOOK机制和枚举窗体虽然过程不同,不过最终目的是一样的,都是为了子类化窗口。所以在此不去探讨孰优孰劣了。现在切入正题,谈谈在子类化过程中遇到的问题:
    一) 重复subclass的问题
    上面提到,子类化的两种方式:用窗口类或者用窗口过程。
    使用窗口类是从CWnd派生一个类,调用CWnd的protected函数SubclassWindow.
    可是如果正常使用一个窗口类(声明成员变量,加入DDX_Control),实际上在DDX_Control中也是是用了SubclassWindow的。
    假如为一个控件声明变量,而在Hook中又进行了子类化,结果会怎么样呢?
    答案是,程序崩溃或弹出消息框"不支持的操作"。
    因为SubclassWindow函数调用前是要先Attach到一个HWND上去的。重复的Attach看来是不允许。
    要避免程序崩溃也有办法:
    1. 只为控件声明一个指针变量,动态的去获取CWnd类的实例,但是这样就达不到换肤的目的了。
    2. 还有一种方法,经过我试验,如果两个SubclassWindow的调用位于不同的模块,例如一个位于exe,一个位于dll(我是通过exe中调用dll中的函数显示该dll中的对话框来测试的),那么就不会出现问题。在还没有找到更好的方法之前,这也姑且算是一种解决方法吧。但是如果使用窗口过程来子类化,就不存在重复subclass的问题了,只要小心处理,子类化无数次都没问题,但是对于复杂的自绘事件,在一个窗口过程中来写switch语句,好像很麻烦。
    我尝试过自己写一个新的SubclassWindow函数来尝试借用CWnd的窗口过程,这样就可以按照MFC的方式来写消息响应函数了。只可惜,最终还是无功而返,因为SubclassWindow不是虚函数,而CWnd的窗口过程是作为一个protected成员存在的。所以没法在外部借用MFC的消息机制。
    所以,自己写代码处理wParam和lParam看来在所难免。二) 子类化系统对话框的问题。
    系统的对话框和自己的对话框表现的总不一样。
    目前我还没有对所有的系统对话框进行测试。在MessageBox弹出的对话框中遇到的问题可以见我这一片帖子:
    http://community.csdn.net/Expert/TopicView1.asp?id=3103399
    在文件对话框中我遇到一个问题,子类化过的CStatic的背景好像没有重绘一样,照理说应该由CStatic的父窗体负责背景的。
    我在我的CStaticNew类中只重载了OnPaint,里面只处理文字和图标的绘制,背景的绘制留给父窗体完成。这样的处理在MessageBox和自己的AboutDlg中都没有问题,Static控件的背景就是父窗口的背景,可是在CFileDlg中背景就没有重绘了
    void CStaticNew::OnPaint() 
    {
    CPaintDC dc(this); // device context for painting
    // TODO: Add your message handler code here
    CRect rt;
    GetWindowRect(rt); // 绘制背景 dc.SetBkMode(TRANSPARENT);
    // 绘制文字
    CFont *pfont, * pOldFont;
    pfont = GetFont();
    if (pfont)
    pOldFont = dc.SelectObject(pfont); CString szTitle;
    GetWindowText(szTitle);
    dc.DrawText(szTitle, CRect(0, 0, rt.Width(), rt.Height()), DT_LEFT | DT_WORDBREAK ); if (pfont)
    dc.SelectObject(pOldFont);
    // 绘制图标
    if ((GetStyle() & SS_ICON) != 0)
    {
    dc.DrawIcon(0, 0, GetIcon());
    }
    // Do not call CStatic::OnPaint() for painting messages
    }三) 类名的识别问题
    到现在为止,我所使用的子类化方法都是基于GetClassName这个函数获得窗口类名,再根据用spy++所得到的知识,
    如"#32770"表示对话框,"ToolbarWindow32"是工具栏,等等。但是窗口类名是可以在创建时任意指定的呀,
    而像CMainFrame的类名根本就不能够确定,例如记事本主窗体的类名是"Notepad",写字板主窗体的类名是"WordPadClass"
    这样的话,子类化如何去进行呢。真想知道windows是怎么做的,skinmagic又是怎么做的。目前主要就是这三个问题了。
    希望大家能展开讨论,给出一个换肤的完善的解决方案
      

  2.   

    你要的是不是这个?
    http://community.csdn.net/Expert/topic/3159/3159620.xml?temp=.4759333
      

  3.   

    子类化
    http://community.csdn.net/Expert/topic/3161/3161208.xml?temp=.1416284