Click here to Skip to main content
Click here to Skip to main content

SkinX, A framework of a skin plug-in package

, 16 May 2004
Rate this:
Please Sign up or sign in to vote.
Introduces the framework of a skin plug-in implementation.

Sample Image - SkinX.jpg

Introduction

I'm not sure what kind of technology they used in those commercial skin components, but this article will give you one implementation of such a skin framework. The code is part of an incomplete project. The project was finally given up because of some business reason and never got used. The original purpose of the project was to create different COM objects with same interface, so that the host application can pick one of those objects and get a different look and feel.

The attached source code includes a small subset of a Mac OS X look & feel object and a demo host application. The application simply creates the skin object and calls InstallHook method, then all the buttons in the application gets a Mac look & feel. I removed other code from the COM object implementation to make it simple. Anyway, this article is aimed to explain the framework, not to give a ready-to-use project.

Skin Theory

A skin component needs to modify the default look & feel of the application windows, like buttons, combo boxes, etc. To do this, a custom window procedure must be plugged into the window class. The Windows hook functionality allows us to do that. Calling SetWindowsHookEx with WH_CALLWNDPROC as hook ID allows us to monitor all the Windows messages before they are sent to the destination window procedure, so we can kidnap certain messages and process them, and then pass it through to the original window procedure, or even eat some messages.

After the hook is installed, all Windows messages will go into our hook procedure. But choosing which message we must process is not an easy job. There are thousands of messages related with different kinds of windows. For a single message, we must process it in different ways for different kinds of windows.

There is a lot to talk about hooks and message processing, but let's just skip those nonsense and give the solution directly. We define different classes for each different kind of window. For example, we define a CMacButton class to wrap the window procedure, which will give a button window the look & feel of Mac OS. And in our hook procedure, we just process one message: WM_CREATE. Then we check to see if it is a button window which is going to be created. If it is, we create a CMacButton instance, and use SubclassWindow to hook our window procedure to the destination window. Then, the CMacButton instance will take over the responsibility of processing window messages.

To conclude, we use SetWindowsHookEx to kidnap WM_CREATE message. For each window that we hope to change its look & feel, we create an object, and use SubclassWindow to connect the object with the window.

Infrastructure

OK, whether you understand the above or not, I'm finished with the theory. But there's a long way from theory to executable code. The framework, or infrastructure, is the key part of this project. The SkinX framework borrows a lot form ATL/WTL library. Let's take a look at the UML modal first:

The Widget Classes

As we described above, we must define different window classes for each widget, such as CMacButton, CMacEdit. These classes process various messages for different kinds of windows. All these window classes, we called widget classes, are derived from a common base class: CWidgetHook, and CWidgetHook is derived from CWidgetHookBase. Let's dig into these classes.

CWidgetHookBase just defines the interface, which only includes one method: Install. The framework calls Install to hook the instance to the destination window. Install is implemented in CWidgetHook class. CWidgetHook also defines some methods which should be overridden in the derived class, they are:

 ////////////////////////////////////////////////////////////////////
 // overrides, we don't need virtual member since we use the ATL way
 void Initialize() {}; //instance initialize
 void Finalize() {}; //instance finalize
 static void InitializeClass() {}; //class initialize
 static void FinalizeClass() {}; //class finalize

The first two methods get called every time a single instance is created or destroyed. The last two methods get called when the first instance of a class is created or when the last instance of a class is destroyed. The reason of defining the last two methods is that for each class, there are some common resources needed. For example, all checkboxes need some bitmaps. To maintain a copy of such resources for each instance is not efficient. So, it would be better to use a static member to keep these resources, and use static member functions to initialize them and release them.

CWidgetHook also has a static member: m_lRef, which is a reference counter, so that the class knows how many instances exist. When the last instance is destroyed, FinalizeClass will be called to clean static members, so that memory footprint is reduced. CWidgitHook:OnFinalMessage deletes the instance itself, so we don't need to worry about cleanup.

CWidgetHook is a C++ template class, one template parameter is the derived class. This concept is borrowed from ATL. The other parameter is a CWindow compatible class, which could be a WTL wrapper class, so that we can use WTL wrapper method in derived class. Such design makes the implementation of a window procedure less pain. Actually, writing a CWidgetHook derived class is as easy as writing a WTL window class. ATL windowing and message map macros help a lot in writing and maintaining code.

However, the hardest thing in writing a skin package is still to write these CWidgetHook derived classes. For most such classes, WM_PAINT message must be processed to give the window another look. Some other message should also be caught so that you know the state of the window. The attached demo project includes an implementation of CMacButton class, which actually implements the Mac look & feel for buttons, checkboxes and radio, since all these three kinds of widgets have the same window class name: Button. Check the code yourself for how to write a widget class.

The Reflector Hook

The reason a reflector exists is that lots of widget window messages are just sent to their parent window, not themselves. A reflector makes the parent window send back these messages to the original widget window. ATL and MFC both have reflector support. Here, we use our own implementation of reflector to avoid conflict.

The Widget Factory

When all the widget classes are ready, we need a way to map these classes with the widget window, and install them to the destination widget window. CWidgetFactory implements the abstract factory pattern. It uses the widget window class name to create a corresponding instance. CreateWidget method takes the class name of the destination window, and returns a CWidgetHookBase interface. CreateWidget method is abstract and must be implemented in the derived class.

The Hook Procedure

With the above infrastructure, the Hook procedure becomes very simple and clear. It just checks the destination window class name, asks Widget factory for a CWidgetHookBase interface, and calls the Install method on the interface to install the class.

How to extend

The framework is designed for easy extending. To write a skin package, first you need to derive a series of classes from CWidgetHook, implement them to override the default behavior to change the look & feel of the destination window. Then you create your Widget Factory by deriving a class from CWidgetFactory, implement the CreateWidget method to create instance by window class. Then you are almost done, the COM object code and the Hook procedure are the same as in the demo project.

That's it

Well, that's almost all about this framework. It's fully extensible, and takes efficiency as a main concern. Take some time to understand this framework and begin to write some creative code, a powerful skin package could be created.

That's what I contribute here. It's not perfect. If you find something that should be improved, tell me please. If you find it useful, then use it. If you create something wonderful with this, please tell me. I won't claim anything about this, but it will make me happy. And if I can get involved in some wonderful project, that will be exciting.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Neil Yao

China China
I'm a chinese programer living in Shanghai, currently working for a software company whose main business is to deliver computer based testing. Software simulation for computer based testing and certifications is my main responsibility in this company. Execpt for software development, I like out-door activities and photography. I am willing to make friends in China and all over the world, so contact me if you have anything in common with meSmile | :)

Comments and Discussions

 
General Pinmemberby67486821221-Jun-11 21:07 
QuestionCan you Open the SkinX.dll's source code? Pinmemberexyeuer1-Sep-06 1:56 
AnswerRe: Can you Open the SkinX.dll's source code? Pinmemberfrontier195-Sep-06 15:53 
GeneralCool framework! Pinmemberzhk.tiger5-Dec-05 16:18 
GeneralRe: Cool framework! Pinmemberzhk.tiger9-Dec-05 17:04 
GeneralNot handling tab well PinmemberKuniva19-Aug-05 5:30 
QuestionQuestion: does anyone implemented skinning for combobox and other controls??????? PinmemberIstvan Nagy1-Aug-05 2:43 
AnswerRe: Question: does anyone implemented skinning for combobox and other controls??????? PinmemberYao Zhifeng1-Aug-05 15:11 
Generaluskin a nother personal free skin lib Pinmemberthirdwolf26-Jun-05 17:25 
QuestionQuestion: CheckBox as PushButton not handled? Pinmemberbalbi2129-May-05 2:02 
GeneralCompilation erors Pinmemberomris17-May-05 4:48 
GeneralBug report: Not re-paint when button state changed PinmemberHanzo6-Apr-05 23:55 
QuestionCould it work with ATLDialogs from an ATL DLL Project? PinsussBjoern M. Albrecht6-Apr-05 9:23 
Hi there - I'm fascinated from the Possibilities this framework could have - one question:
 
Is it possible to integrate this Skin procedure to an ATL DLL Project, where I haven't Information about the Main Thread?
 
E.g. I have a DLL that has several ATL Dialogs - but the Msater Application could be VB or Delphi or somewhat... so I don't know, where I can get the Thread ID for the Application. Also I want only to cahnge the look of the ATL Dialogs of the DLL Dialogs, not the Dialogs of some calling Applicaiton also - if the DLL calls and the main application runs in the same thread...
 
Does anybody know, how this Skin DLL could be integrated into a DLL project?
 
First question: In which Start procedure? DLLMain instead of WinMain?
Also a question:What is with new ATl Version: Should the COM Init of Skin bevor or after the call of _AtlModule.DllMain() (=return value of
Projects DLLMain).
I think, the _AtlModule is something differnet of _Module in older ATL DLL Projects and of course something different of _Module in ATL Applicaitons?
 
I will be happy, if someone could help me,
 
Best regards,
Bjoern Albrecht
(sorry for my bad english)
GeneralGood work! Thank U! PinmemberDavidRipple19-Aug-04 21:32 
GeneralRe: Good work! Thank U! PinsussAnonymous5-Dec-04 20:38 
Questionhi,Yao Zhifeng !Can you tell me you email or msn number? Pinmemberhappycampus18-May-04 23:54 
AnswerRe: hi,Yao Zhifeng !Can you tell me you email or msn number? PinmemberYao Zhifeng19-May-04 2:40 
QuestionAny VC6 version by chance? Pinmemberprcarp17-May-04 2:21 
AnswerRe: Any VC6 version by chance? PinmemberYao Zhifeng17-May-04 2:28 
GeneralRe: Any VC6 version by chance? Pinmemberprcarp17-May-04 2:42 
GeneralRe: Any VC6 version by chance? PinmemberYao Zhifeng17-May-04 2:54 
GeneralSeems you're not the first :) PinmemberKochise17-May-04 3:36 
GeneralRe: Seems you're not the first :) Pinmemberll20021-Aug-04 6:30 
GeneralDoesn't work under Win2000 PinmemberChrLipp17-May-04 0:01 
GeneralRe: Doesn't work under Win2000 PinmemberYao Zhifeng17-May-04 2:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.2 | Last Updated 17 May 2004
Article Copyright 2004 by Neil Yao
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid