(Go to my own post posted on the Forum 14:52:52 and ask a question on the VC/MFC/interface)
Skin replacement and subclass of applications. Below are some methods I have tried,
Take 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 g_hWndProcHook =: SetWindowsHookEx (WH_CALLWNDPROC,
WndProcHook, NULL,: GetCurrentThreadId (); install a hook
3. process messages created and destroyed in the WndProcHook window.
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. 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 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. Same as the second
3. Same as the second
4. After obtaining the class name in BeginSubclassing, use SetWindowLong to subclass it.
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, use the following two sentences to implement
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 and 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, such as skinmagic, for skin replacement.
7. Use a third-party application to skin over windows.
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:
The fifth manifest method is the fastest and concise, but has limited functions and has severe platform restrictions,
But the advantage is that the application can have a style with windows.
The sixth skinmagic method is very simple to use, and the customization is also good. If he is willing to open the source, and can provide a super self-painting method outside the texture,
It can be called the terminator of the skin swap function. Otherwise, it is of little significance to developers.
The seventh type is self-entertaining.
First, using the ready-made classes directly is a very common usage. Generally, 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 convenient implementation, which is the same as the workload for creating a self-painted control.
The third method is to use the hook + Window Process, which is difficult to implement. You need to handle a bunch of switch cases and convert the message parameters yourself,
It takes a lot of work to maintain a bunch of state variables in your own location.
The fourth method, without hook, has a disadvantage: the source code of the program to be replaced is modified a lot. Of course, go directly to the process to find the window handle,
Then, the source code is no longer needed for subclass, but it is better to use hook in this case.
In fact, although the process of the hook mechanism is different from that of the enumeration form, the final purpose is the same, all of which is to subclass the 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:
1) Duplicate subclass issues
As mentioned above, two subclass methods are used: window class or 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 way, 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.
2) Questions about the subclass system dialog box.
The system dialog box is different from your 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/TopicView1.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 processing 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 here
CRect rt;
GetWindowRect (rt );
// Draw the background
Dc. 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
}
3) Class Name Recognition
So far, the subclass method I have used is based on the GetClassName function to obtain the window class name, and 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,
The class name like CMainFrame cannot be determined at all. For example, the Class Name of the main form of Notepad is "Notepad", and the class name of the main form of the board 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.
I hope you can discuss the problem and provide a perfect solution for skin replacement.