Windows Management

   

Windows Management

Let's start off by examining the Windows Management functional area. Windows applications are created and managed through a majority of the functions provided by the user library ( user32.dll ). The user library interface includes services for window and menu management, dialogs, messaging, message boxes, mouse and keyboard access, and other built-in controls.

Before we dive any deeper, it's important to first understand the concept of a window. A window acts as the interface between the user and the application. At least one window, called the main window, is created by a Windows application. Applications can create additional windows as well. A window's primary purpose is to display information and receive input from a user.

Windows Management functions are used to control the aspect of the windows created and used by an application. The main window receives mouse and keyboard input through messages . These messages are passed between window resources through Windows Management support. Windows Management functions also provide the capability for an application to display icons, menus , and dialog boxes that receive and display additional user information.

NOTE

Some type of handle identifies all window resources. Examples of entities with handles include modules, processes, threads, frame windows, menus, bitmaps, icons, cursors , and color space. A handle is always represented by a 32-bit unsigned value and is an extremely important aspect of the Win32 API. Handles provide the means and mechanism to control and manipulate objects, such as a window and children processes, and they provide the capability to pass input to other applications through message callbacks . You'll find that a number of the Win32 API examples provided in this chapter make use of handles.


The functions in Table 14.1 provide a sampling of some of the more popular Windows Management API routines used to create and manage windows. These routines are accessed simply by including the windows.h header file or Borland's vcl.h header file within your application's source file.

Table 14.1. Common Windows Management Functions

Windows Management

Common Functions

Description

CascadeWindows()

Cascades the specified windows or the child windows of the specified parent window.

CloseWindow()

Minimizes, but does not destroy a specified window.

CreateWindow()

Creates an overlapped , pop-up, or child window.

DestroyWindow()

Destroys a window. The system responds by sending a WM_DESTROY message to a specified window.

EnableWindow()

Enables or disables mouse and keyboard input to a specified window or control.

EnumWindows()

Enumerates by looping through each top-level window on the display and passing the handle of each window individually to an application-defined callback function.

EnumWindowsProc()

Used by the EnumWindows() function. This is the application-defined callback function that EnumWindows() uses to pass the handles of top-level windows.

FindWindow()

Retrieves the handle to the top-level window in which the class name and window name match the specified strings.

FindWindowEx()

Retrieves the handles of available active windows. Similar to FindWindow() , but also provides support for locating child windows.

GetWindowRect()

Retrieves the screen coordinates of the specified window.

GetWindowText()

Retrieves the title bar caption of the specified window.

MessageBeep()

Plays a predefined waveform sound asynchronously.

MessageBox()

Creates a small dialog box containing an application-defined title and message.

MoveWindow()

Moves the location and size of a specified window.

PostMessage()

Directs a specified message to another window and returns immediately.

RegisterWindowMessage()

Defines a new windows message that is guaranteed to be unique.

SetWindowText()

Modifies the text of the title bar for the specified window.

SendMessage()

Directs a specified message to another window and waits until the message has been processed .

ShowWindow()

Sets the show state of the specified window. Show states include hiding, maximizing, minimizing, restoring, and activating a window.

TileWindows()

Tiles the specified windows or the child windows of the specified parent window.

WinMain()

Called by the system as the initial entry point for a Win32-based application.

Many other Windows Management routines exist that we have not identified. In fact, there are more than 640 routines provided by the user32.dll within current versions of Windows. Use Borland's impdef command-line tool or the DLL LIB Util utility, described in the Tip section earlier, to view the full list of available functions within user32.dll .

Let's now create an example application in C++Builder that uses some of the Windows Management routines identified in Table 14.1.

Windows Management Example

In the Chapter 14 source directory on the CD-ROM, there is a project called WinManUtil , which is illustrated in Figure 14.4. This project contains a comprehensive sample that utilizes the Windows Management API calls and messages to manage and control other Windows applications.

Figure 14.4. Windows Management Utility screen shot.

graphics/14fig04.jpg

We'll take a look at five different aspects of the program that demonstrate the application of the Windows Management API: Enumerating Windows, Controlling External Windows, Message Handling Support for moving an external window, Flashing a Window for user notification, and Window Animation Effects on open and close.

Enumerating Windows

Listing 14.1 shows some of the source code used to enumerate active windows in the system by using the EnumWindows() call and an application-defined callback function called GetWinHandleAll() . The information gathered by the callback function is displayed to the user within a tree view control.

Listing 14.1 Windows Management Utility ” Enumerating Windows Example
 void __fastcall TFormWinMan::ButtonEnumWindowsClick(TObject *Sender)  {      TCursor Save_Cursor = Screen->Cursor;      Screen->Cursor = crHourGlass;    // hourglass cursor      try      {          TreeView1->Items->Clear();                    // clear tree          EnumWindows((WNDENUMPROC)GetWinHandleAll,0); // enumerate all windows          TreeView1->AlphaSort();   // sort tree which contains enum results      }      __finally      {        Screen->Cursor = Save_Cursor; // restore cursor      }  }  //--------------------------------------------------------------------------- BOOL CALLBACK TFormWinMan::GetWinHandleAll(HWND hwnd, unsigned long hproc)  {   int correlation = FormWinMan->GetCorrelationChoice();   FormWinMan->ProcessHandleInformation(hwnd,correlation);   if (hwnd != NULL) return true;  // keep going (we want them all)   return false; //stop enumeration  }   //--------------------------------------------------------------------------- TTreeNode* __fastcall TFormWinMan::ProcessHandleInformation(HWND H_Window,                                                unsigned int correlation_type)  {      // get the handle of the application instance      HINSTANCE  hAppInstance = (HINSTANCE)GetWindowLong(H_Window,GWL_HINSTANCE);      unsigned long dwProcessId = 0;      // find out who created window      unsigned long tempID = GetWindowThreadProcessId(             H_Window,             &dwProcessId);     // address of variable for process identifier      int length = GetWindowTextLength(H_Window);    // get the length      if ((CheckBoxIgnoreWindowsNoTitles->Checked) && (length == 0))          return NULL;      char classname[80];      char windowtitle[80];      GetWindowText(H_Window, windowtitle, 80);      GetClassName(H_Window, classname, 80);      HWND hwndParent = (HWND)GetWindowLong(H_Window,GWL_HWNDPARENT);      if ((!CheckBoxIncludeParent->Checked) && (hwndParent == 0))        return NULL;      TAppInfo *appinfo = new TAppInfo();      appinfo->DeviceContext      = GetWindowDC(H_Window);      appinfo->Title              = AnsiString(windowtitle);      appinfo->Class              = AnsiString(classname);      appinfo->WindowHandle       = H_Window;      appinfo->ParentWindowHandle = hwndParent;      appinfo->InstanceHandle     = (int)hAppInstance;      appinfo->ProcessID          = dwProcessId;       TTreeNode* current_node = NULL;      // let's double check to make sure we have a node for the parent.      if ((CheckBoxIncludeParent->Checked) && (appinfo->ParentWindowHandle != 0))      {          TTreeNode* parent_node = FormWinMan->GetNode_AppInfoValue(                    FormWinMan->TreeView1->Items,                    NULL,                    WINHANDLE,                    (unsigned long)appinfo->ParentWindowHandle);          if (!parent_node)  // need to create node with parent info               current_node = ProcessHandleInformation(      // recurse                          appinfo->ParentWindowHandle,                          correlation_type);      }      AnsiString treetext;      switch (correlation_type)      {        case PROCESS :            current_node =                    FormWinMan->GetNode_AppInfoValue(                                      FormWinMan->TreeView1->Items,                                      NULL,                                      PROCESSID,                                      (unsigned long)appinfo->ProcessID);            treetext = "PID=" + AnsiString(appinfo->ProcessID) + " : ";            break;        case INSTANCE :            current_node =                     FormWinMan->GetNode_AppInfoValue(                                      FormWinMan->TreeView1->Items,                                      NULL,                                      INSTANCEHANDLE,                                      (unsigned long)appinfo->InstanceHandle);            treetext = "INST=" + AnsiString(appinfo->InstanceHandle) + " : ";            break;         case WINDOW :            current_node =                     FormWinMan->GetNode_AppInfoValue(                                      FormWinMan->TreeView1->Items,                                      NULL,                                      WINHANDLE,                                      (unsigned long)appinfo->ParentWindowHandle);            treetext = "HWND=" + AnsiString((unsigned int)appinfo->WindowHandle) +                       " : ";            break;        default :                if (CheckBoxProcessRootNode->Checked)                        current_node = FormWinMan->GetNode_Process(                                  FormWinMan->TreeView1->Items,                                  appinfo->ProcessID);                treetext = "";      }      TTreeNode* node = NULL;      if (CheckBoxProcessRootNode->Checked) // user wants process IDs at 1st level      {         TAppInfo *processinfo = new TAppInfo();         processinfo->ProcessID = appinfo->ProcessID;         // make sure we have ProcessRootNode for this WindowHandle         node = FormWinMan->GetNode_Process( FormWinMan->TreeView1->Items,                                             appinfo->ProcessID);         if (!node) {            // need to create Process Node            AnsiString temptext = "PID=" + AnsiString(appinfo->ProcessID);            node = FormWinMan->TreeView1->Items->AddObject(NULL,                                                          temptext,                                                          processinfo);            current_node = node;    // reset current_node         }      }       node = FormWinMan->GetNode_AppInfoValue(FormWinMan->TreeView1->Items, NULL,                                           WINHANDLE,                                           (unsigned long)appinfo->WindowHandle);      if (!node)   // make sure we don't already have the WindowHandle      {          if (!current_node)  // parent node          {              if (FormWinMan->TreeView1->Items->Count > 0)                    current_node = FormWinMan->TreeView1->Items->Item[0];              node = FormWinMan->TreeView1->Items->AddObject(current_node,                        AnsiString(treetext +                              AnsiString(windowtitle) +  " (" +                              AnsiString(classname) + ")"),appinfo);          }          else // child node          {              node = FormWinMan->TreeView1->Items->AddChildObject(                            current_node,                            AnsiString(treetext +                                    AnsiString(windowtitle) + " (" +                                    AnsiString(classname) + ")"),appinfo);          }      }      return node; // make sure we pass back the node  }      node = FormWinMan->GetNode_AppInfoValue(FormWinMan->TreeView1->Items, NULL,                                                WINHANDLE,                                                (unsigned long)appinfo-> WindowHandle);      if (!node)   // make sure we don't already have the WindowHandle      {          if (!current_node)  // parent node          {              if (FormWinMan->TreeView1->Items->Count > 0)                    current_node = FormWinMan->TreeView1->Items->Item[0];               node = FormWinMan->TreeView1->Items->AddObject(current_node,                        AnsiString(treetext +                              AnsiString(windowtitle) +  " (" +                              AnsiString(classname) + ")"),appinfo);          }          else // child node          {              node = FormWinMan->TreeView1->Items->AddChildObject(                            current_node,                            AnsiString(treetext +                                    AnsiString(windowtitle) + " (" +                                    AnsiString(classname) + ")"),appinfo);          }      }      return node; // make sure we pass back the node  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::TreeView1Change(TObject *Sender, TTreeNode *Node)  {     TTreeNode* node  = TreeView1->Selected;     if ((node->Level == 0) && (CheckBoxProcessRootNode->Checked)) return;     TAppInfo* info = (TAppInfo*)node->Data;     winhandle = info->WindowHandle;     UpdateAppInfo(winhandle);  } 

Let's examine what's happening in this code. The ButtonEnumWindowsClick() method is used to enumerate all the active windows in the system. The actual Win32 API call used to initiate this enumeration is provided by the EnumWindows() call. As a parameter to the EnumWindows() call, we passed a parameter to a callback function that we've defined called GetWinHandleAll() . All callback functions provided to EnumWindows() must take on this same form.

The GetWinHandleAll() callback that we've created receives the windows handle to each enumerated window. Calls used to process and display the windows handle information is provided by the ProcessHandleInformation() method defined for this sample. The GetWinHandleAll() also checks for a NULL window handle to determine when all windows have been enumerated, and returns false after this condition is met.

ProcessHandleInformation() examines each window handle using Windows Management API calls and builds a tree of the enumerated data using objects. The specific Win32 API calls used to collect the information for each node object include GetWindowLong() with the parameter GWL_INSTANCE to determine the handle instance of the window; GetWindowThreadProcessID() to determine the Process ID for the window handle; GetWindowText() to determine the caption title of the specified window; and GetClassName() to determine the class used to defined the window. This information is used to organize and label the nodes within our tree view.

TreeView1Change() event handler is triggered when the user clicks or keys any of the nodes within the tree view. When this occurs, the windows handle is retrieved from the node object and additional information is provided to the user through UpdateAppInfo() .

Let's take a look, for a moment, at the UpdateAppInfo() function created for this example.

 void __fastcall TFormWinMan::UpdateAppInfo(HWND winhandle)  {      EditWHandle->Text = (AnsiString)(int)winhandle;      int length = GetWindowTextLength(winhandle);      char * title = new char[length+1];      title[length] = 0;      GetWindowText(winhandle,title,length+1);      EditTitle->Text = AnsiString(title);      char classname[80];      GetClassName(winhandle, classname, 80);      EditClass->Text = AnsiString(classname);      RECT Rect;      GetWindowRect(winhandle,&Rect);    // get the size for the current window      EditLeft->Text = AnsiString((unsigned int)Rect.left);      EditTop->Text = AnsiString((unsigned int)Rect.top);      EditWidth->Text = AnsiString((unsigned int)(Rect.right - Rect.left));      EditHeight->Text = AnsiString((unsigned int)(Rect.bottom - Rect.top));      unsigned long dwProcessId = 0; //(unsigned long) GetWindowLong(H_Window,GWL_ID);      // find out who created window      unsigned long tempID = GetWindowThreadProcessId(             winhandle,             &dwProcessId); // address of variable for process identifier      EditProcessID->Text = AnsiString(dwProcessId);      // get the handle of the application instance      HINSTANCE  hAppInstance = (HINSTANCE)GetWindowLong(winhandle,GWL_HINSTANCE);      EditIHandle->Text = AnsiString((unsigned int)hAppInstance);      HWND ParentWindowHandle= (HWND)GetWindowLong(winhandle,GWL_HWNDPARENT);      EditPWHandle->Text = AnsiString((unsigned int)ParentWindowHandle);  } 

In this code, we again use several Win32 API calls to obtain and display information regarding the windows handle that was passed in as a parameter.

GetWindowTextLength() and GetWindowText() are used in tandem to retrieve the window text such as a title bar caption. In the next example, we'll use SetWindowText() to modify text of an external window.

GetClassName() is used to retrieve the class name associated to the window. For a C++Builder application you might see TApplication , a TForm descendent , or a class used to represent a subcomponent on the form. It really depends on the level of the window being examined.

GetWindowRect() is then used to retrieve the screen coordinates of the window. Some simple math is performed to determine the width and height of the window. In a short bit, we will demonstrate how to move an external window. The code on the CD-ROM also shows how to resize an external window.

GetWindowThreadProcessID() is used to find out what process is associated with the window we use. Finally, we use GetWindowLong() twice to find out the Instance Handle of the window by using the GWL_HINSTANCE flag, and, if it exists, the Window handle of the parent using the GWL_HWNDPARENT flag.

Controlling External Windows

Let's now take a look at some other code from this sample that uses the Windows Management calls to control the external windows that have been enumerated (see Listing 14.2).

Listing 14.2 Windows Management Utility ”Manipulating External Windows Example
 void __fastcall TFormWinMan::ButtonChangeTitleClick(TObject *Sender)  {          SetWindowText(winhandle, EditTitle->Text.c_str());  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::ButtonBringToFocusClick(TObject *Sender)  {          SendMessage(winhandle,WM_SYSCOMMAND,SC_RESTORE,0);          SetForegroundWindow(winhandle);          if (CheckBoxTop->Checked) SetForegroundWindow(Handle);  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::ButtonMaximizeClick(TObject *Sender)  {      SendMessage(winhandle,WM_SYSCOMMAND,SC_MAXIMIZE,0);  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::ButtonHideClick(TObject *Sender)  {      ShowWindow(winhandle, SW_HIDE);  } 

This listing provides just a handful of some of the functions that manipulate other windows through Windows Management API calls. Let's examine some of these calls.

In ButtonChangeTitleClick() , the SetWindowText() API call is used to alter the text associated to a window handle. This can be used to change the window's title bar or the text of window controls such as a button caption or menu item if we know its window handle.

 BOOL SetWindowText(    HWND  hWnd  ,         // handle to window or control    LPCTSTR  lpString  // title or text  ); 

In our example, we simply pass as parameters the active window handle of the window currently being examined, and the text that we want to assign to the control.

There are up to three Windows Management API Calls used in the ButtonBringToFocusClick() event handler. The first call used is SendMessage() , which is used to transmit a synchronous Windows message to a single window handle or to all top-level windows. The message that is being passed in this example is the WM_SYSCOMMAND windows message with the SC_RESTORE parameter as the command being requested . Here we are requesting that the targeted application restore its window to its normal position and size. The ButtonBringToFocusClick() then waits until this message has been processed by the targeted window.

Following the SendMessage() call, ButtonBringToFocusClick() issues a SetForegroundWindow() to activate and raise the targeted window into the foreground. Additionally, if the user has checked the CheckBoxTop control, which indicates that the user wants the WinManUtil app to not be masked by the targeted window, our example app is raised one layer higher than the targeted window by using SetForegroundWindow() again. This time, however, we pass its own windows handle.

NOTE

Windows messages provide the interaction mechanism used to pass input to various objects represented by a handle . The two common API calls used to pass a specified message to a window or windows are SendMessage() and PostMessage() . Although these calls perform a similar action, there are some differences between the two. SendMessage() is used to pass a message synchronously; the call waits until the message has been processed. PostMessage() , however, is used to pass a message asynchronously; it does not bother to wait for the message to be processed and returns immediately. The declaration for the PostMessage() is provided below:

 LRESULT PostMessage(      HWND hWnd,    // handle of destination window      UINT Msg,    // message to send      WPARAM wParam,    // first message parameter      LPARAM lParam     // second message parameter     ); 

Both SendMessage() and PostMessage() require four elements: a window handle indicating the target window, a message identifier that describes the purpose of the message, and two 32-bit message parameters. The message parameters can be used to pass value information or address information to the destination handle. The LRESULT value returned by the SendMessage() or PostMessage() function specifies the result of the message processing. For instance, a return value of nonzero indicates success for PostMessage() .


In ButtonMaximizeClick() we again use SendMessage() , but this time requesting for the target window to Maximize its window by using the SC_MAXIMIZE command in connection with the WM_SYSCOMMAND message.

In ButtonHideClick() we are using another Windows Management routine called ShowWindow() to change the state of the targeted window. In this case, we are requesting the window to be hidden and no longer active on the desktop by using the SW_HIDE command.

Message Handling Support

Let's take a look at a few more code excerpts from this sample that demonstrates some practical ways of using the Windows Management calls for moving the display location of an external window. Code to support this functionality is provided in Listing 14.3.

Listing 14.3 Windows Management Utility ”Message Handling Support for Moving an External Window
 __fastcall TFormWinMan::TFormWinMan(TComponent* Owner)          : TForm(Owner)  {      // define custom messages      WM_MOVE_A_WINDOW = RegisterWindowMessage("WM_MOVE_A_WINDOW");      WM_STRETCH_A_WINDOW = RegisterWindowMessage("WM_STRETCH_A_WINDOW");      WindowProc = MyWndProc;  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::MyWndProc(Messages::TMessage &Message)  {    // If the window receives a notification message then    // pass it to the appropriate windows messaging function. Otherwise    // let the default processing for the message take place.    if (Message.Msg == WM_MOVE_A_WINDOW)          wmMoveAWindow(Message);    else if (Message.Msg == WM_STRETCH_A_WINDOW)          wmStretchAWindow(Message);    else          WndProc(Message) ;  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::ButtonMoveRightMouseDown(TObject *Sender,        TMouseButton Button, TShiftState Shift, int X, int Y)  {       ButtonDown = true;       PostMessage(Handle, WM_MOVE_A_WINDOW, (unsigned int)winhandle, MOVERIGHT);  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::ButtonMoveRightMouseUp(TObject *Sender,        TMouseButton Button, TShiftState Shift, int X, int Y)  {      ButtonDown = false;  }  //--------------------------------------------------------------------------- void __fastcall TFormWinMan::wmMoveAWindow(TMessage Msg)  {      RECT rect;      HWND wh = HWND(Msg.WParam);      GetWindowRect(wh, &rect);  // get the size for the current window      int width  = rect.right  - rect.left;      int height = rect.bottom - rect.top;      int direction = Msg.LParam;      while (ButtonDown)      {          switch (direction)          {              case MOVEUP    : rect.top--;  break;              case MOVEDOWN  : rect.top++;  break;              case MOVELEFT  : rect.left--; break;              case MOVERIGHT : rect.left++; break;          }          MoveWindow(wh, rect.left, rect.top, width, height, true);          Application->ProcessMessages(); // process other messages          UpdateAppInfo(winhandle);      }  } 

In this example, the effect that is desired is for the user to be able to hold the Move Right button down, and for the targeted window to continually move horizontally to the right until the Right button is finally released ”that is, the button comes back up. To do this, however, we will need to use a custom windows message that will be handled by our application. The custom windows message of interest is called WM_MOVE_A_WINDOW , which has been declared as an integer property for TFormWinMan . To set this up, we use the RegisterWindowMessage() API call in the constructor for TFormWinMan . RegisterWindowMessage() will guarantee that the user-defined message WM_MOVE_A_WINDOW will be unique throughout the system. The WindowProc property of the form, which is inherited from TConrol, is also set to a new windows-handling method called MyWndProc() . MyWndProc() will now intercept any incoming Windows message calls for our application. If the method receives a specific message such as WM_MOVE_A_WINDOW , it passes it to the appropriate windows messaging-handler function. Otherwise, the default processing for the message takes place through a call to the form's standard WndProc() method. In this example, after WM_MOVE_A_WINDOW is intercepted, a call is made to another routine defined for our application called wmMoveAWindow() . In a short bit, we will take a look at wmMoveAWindow() and how it is applied to support the task of moving our targeted window. But next, let's examine how we trigger the move event.

The ButtonMoveRightMouseDown() function provides the desired processing after the mouse-down event occurs. In our case, the ButtonDown property associated to the form is set to true , indicating that we have a button down. Then, a PostMessage() call is made to itself using our user-defined message WM_MOVE_A_WINDOW with parameters identifying what window to move (a handle), and in what direction to move. The code that receives and processes this specific message is contained within the wmMoveAWindow() method. Although the Move Right button is down ”remember we're keeping track of the status of that button through the ButtonDown property ”we issue a MoveWindow() API call passing the window handle of the target, and the new desired desktop location in the form of a RECT structure. We continue to call MoveWindow() until ButtonDown is false.

You may ask, "Why do you post a windows message to yourself? Why not put the MoveWindow() looping code right into the event handler?" The reason why a PostMessage() call is made, is because in Borland C++Builder (and in Delphi) the insertion of any type of loop processing directly into a VCL event handler could jeopardize the capability to capture other similar events. In our case, we're looking for two events associated to the same button: a mouse-down event and a mouse-up event. If we put while loop type processing directly into the method handling the first event, we risk missing the back-end event, which is the mouse release. If we miss the mouse release, the target window we're moving will keep moving forever. Obviously, that's not the desired effect, so we need some way to provide loop processing for an event and still be able to field other events that might occur. The easiest way to do that, short of threading, is to use the asynchronous PostMessage() call with a custom windows message, just as we have done in our example. The handler of that message provides the conditional looping. In our case, this conditional looping is found in the wmMoveAWindow() routine. Notice also the use of the Application->ProcessMessages() routine, which temporarily interrupts the looping that is occurring so that other window message events can be processed and fielded.

The ButtonMoveRightMouseUp() method is simply used to process the mouse-up event associated to the Move Right button by toggling the ButtonDown property to false . After the ButtonDown property is set to false , the loop processing that has been occurring within the wmMoveAWindow() routine finishes because the loop condition, while (ButtonDown) , has now failed.

Flashing a Window

Sometimes it's helpful to notify the user that an application has completed its task without being interrupted by an annoying message box. Microsoft has two API calls that provide a nonobtrusive way of signaling a notification to the user: FlashWindow() and FlashWindowEx() . Both of these routines can be used to alert a user that his attention is recommended, but not required. Whereas, a message box, which can be generated by the Windows API MessageBox() call, requires that a user perform the extra step of closing the window alert.

Let's take a look at the code in our sample program that uses FlashWindowEx() .

 void __fastcall TFormWinMan::ButtonFlashClick(TObject *Sender)  {    FLASHWINFO flash_info;    flash_info.cbSize    = sizeof(FLASHWINFO);    flash_info.hwnd      = winhandle;    flash_info.dwFlags   = FLASHW_TIMER  FLASHW_ALL;    flash_info.uCount    = EditCount->Text.ToIntDef(10); // # times to flash    flash_info.dwTimeout = EditDur->Text.ToIntDef(100); // duration for each flash    FlashWindowEx(&flash_info);  } 

In this code, FlashWindowEx() simply flashes the title bar of the application, and/or the icon representing the application in the taskbar.

NOTE

You might have noticed the use of FlashWindow() or FlashWindowEx() in popular programs such as America Online's Instant Messenger, Yahoo Pager, and ICQ. This feature is used to alert a user that something in a specific program has occurred and is in need of attention, or to let the user know that that a window is ready to receive focus.


FlashWindowEx() requires a pointer to a structure containing the flash information. The variable to this pointer is defined by FLASHWINFO , which is listed as follows :

 typedef struct {    UINT  cbSize;    HWND  hwnd;    DWORD dwFlags;    UINT  uCount;    DWORD dwTimeout;  } FLASHWINFO, *PFLASHWINFO; 

The FLASHWINFO structure controls the way the FlashWindowEx() function is going to execute. First, cbSize identifies the size of the FLASHINFO structure using sizeof (FLASHWINFO) . Hwnd is used to identify the handle of the window to be flashed. dwFlags identifies how the flashes are going to be carried out. Table 14.2 shows the different flags:

Table 14.2. FlashWindowEx() Flags

Flag

Meaning

FLASHW_STOP

Stop flashing. The system restores the window to its original state.

FLASHW_CAPTION

Flash the window caption.

FLASHW_TRAY

Flash the taskbar button.

FLASHW_ALL

Flash both the window caption and taskbar button. This is equivalent to setting the FLASHW_CAPTION and FLASHW_TRAY flags.

FLASHW_TIMER

Flash continuously until the FLASHW_STOP flag is set.

FLASHW_TIMERNOFG

Flash continuously until the window comes to the foreground.

In our example, we used the FLASHW_TIMER to identify that we want to use a timer, and FLASHW_ALL flags to identify that we want both the title bar and taskbar icon to flash. The uCount parameter identifies the number of times we want the window to flash. In our example, we retrieve the flash count from a TEdit control with a default of ten counts. The dwTimeout parameter contains the number of milliseconds the window is to flash. Again, we retrieve a user entry from a TEdit control, this time with a default duration of 100. Using these default values, the target app will flash ten times with one flash every 100 milliseconds for a total duration of flashes at approximately 1,000 milliseconds (or one full second).

CAUTION

FlashWindowEx() is a fairly new Win32 API that is not supported by Windows 95, so use it cautiously. Its older sibling FlashWindow() , however, is supported by Windows 95. The difference is that FlashWindow() does not have the flexibility offered by FlashWindowEx() with such features as a built-in timer and count support.

If you are developing applications that need to operate under Windows 95, you can mimic FlashWindowEx() simply by creating your own function that uses a windows timer, a counter, and FlashWindow() . However, be aware that FlashWindow() can have varying effects across different versions of Windows, specifically between 9x and NT-based systems. Therefore, if you add this capability, be sure to test your application under various Windows platforms before you deploy your application.


Window Animation Effects

Within C++Builder, you can get some pretty interesting form effects for your apps through the use of AnimateWindow() . This is one of the newer features introduced in Windows 98 and Windows 2000. AnimateWindow() can be used in place of the standard ShowWindow() call used to show or hide a window. It provides window animation effects including slides, blends, center expansions, and contractions of a window. Let's take a look at a short example that uses AnimateWindows() .

 void __fastcall TFormWinMan::Button1Click(TObject *Sender)  {    // hide it    AnimateWindow(Handle, 5000, AW_HIDE  AW_SLIDE  AW_VER_POSITIVE);    // show it    AnimateWindow(Handle, 5000, AW_ACTIVATE  AW_SLIDE  AW_HOR_POSITIVE);  } 

In this example, we hide the window represented by the Form's Handle property with a five second (5,000 millisecond) animation using a slide effect in the upward vertical direction. Then we reactivate the same window using a slide effect in the right horizontal direction, also in five seconds. It's simple, but can be quite impressive.

AnimateWindow() looks quite easy, but if you don't use it correctly, you won't get good results. It has the following parameters:

 BOOL AnimateWindow(    HWND  hwnd  ,     // handle to window    DWORD  dwTime  ,  // duration of animation    DWORD  dwFlags  // animation type  ); 

hwnd is a handle to the window in which you want to animate.

dwTime is the time in milliseconds to perform the animation.

dwFlags represents different flags and types of animation you can perform. Table 14.3 lists the flags from which to choose.

Table 14.3. AnimateWindow() Flags

Flag

Value

AW_SLIDE

Uses slide animation. By default, roll animation is used. This flag is ignored when used with AW_CENTER .

AW_ACTIVATE

Activates the window. Do not use this value with AW_HIDE .

AW_BLEND

Uses a fade effect. This flag can be used only if hwnd is a top-level window.

AW_HIDE

Hides the window. By default, the window is shown.

AW_CENTER

Makes the window appear to collapse inward if AW_HIDE is used. If AW_HIDE is not used, the window appears to expand outward.

AW_HOR_POSITIVE

Animates the window from left to right. This flag can be used with roll or slide animation. It is ignored when used with AW_CENTER or AW_BLEND .

AW_HOR_NEGATIVE

Animates the window from right to left. This flag can be used with roll or slide animation. It is ignored when used with AW_CENTER or AW_BLEND .

AW_VER_POSITIVE

Animates the window from top to bottom. This flag can be used with roll or slide animation. It is ignored when used with AW_CENTER or AW_BLEND .

AW_VER_NEGATIVE

Animates the window from bottom to top. This flag can be used with roll or slide animation. It is ignored when used with AW_CENTER or AW_BLEND .

If AnimateWindow() doesn't work, there are typically three reasons why it failed: you are trying to show the window when it is already visible, you are trying to hide the window when it is already hidden, or the thread or process calling AnimateWindow() does not own the specified window. In the case of the sample program included on the CD-ROM, the AnimateWindow() demo will only work on a window owned by the sample program. If you try it on a window owned by a another process, AnimateWindow() will do nothing.

NOTE

Using AnimateWindow() might not work for all users because the Animate Windows feature can be disabled within the Windows Control Panel.


Message Identifiers

In our sample program earlier, we used the RegisterWindowMessage() API call to dynamically define a unique user-defined Windows message. Our program also issued several different types of predefined Windows Message Identifiers through the SendMessage() and PostMessage() calls to affect the appearance of other applications. This included WM_CLOSE and WM_SYSCOMMAND .

There are well over 200 predefined Windows Message Identifiers within current versions of Windows that both the Windows system and applications can dispatch. A majority of the predefined Windows Message Identifiers within Windows all begin with the WM_ prefix. Windows Messages are used to signify input, system changes, or direct information passed from one application or windows object to another. In general, Windows Messages are used to perform interrupt handling between applications and/or the operating system. For instance, when a user left clicks the mouse, the system generates a WM_LBUTTONDOWN message to the appropriate application indicating the action that occurred. If a user resizes the active screen, the system generates a WM_SIZE message to the appropriate application indicating the type of resizing and the size values to be applied. The key to making messaging work is to know the windows handle for which the message is to be delivered.

Responding to Windows Messages

To respond to specific messages sent either by the SendMessage() or PostMessage() routines, an application needs to have some type of message handling capability. In our sample program, we created our own window message handling procedure, temporarily overriding the form's WndProc method so that it could respond to both user-defined messages, which we created dynamically, and standard Windows Messages.

However, the common way to field Windows Messages that are predefined is to use an event response table. The most popular way to set up an event response table within C++Builder is to declare callback routines and a message map within the protected area of the main form's class declaration. Here is an example:

 protected:     // User declarations      void __fastcall Process_wmMoveAWindow(TMessage &);      void __fastcall Process_wmQuit(TMessage &);      void __fastcall Process_wmXYZ(TMessage &);      BEGIN_MESSAGE_MAP         MESSAGE_HANDLER(WM_PAINT,TMessage,Process_wmPaint);         MESSAGE_HANDLER(WM_QUIT,TMessage,Process_wmQuit);         MESSAGE_HANDLER(WM_XYZ,TMessage,Process_wmXYZ);      END_MESSAGE_MAP(TForm); 

Within the source code for an application, the callback routine would look something like the following:

 void __fastcall TForm1::Process_wmXYZ(TMessage Msg)  {      int fromhandle = LOBYTE(LOWORD(Msg.WParam));      int infoid = Msg.LParam;      AnsiString StatusText =      " Application sent message XYZ\nApplication Handle = " + IntToStr(fromhandle) +      "\nInfo = " + IntToStr(infoid);      MessageBox(Handle,StatusText,"Received Message Callback",MB_OK);  } 

In this example, Borland's TMessage provides the structure for containing the Windows message information, specifically the 32-bit WParam and LParam values that were passed by the SendMessage() or PostMessage() call. As with most callback routines, in our example we've examined and deciphered the WParam and LParam values.

TIP

Either one of the 32-bit WParam or LParam values passed by SendMessage() or PostMessage() (and contained within the TMessage structure) can also be used to represent a pointer to an accessible address location, but only if the caller resides within the same process as the recipient. In the Win16 days, address space was accessible across multiple processes. Theoretically, you could pass pointers anywhere in the system if you wanted. Win32, however, is much more protected. But the same trick is applicable . If a pointer is passed using WParam or LParam to a DLL resident in the same memory segment or to a local thread, more information than just a 32-bit data value can be shared with other objects. Keep that in mind the next time you need to pass a structure or character string to or from a DLL or thread.



   
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net