Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Quick and Dirty Window Transparency

0.00/5 (No votes)
19 Nov 2003 1  
A simple way to add transparency to windows that won't fail on older versions of Windows.

Introduction

One of the newer features of Windows is the ability to use what's known as compositing to draw Windows onto the desktop. Put simply, this makes it possible to make a window transparent, so that the background can be seen through the window. While this can be a visual distraction, it can also be used occasionally to provide useful feedback to the user. In Windows 2000 and XP window transparency is used during some kinds of drag and drop operations to render icons as they are dragged across the screen; These operating systems also use window transparency to achieve menu fade effects.

This article aims to provide an introduction to using layered windows for transparency, as well as a simple function that allows transparency to be used on newer versions of Windows, without breaking compatibility with Windows98. Hopefully, with the technique and code presented in this article, transparency effects can be added to your application with only one or two new lines of code, and no alteration to existing drawing logic. Before I continue, let me extend my sincere thanks to other articles that provided the inspiration for this one.

Making a Window Transparent

The way Windows 2000 and XP support transparent windows is via an extended style bit, WS_EX_LAYERED. When this bit is set, calling SetLayeredWindowAttributes on a window causes Windows to redirect all window paint operations to an off-screen bitmap. Windows then can use this off-screen bitmap to draw the window onto the screen using transparency effects. (It's also possible to take more control over the layered window update process by calling UpdateLayeredWindow, but that is beyond the scope of this article. For more details, see the Microsoft technical article Layered Windows: A New Way to Use Translucency and Transparency Effects in Windows Applications .)

The most straightforward approach to adding transparency to your windows is to simply set the style bit and call SetLayeredWindowAttributes. The problem with this approach is that it will fail on Windows 98. An application written this way will attempt to link to SetLayeredWindowAttributes at load time; On Windows98, since the API call does not exist, the linkage will fail, the program will not run, and a cryptic Windows error message will be displayed to your customers in a MessageBox. A much safer approach is to use LoadLibrary and GetProcAddress to control the process of linking to the API manually. The code that does this is a common Windows idiom:

typedef DWORD (WINAPI *PSLWA)(HWND, DWORD, BYTE, DWORD);

static PSLWA pSetLayeredWindowAttributes = NULL;
static BOOL initialized = FALSE;


BOOL MakeWindowTransparent(HWND hWnd, unsigned char factor)
{
   /* First, see if we can get the API call we need. If we've tried
    * once, we don't need to try again. */
   if (!initialized)
   {
      HMODULE hDLL = LoadLibrary ("user32");

      pSetLayeredWindowAttributes = 
         (PSLWA) GetProcAddress(hDLL, "SetLayeredWindowAttributes");

      initialized = TRUE;
   }

   /* ... elided ... */
}

This code programmatically attempts to link to SetLayeredWindowAttributes, and if it fails, leaves pSetLayeredWindowAttributes set to NULL. Otherwise, pSetLayeredWindowAttributes is a pointer to the API function. This enables the API to be called, while avoiding the problems associated with linking to the API statically.

Using the MakeWindowTransparent Function

The library code attached to this article is designed to be as easy to develop with as possible. After adding the files MakeWindowTransparent.cpp and MakeWindowTransparent.h to your project, you're almost all the way there. The only major sticking point is that MakeWindowTransparent.cpp relies on some Windows constants that are only defined in Windows 2000 and later versions of the operating system. To gain access to these constants, you must either make the following preprocessor definition before stdafx.h, or modify the corresponding definition already in stdafx.h. While neither of these are likely to break your build, both of them are global changes, and should be tested accordingly, no matter which one you pick.

#define _WIN32_WINNT 0x0500

Once this is done, all that remains to be done is include MakeWindowTransparent.h in your source files and call MakeWindowTransparent on the windows that are to be made transparent. For the sake of convenience, there are two definitions of the function, one takes an MFC CWnd, one takes a standard Windows window handle:

bool MakeWindowTransparent(HWND hWnd, BYTE factor);
bool MakeWindowTransparent(CWnd *w, BYTE factor);

The other parameter to MakeWindowTransparent, factor, determines how transparent the window will become. A value of 0 will make the window completely transparent, a value of 0xFF will make the window completely opaque. It's also worth pointing out that there's no restriction on calling MakeWindowTransparent for a given window more than once. This can be done to vary the transparency of a window as a program executes.

The return value of MakeWindowTransparent is true if the function succeeded, and false if it failed. Failure can be reported when the function is called on versions of Windows that do not support layered windows or transparency. In the event that the call to make the window transparent fails, one way to gracefully degrade might be the API function SetWindowRgn It can be used on earlier versions of windows to set a window's shape to be non-rectangular. I've used it successfully in the past to good effect.

Working Without MFC

Despite its support of MFC's CWnd, this little library does not have any real dependencies on MFC. In non-MFC applications, this function is all you need:

/**************************************************************
 * MakeWindowTrasparent(window, factor)
 *
 * A function that will try to make a window transparent
 * (layered) under versions of Windows that support that kind
 * of thing. Gracefully fails on versions of Windows that
 * don't.
 *
 * Returns FALSE if the operation fails.
 */

typedef DWORD (WINAPI *PSLWA)(HWND, DWORD, BYTE, DWORD);

static PSLWA pSetLayeredWindowAttributes = NULL;
static BOOL initialized = FALSE;


BOOL MakeWindowTransparent(HWND hWnd, unsigned char factor)
{
   /* First, see if we can get the API call we need. If we've tried
    * once, we don't need to try again. */
   if (!initialized)
   {
      HMODULE hDLL = LoadLibrary ("user32");

      pSetLayeredWindowAttributes = 
         (PSLWA) GetProcAddress(hDLL, "SetLayeredWindowAttributes");

      initialized = TRUE;
   }

   if (pSetLayeredWindowAttributes == NULL) 
      return FALSE;

   /* Windows need to be layered to be made transparent. This is done
    * by modifying the extended style bits to contain WS_EX_LAYARED. */
   SetLastError(0);

   SetWindowLong(hWnd, 
                 GWL_EXSTYLE , 
                 GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);

   if (GetLastError())
      return FALSE;

   /* Now, we need to set the 'layered window attributes'. This
    * is where the alpha values get set. */
   return pSetLayeredWindowAttributes (hWnd, 
                                       RGB(255,255,255), 
                                       factor,
                                       LWA_COLORKEY|LWA_ALPHA);
}

But I don't want to modify _WIN32_WINNT !

If the thought of making a global change to your Windows header includes makes you queasy, it can be avoided by making the definitions yourself in MakeWindowTransparent.cpp:

#define WS_EX_LAYERED           0x00080000    

#define LWA_COLORKEY            0x00000001
#define LWA_ALPHA               0x00000002

Obviously, re-declaring Windows API constants is really bad form, but it is a way around making such a far reaching change to your build, if you don't have the time to do it right.

Conclusion

Window transparency is a powerful effect when used with discretion. If the transparent window is too complex, as in a terminal window, it can create serious readability problems for your customers. That said, transparency can provide useful visual feedback as well as a more modern style. Not only is it used successfully in Windows 2000 and XP, it's also well supported in Windows Forms, Mac OS X, and will be a key part of Avalon, the graphics system used in upcoming versions of Windows. I hope that this article, and the associated code, is helpful in bringing this capability to your existing MFC and Win32 applications.

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