Hi all, in this tutorial I will be teaching you the basics of skinning your Win32 GUIs(Graphical-User-Interface’s) using C\C++. I hope you enjoy and learn something from this tutorial.
Questions and answers:
Q. What do you mean by skinning?
If you have ever used Windows media player or Winamp, your notice they use fancy looking buttons and that they don’t use the standard rectangular windows your used to seeing with Windows. These tutorials aim to teach you how to make your standard Windows GUI application look more visually appealing.
Q. What are the drawbacks?
More of an annoyance than a drawback, should the end user of your application be using a non standard Windows theme, then your application wont match there theme, which may be a minus in some case’s.
Q. What will I need to know to keep up with this tutorial?
This tutorial assumes you have a decent knowledge of creating GUI’s using dialogs or the standard win32 API in C\C++. It also assumes you have a working knowledge of C\C++.
Ok lets get started with create non-rectangular windows.
Firstly there are two to ways to create non-rectangular windows, the first involves using regions and the second involves using layered windows, we can do two things using layered windows.
Firstly we can make whole windows transparent (see figure 1.1), secondly we can make a certain pixel colour in our window transparent\invisible (see figure 1.2), we can do this using the following windows API call:
Figure 1.1. Example of using transparent windows, note the calculator.
Figure 1.2. Example of making a single pixel colour invisible.
Note: the SetLayeredWindowAttributes(…) function is not compatible with Windows 98 and below.
What about regions?
I chose to keep this tutorial simple, regions are mainly used for backward compatibly should you use windows 98 or below. So I opted not to cover them in this tutorial.
Note: regions are also used to create non-rectangular buttons; I will cover this in part 3 of these tutorials.
Shall we start coding?
Not yet, were nearly there though, “No pain no gain”…
There are a few things you need understand before you can start using the SetLayeredWindowAttributes(…) function.
Firstly we need to load the function from User32.dll so our code will run on Win9x machines (otherwise export will fail and our application may crash).
But before we can load the function we must declare it:
#define LWA_COLORKEY 0x00000001
#define LWA_ALPHA 0x00000002
#define g_ColourKey 0xFF00FF // 0,0,255(pink) in RGB hex value
// declare the function
typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hWnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
lpfnSetLayeredWindowAttributes SetLayeredWindowAttributes;
// NOTE: this code goes above main() …
Now we need to import the function:
HMODULE hUser32 = GetModuleHandle(("USER32.DLL"));
// get pointer to function from DLL
SetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes)GetProcAddress(hUser32, "SetLayeredWindowAttributes");
// error importing function
if(SetLayeredWindowAttributes == NULL)
MessageBox(0, "Error importing function", "Error!", MB_ICONSTOP | MB_OK);
// NOTE: this code goes just after main()
So to recap, we have now declared and imported our function, time to start using it.
To use layered windows we need to give our window an extended window style, that being the following:
To set this style on our window we need to add the following code into our GUI’s WM_INITDIALOG or WM_CREATE message, depending if your using dialogs or not.
The code:
{
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
}
Done that? Good.
Now we need to actually to make our window layered, we can do this using the following code:
/* NOTE: put this code into the “if(SetLayeredWindowAttributes != NULL)” statement */
After you call this function any pixels with an RGB value of 0,0,255 will become invisible. If your using dialogs your WM_INITDIALOG message should look like this:
{
if(SetLayeredWindowAttributes != NULL)
{
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hwnd, g_ColourKey, 0, LWA_COLORKEY);
}
break;
}.
Ok so we have managed to make any pixel with an RGB value of 0,0,255 invisible, lets make this useful. We will need to find a nice background for our application; I chose to use this (see figure 1.3). Note I did not make it, I just edited it.
Figure 1.3, Example background.
Note the pink, remember our code makes pink pixels transparent, so we know have nice skin and a non-rectangular window… Well we should have, but there is a little bit more work to do.
We have declared our function, we have imported it, we have set our windows style to layered, we have told our window to make any pixels with an RGB value of 0,0,255 invisible… All we need to do now is load and draw the bitmap (our skin), which isn’t to hard. Save your skin as skin.bmp.
Ok so first we need to make the bitmap a resource, in your projects directory create a 2 files, one called Res.rc and the second called Res.h,. Res.rc is our resource file, we will make our bitmap a resource using this file, the 2nd is used to declare our resource so our main application can see it. Lets begin.
Open Res.rc and add the following code:
Ok our bitmap is now included as a resource in our application, we now need to declare it in Res.h.
Open Res.h and add the following code:
Now in your main project’s file add:
Into the includes section.
You now have your bitmap contained in your file as a resource, lets load it and start using it in our application.
To load a bitmap we need to use the LoadBitmap() function, which looks like this:
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpBitmapName // name of bitmap resource
);
and its description:
Ok so once we load our bitmap we need to get a handle to it, a bitmap has a special type of handle, which is known as “HBITMAP”. Lets see some code.
To load our bitmap:
HBITMAP hSkinMBmp = NULL; // note: this is a global variable
// OTHER CODE HERE
// ENTRY POINT (WinMain())
hSkinMBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_SKIN));
if(hSkinMBmp == NULL)
{
MessageBox(0, "Could not load Skin", "Warning", MB_OK | MB_ICONEXCLAMATION);
}
Pretty easy isn’t it… All that’s left is to draw our bitmap.
Scroll through your main source file until you reach your dialogs callback function.
We need to add a WM_PAINT message to the dialog, this message is sent every time the dialog redraws itself, so we need to tell the dialog to draw the bitmap, here is my WM_PAINT implementation:
{
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC dcSkin = CreateCompatibleDC(hdc);
GetObject(hSkinMBmp, sizeof(bm), &bm);
SelectObject(dcSkin, hSkinMBmp);
BitBlt(hdc, 0,0,500,500, dcSkin, 0, 0, SRCCOPY);
DeleteDC(dcSkin);
EndPaint(hwnd, &ps);
break;
}
once more thing we need to do is destroy our windows caption bars and make it non-resizable, I wrote this function to do it for us:
{
HWND hWnd = hwnd;
DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
dwStyle &= ~(WS_CAPTION|WS_SIZEBOX);
SetWindowLong(hWnd, GWL_STYLE, dwStyle);
InvalidateRect(hWnd, NULL, TRUE);
SetWindowPos(hWnd, NULL, 0,0,windowWidth, windowLentgh, SWP_NOMOVE | SWP_NOZORDER);
}
If you thought about what I just said you may be thinking how would I move my window if there was no caption bar? To do this we need to add one more message to our dialogs callback rouitine, see the code below:
{
PostMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
break;
}
This makes our window think we our clicking the caption bar no matter where we click on it, so it means we can drag the window by clicking anywhere on it.
Ok the last thing we need to do is clear up, this is simple, in your dialogs WM_CLOSE message add a call to “DeleteObject(hSkinMBmp);”.
Your WM_CLOSE message should look like this:
{
DeleteObject(hSkinMBmp);
EndDialog(hwnd, 0);
break;
}
Ok your now finally done, it was a lot of hard work I know, but you did it, you can now create non-rectangular windows and impress your friends with your better looking than there’s GUI (lol). Just in case you had trouble piecing together all the source in this tutorial, I wrote a complete example in Dev-CPP, you can find the download link below. Well I hope you enjoyed this tutorial, I know I missed a few details but I wanted to keep it simple, I will expand on what you have learnt here in part 2 of this tutorial. Any comments, questions just let me know, KOrUPt .
Source code download link:
http://rapidshare.com/files/66820612/Skinn...xample.rar.html