Post Message Post ThreadMessage and Post QuitMessage

The PostMessage() and the SendMessage() functions perform essentially the same task—they both send a message to the specified thread. However, the PostMessage() function returns from the call immediately, while the SendMessage() function waits for the recipient to respond. Both functions accept essentially the same arguments, so anything you can do with a SendMessage() call, you can do with a PostMessage() call.

You might wonder why Microsoft would include two calls with essentially the same functionality. There's a distinct disadvantage when using the PostMessage() call—you don't know if anyone received the message. It's best to use PostMessage() when you don't care if anyone receives or acts upon the call.

Note There are a number of superceded message functions that you'll still find in the Platform

SDK help and in the C/C++ header files. Never use these functions within an application because Microsoft doesn't support them and you don't know if they'll work in future versions of Windows. For example, the PostAppMessage() function has been replaced by the PostThreadMessage() function. Unfortunately, examples of these old functions prevail online and you even see them in the help files. Refer to the "Obsolete Windows Programming Elements" topic in the Platform SDK help file for a list of old functions and their replacements. This list isn't complete, but it's good place to start.

Both the PostMessage() and SendMessage() functions can accept special handles. However, one of these special handles works better with PostMessage() because it doesn't incur the delay that using SendMessage() would incur. You can use the HWND_BROADCAST handle to tell Windows to send a particular message to every accessible window. For example, you might use such a call to restore all of the windows as shown in Listing 4.1. (The example code appears in the \Chapter 04\C#\MinimizeAll and \Chapter 04\VB\MinimizeAll folders of the CD.)

Listing 4.1: Using the HWND_BROADCAST Handle to Call All Windows

// Used to send a system command message. [DllImport("User32.DLL")]

public static extern int PostMessage(IntPtr hWnd,

UInt32 Msg, Int32 wParam, Int32 lParam);

// The WM_SYSCOMMAND constant used to access the SC constants. public const Int32 WM_SYSCOMMAND = 0x112;

// The HWND_BROADCAST handle sends the message to all windows. public IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);

// A list of SC constants used for all types of system // command access.

public enum SystemCommand {

SC

SIZE =

= 0xF000

SC

MOVE =

= 0xF010

SC

MINIMIZE =

= 0xF020

SC

MAXIMIZE =

= 0xF030

SC

NEXTWINDOW =

= 0xF040

SC

PREVWINDOW =

= 0xF050

SC

CLOSE =

= 0xF060

SC

VSCROLL =

= 0xF070

SC

HSCROLL =

= 0xF080

SC

MOUSEMENU =

= 0xF090

SC

KEYMENU =

= 0xF100

SC

ARRANGE =

= 0xF110

SC

RESTORE =

0xF120

SC

TASKLIST =

0xF130

SC

SCREENSAVE =

0xF140

SC

HOTKEY =

= 0xF150

SC

DEFAULT =

= 0xF160

SC

MONITORPOWER =

= 0xF170

SC

CONTEXTHELP =

= 0xF180

SC

SEPARATOR =

0xF00F

private void btnTest_Click(object sender, System.EventArgs e) {

// Minimize all of the windows. PostMessage(HWND_BROADCAST, WM_SYSCOMMAND,

(Int3 2)SystemCommand.SC_RESTORE, 0);

As you can see, using PostMessage() with the broadcast handle is essentially the same as using SendMessage()—the main difference is that the function returns immediately. If you try using this code with SendMessage() in place of PostMessage(), you'll see a definite delay as SendMessage() waits for all of the windows to return a response.

This code has an interesting side effect. Not only does it restore all of the visible windows, but it restores all of the hidden windows as well. The resulting chaos might look unappealing, but I've actually learned about a few windows that don't appear in the Spy++ list, but do appear on screen after using this call. Log off and back on your machine to restore the screen—a reboot isn't necessary to hide the hidden windows again.

A second special handle accesses the Desktop. The HWND_DESKTOP handle enables you to send messages to the Desktop using either PostMessage() or SendMessage(). Here's the definition for HWND_DESKTOP.

// The HWND_DESKTOP handle sends message only to the Desktop, public IntPtr HWND_DESKTOP = new IntPtr(0);

The AddFontFile() method replicates the functionality of the AddFontResource() function of the Win32 API. Both enable you to add private fonts to your application without registering them within Windows first. Either form of the function works fine if you want to share your registered font with every other application running in Windows. However, what happens if you want to keep your special font truly secret? You need to use the AddFontResourceEx() function. This Win32 API function includes flags that keep your font secret and prevent other applications from enumerating the font. However, no matter which function you use to load a font, you still have to tell everyone that there was a change to the font table, which means sending a message. The code in Listing 4.2 shows how to load the VisualUI.TTF font that appears on most hard drives with Visual Studio .NET (among other applications) installed.

Note For best viewing results, run this example application outside of the debugger. If you load the font while within the debugger, it tends to stay in memory until you exit the Visual Studio IDE. You can still follow code execution within the debugger to see how the various calls work—the only problem is that the font won't unload. That's because Windows associates the font with the Visual Studio IDE instead of the application since the application is executing within the debugger environment.

Listing 4.2: Use the AddFontFileEx() Function to Load Fonts Privately.

// The function required to add a private font resource. [DllImport("GDI3 2.DLL")]

public static extern int AddFontResourceEx(String lpszFilename,

Int32 fl, IntPtr pdv);

// The function required to remove a private font resource. [DllImport("GDI3 2.DLL")]

public static extern bool RemoveFontResourceEx(String lpszFilename,

Int32 fl, IntPtr pdv);

// Flags used to define how the private font resource is loaded, public const Int32 FR_PRIVATE = 0x10;

public const Int32 FR_NOT_ENUM = 0x20;

// Used to send a system command message. [DllImport("User3 2.DLL")]

public static extern int PostMessage(IntPtr hWnd,

UInt32 Msg, Int32 wParam, Int32 lParam);

// The WM_SYSCOMMAND constant used to access the SC constants, public const Int32 WM_FONTCHANGE = 0x0 01D;

// The HWND_BROADCAST handle sends the message to all windows, public IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);

System.Drawing.Text.PrivateFontCollection PFC;

private void btnLoadFont_Click(object sender, System.EventArgs e) {

// Determine which action to take.

int Result = 0; // Results of loading the font.

// Load the desired font. Result = AddFontResourceEx(

"D:\\Program Files\\Microsoft Visual Studio .NET\\Common7\\IDE\\VisualUI.TTF",

FR_PRIVATE,

IntPtr.Zero);

// Display an error message if necessary.

MessageBox.Show("The font failed to load for some reason.", "Load Failure", MessageBoxButtons.OK, MessageBoxIcon.Error);

// Change the button caption. btnLoadFont.Text = "Unload Font";

// Tell everyone we've loaded a new font. PostMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);

bool Result; // Results of loading the font.

// Load the desired font. Result = RemoveFontResourceEx(

"D:\\Program Files\\Microsoft Visual Studio .NET\\Common7\\IDE\\VisualUI.TTF",

FR_PRIVATE,

IntPtr.Zero);

// Display an error message if necessary.

MessageBox.Show("The font failed to unload for some reason.", "Unload Failure", MessageBoxButtons.OK, MessageBoxIcon.Error);

// Change the button caption. btnLoadFont.Text = "Load Font";

// Tell everyone we've loaded a new font. PostMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);

private void btnDisplayDialog_Click(object sender, System.EventArgs e) {

// Display the font dialog. dlgFont.ShowDialog(this);

As you can see, the sample code can load and unload the VisualUI.TTF font. The AddFont-ResourceEx() and RemoveFontResourceEx() function calls load the font publicly if you don't specify any flags or privately if you specify the FR_PRIVATE flag shown. Notice the use of PostMessage() in this example. You must tell other windows about the new font or they won't recognize it (this includes other windows in the current application). The WM_FONTCHANGE message doesn't require any parameters—the other windows will create a fresh enumeration of the font list if necessary.

If you click Display Fonts immediately after loading the example application, you'll notice that the VisualUI is missing from the list. Load the font with the code shown in Listing 4.2 and you'll see the VisualUI font in the list as shown in Figure 4.6.

Figure 4.6: Loading the VisualUI font using the default code displays it in the Font dialog box.

There are some interesting changes you can make to the code in Listing 4.2. For example, try the example with a SendMessage() in place of a PostMessage() call and you'll see that the time differential can be significant. Try running the call without sending the WM_FONTCHANGE message at all and you'll notice that not even the local application will notice it in some cases (the change becomes intermittent). Try loading the font publicly (without any flags). Other applications such as Word will contain the font in their font list. Reboot the machine after a public load to ensure that the font is removed from memory. Now, try using the FR_NOT_ENUM flag when loading the font and you'll notice that only the test application displays the font.

Note The AddFontResourceEx() function, like many of the special functions in the book, isn't supported by Windows 9x systems, including Windows Me. In addition, the fonts you add using this function are only accessible for the current session—they're unloaded as soon as the user reboots the machine. As you can see, it's essential to check the Platform SDK documentation for limitations on using Win32 API functions directly.

The VisualUI.TTF font is interesting for developers, but almost useless for users, so it makes a perfect private font. Figure 4.7 shows what this font looks like. As you can see, it contains the special font Microsoft uses for drawing the VCR-like controls on screen. It also contains some unique graphics such as the pushpin used in some areas of the Visual Studio IDE. Having access to these special graphics can save development time.

B 1*>m4JI <! •» !>|rl

TiBiS

VisualUI (TrueType)

1. ., , i — \i i "i VI itV^r ;r i ^111 Htrrrrti rrTM

«m il 'fjni rwwrwl

in

nnn

Figure 4.7: The VisualUI font may not have much to offer users, but it can save some drawing time for developers.

There are several variations on the PostMessage() function. One of the more interesting messages is PostThreadMessage(). This form of the function enables you to post a message to the threads of the current application. You still provide Msg, lParam, and wParam arguments. However, instead of a window handle, you need to provide a thread identifier. The PostThreadMessage() function has several constraints, including a special constraint under Windows 2000 and Windows XP—the thread identifier must belong to the same desktop as the calling thread or to a process with the same Locally Unique Identifier (LUID).

You'll almost never need to use the PostQuitMessage() function. All .NET languages have a built-in method to perform this task and you're better off using it whenever possible. The PostQuitMessage() tells Windows that your application is going to exit. It includes an exit code that an external application can use to determine the exit status of your application (generally 0 for a successful exit). It's important to know about this function because it does come in handy in certain rare circumstances—mainly within wrapper DLLs. You can use this message to force an application exit when catastrophic events occur. The only time you should consider using this message is if the application is hopelessly frozen and you still want to provide some means of exit (so the user doesn't have to perform the task manually). In short, for the .NET developer, this is the message of last resort.

Was this article helpful?

0 -1

Post a comment