Solutions for skin replacement and subclass

Source: Internet
Author: User
For applications Program Skin replacement and subclass. Below are some methods I have tried, taking the button in the subclass of caboutdlg as an example:

First: Use existing classes directly

1. Write a class cbuttonxp: Public cbutton {/*...*/}

Use messagemap to process messages of interest.

2. Use cbuttonxp instead of cbutton to declare the variable m_btn;

3. Add the following sentence to void caboutdlg: dodataexchange (cdataexchange * PDX:

 
Ddx_control (PDX, idb_button1, m_edit );

Or add it to initdialog ().

 
M_btn.subclassdlgitem (idb_button1, this );

These two effects are similar.

Method 2: Use a ready-made class in the hook

1. Write a class cbuttonxp: Public cbutton {/*...*/}

Use messagemap to process messages of interest.

2. Use setwindowshookex to install a HOOK:

 
G_hwndprochook =: setwindowshookex (wh_callwndproc, wndprochook, null,: getcurrentthreadid ());

3. process messages created and destroyed in the wndprochook window:

 
Lresult callback wndprochook (INT code, wparam, 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. Use getclassname in beginsubclassing to get the class name, for example, "button", and then use the cbuttonxp class for subclass.

 
Cbuttonxp pbutton = new cbuttonxp; Verify (pbutton-> subclasswindow (hwnd ));

Third, use the Window Process in the hook.

1. Write a button window by yourself

Wndproc oldproc; lresult callback procbutton (hwnd, uint umsg, wparam, 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. Same as the second
3. Same as the second

4. After obtaining the class name in beginsubclassing, use setwindowlong to subclass:

 
Oldproc = (wndproc) getwindowlong (hwnd, gwl_wndproc); setwindowlong (hwnd, gwl_wndproc, (long) procbutton );

Category 4: no hook

Enumerate all its subforms in oninitdialog in a dialog box, for example, using the following two sentences:

 
Hwnd = getwindow (hdlg, gw_child); hwnd = getwindow (hwnd, gw_hwndnext );

Perform subclass processing on each sub-form. The processing process is the same as that of the second and third forms.

Type 5: If you are running in XP, you can use manifest, which is the following XML file.

<? 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>

Store it as the application name. manifest, put it in the directory corresponding to the application, or compile it into the application as a resource type of 24. In this way, the program automatically has the XP style under XP.

Method 6: Use a third-party library skin ++ (www.uipower.com) for skin replacement

7. Use a third-party application to skin over windows (windowblinds)

The above seven methods have their respective advantages and disadvantages. I have encountered many problems in the use process. I hope to solve the problem together with you. First, we should exclude several methods not to be discussed in depth:

Fifth, the manifest method is the fastest and concise, but has limited functions and has severe platform restrictions. However, the advantage is that applications can share a style with windows.
Sixth, it is easy to use the third-party library skin ++ (www.uipower.com) for skin replacement. The customization is also good, and there are many skin types to choose from, it supports a wide range of languages and can be called the terminator of skin replacement. It is a good solution for companies that share software developers and pay attention to interfaces. His concept of skin replacement is quite new, in some cases, it is very unique. For example, it can be used to replace BCG. In some technical aspects, many similar products do not, such as the ComboBox scroll bar and the system dialog box (open or close DIALOG).
The seventh type is self-entertaining.
First, using the ready-made classes directly is a very common usage. In general, there will be no problems in use, and the shortcomings will not be mentioned. If this method is satisfactory to me, I will not post this post.

Let's take a look at the second three types:

the second method is to use the hook + window class for convenience, which is the same as the workload for creating a self-painted control.
The third type is the hook + Window Process, which is difficult to implement. You need to handle a bunch of switch cases, convert the message parameters yourself, and maintain a bunch of status variables in your own location, heavy workload.
the fourth method does not need to be hooked. There are many disadvantages: There are many modifications to the Source Code of the program to be replaced. Of course, go directly to the process to find the window handle, and then subclass, then you don't need the source Code , however, Hook is not recommended.
In fact, although the process of the hook mechanism is different from that of the enumeration form, the final purpose is the same, both for the subclass window. Therefore, we will not discuss the advantages and disadvantages of optimization. Now let's start with the question and talk about the problems encountered during the subclass process:
one is to repeat subclass. As mentioned above, there are two subclass methods: use a window class or a window process. The window class is used to derive a class from cwnd and call the protected function subclasswindow of cwnd. However, if you normally use a window class (declare a member variable and add ddx_control), subclasswindow is also used in ddx_control. If a variable is declared for a control and a subclass is made in the hook, what will happen? The answer is: The program crashes or the pop-up message box "unsupported operations ". Because the subclasswindow function must attach to an hwnd before calling it. Repeated attach statements are not allowed. There are also ways to avoid program crashes:

1. Declare only one pointer variable for the control and dynamically obtain the cwnd class instance, but this will not achieve the goal of skin replacement.
2. There is another method. After my experiment, if two calls of subclasswindow are located in different modules, for example, one is located in EXE, a dll is located in the DLL (I tested it by calling the function in the DLL in EXE to display the dialog box in the DLL), so there will be no problems. Before we can find a better solution, this is also a solution.
However, if the window process is used as a subclass, there will be no repeated subclass issues. As long as you are careful with it, it will be okay if the subclass is made countless times, but for complicated self-painting events, writing a switch statement in a window may seem troublesome.
I tried to write a new subclasswindow function to borrow the cwnd window, so that I could write the message Response Function in the MFC mode. Unfortunately, the final result is still unsuccessful, because subclasswindow is not a virtual function, and the window process of cwnd exists as a protected member. Therefore, the message mechanism of MFC cannot be used externally. Therefore, it is inevitable to write your own code to process wparam and lparam.
Zero is the problem of subclass system dialog box. The system dialog box is different from its own dialog box. Currently, I have not tested all system dialogs. In the dialog box popped up by MessageBox, you can see this post:

 
Http://community.csdn.net/Expert/To....asp? Id = 3103399

In the file dialog box, I encountered a problem. The subclass cstatic background does not seem to have been re-painted. It is reasonable to say that the background should be the responsibility of the cstatic parent form.
I only reload onpaint in my cstaticnew class, which only handles text and graph painting, and left the background painting to the parent form. This process is no problem in MessageBox and its own aboutdlg. The background of the static control is the background of the parent window, but the background in cfiledlg is not re-painted:

 void cstaticnew: onpaint () {cpaintdc DC (this); // device context for painting // todo: add your message handler code herecrect RT; getwindowrect (RT ); // draw the DC background. setbkmode (transparent); // draw text 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); // draw the icon if (getstyle () & ss_icon )! = 0) {DC. drawicon (0, 0, getIcon ();} // do not call cstatic: onpaint () for painting messages} 

So far, the subclass method I have used is based on the getclassname function to obtain the window class name. Based on the knowledge obtained by using spy ++, for example, "#32770" indicates the dialog box, "toolbarwindow32" indicates the toolbar, and so on. However, the window class name can be specified at any time during creation, and the class name like cmainframe cannot be determined at all. For example, the Class Name of the main form of notepad is "Notepad ", the Class Name of the main form is "wordpadclass ". In this case, how does one perform subclass. I really want to know how Windows works and how skinmagic works. These three problems are the main issues at present. We hope that you can discuss and provide a complete solution for skin replacement.
I wrote a simplified cwnd class to solve repeated subclass problems and simplify the window process, however, it does not support repeated subclass (that is, it can only be used for hwnd that is not quilt or that is cwnd subclass ).
Because you do not want to make it as complex as messagemap, the functions are limited: You must manually convert wparam and lparam, message processing cannot inherit, and multithreading is not supported. Easy to use:

 
Cwndnew * pwnd = new cwndnew; pwnd-> subclasswindow (hwnd );

When it is used up, remember to release it:

 
Pwnd-> unsubclasswindow (); Delete pwnd;

To expand (inherit) the functions, rewrite the following virtual functions:

Class cwndnew {public: cwndnew (); Virtual ~ Cwndnew (); bool subclasswindow (hwnd); void Merge (); protected: // virtualvirtual lresult windowproc (uint umsg, wparam, lparam); Virtual void presubclasswindow () {}; virtual void postunsubclasswindow () {}; protected: lresult prevwindowproc (uint umsg, wparam, lparam); hwnd m_hwnd; private: wndproc m_oldproc; static Map
 
  
M_map; static lresult callback staticwindowproc (hwnd, uint umsg, wparam, lparam );}; //////////////////////////////////////// /// // construction/destruction ///// //////////////////////////////////////// /// // Map
  
   
    
Cwndnew: m_map; cwndnew: cwndnew () {m_hwnd = NULL;} cwndnew ::~ Cwndnew () {assert (m_hwnd = NULL);} bool cwndnew: subclasswindow (hwnd) {m_map [hwnd] = This; Assert (m_hwnd = NULL ); m_hwnd = hwnd; // allows the derived class to perform initialization before subclass. presubclasswindow (); m_oldproc = (wndproc) getwindowlong (hwnd, gwl_wndproc); Assert (m_oldproc! = 0); setwindowlong (hwnd, slow, (long) staticwindowproc); Return true;} void cwndnew: unsubclasswindow () {setwindowlong (m_hwnd, gwl_wndproc, (long) m_oldproc ); postunsubclasswindow (); m_map.erase (m_hwnd); m_hwnd = NULL;} lresult callback cwndnew: staticwindowproc (hwnd, uint umsg, wparam, lparam) {cwndnew * pwnd = m_map [hwnd]; Assert (pwnd! = NULL); Return pwnd-> windowproc (umsg, wparam, lparam);} lresult cwndnew: prevwindowproc (uint umsg, wparam, lparam) {return callwindowproc (callback, callback, m_hwnd, umsg, wparam, lparam);} lresult cwndnew: windowproc (uint umsg, wparam, lparam) {return prevwindowproc (umsg, wparam, lparam );}

   
  
 

Concerning the subclass and the order of revocation, when using your own class or process subclass window, you need to deal with the sequence conflict with the MFC subclass. Assume that our class is cwndnew, so no matter which of the cwnd and cwndnew classes subclass a window, the final result of the synergy between the two is that the Window Process of the window is restored to the State before the subclass. First, do not process the wm_ncdestroy message during the hook process. Reason: If cwndnew is more subclass than cwnd, you will still process wm_ncdestroy for Hook reasons. If you revoke subclass, The cwnd class will not be able to be cleared. If you do not undo subclass, cwnd cannot restore the quilt window to its original state. During the hook process, you cannot call the sendmessage function to enable cwnd to process it first, and then you can process it yourself, because after sendmessage, the message will be intercepted by the hook again.
For the above reasons, it is a good choice to process wm_ncdestroy in the cwndnew message process, and so does MFC. Refer to the following code for explanation:

 
Case when: {lresult LRET; wndproc = (wndproc) getwindowlong (m_hwnd, callback); If (wndproc = cwndnew: staticwindowproc) {hwnd = m_hwnd; unsubclasswindow (); LRET = callwindowproc (m_oldproc, hwnd, umsg, wparam, lparam);} else {LRET = callwindowproc (callback, m_hwnd, umsg, wparam, lparam ); if (wndproc = (wndproc) getwindowlong (m_hwnd, gwl_wndproc) unsubclasswindow () ;}delete this; return LRET ;}

first, determine whether the wndproc of the window has changed. If not, it is the best. immediately remove the subclass and pass the message to the previous window process, no matter what the world is.
If a change occurs, other classes are subclass after cwndnew, and wm_ncdestroy is passed to cwndnew. This is easy to do. For example, if the message continues to pass forward, if wndproc changes again, it indicates that a previous window process has been processed and there is no need to cancel the subclass operation. This is also the case for the cwnd class of MFC.
another problem is how the embedded scroll bars of the edit, ListBox, listctrl, and other controls are replaced? The method described on the Internet is to hide the original one and then implement it on your own. The original shape can be seen in spy ++, but the scroll bar after skin ++ is changed does not know how to implement it? I have read the Article of coolsb, which can be used to skin the scroll bar, but does not support ComboBox.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.