From the December 2001 issue of MSDN Magazine.

MSDN Magazine

Stopping Screen Savers, Detecting Screen Resolution, Adding Status Bar Buttons
Paul DiLascia
Download the code for this article:CQA0112.exe (52KB)
Browse the code for this article at Code Center:Status Bar Button

Q

I'm writing an emergency alert program. Upon receipt of an alerting message, my app must stop the screen saver or power save mode and display that message, or it must be shown over the screen saver. I tried SetWindowPos(&wndTopMost...) but it was in vain, even for the screen saver in Windows® 2000. How can I stop the screen saver when my program runs in Windows 2000?

Jungkhun
A

In the old days of Windows 3.1 and even Windows 98, you could stop the screen saver simply by grabbing the active window and sending it a WM_CLOSE message.

  PostMessage(GetActiveWindow(),WM_CLOSE,0,0);
  

 

      What could be simpler? Alas, in Windows 2000, this no longer works. Windows 2000 has the notion of separate desktops, and the screen saver runs in a special desktop called Screen-saver. You can't find the screen saver window using GetActiveWindow or GetForegroundWindow because it's running in this other desktop. Instead, you have to open the desktop, enumerate its windows, and send WM_CLOSE to the screen saver. See Figure 1 to examine the details.

Q

How can I detect whether the screen resolution is 640×480 or 600×800 from C++?

Amir Dashti
Tehran


A

It's easy—just call GetSystemMetrics.

     // width
  
int cx = GetSystemMetrics(SM_CXSCREEN);
// height
int cy = GetSystemMetrics(SM_CYSCREEN);

 

      GetSystemMetrics is a handy function you can use to get all sorts of global dimensions, like the size of an icon or height of a window caption. In Windows 2000, there are new parameters like SM_CXVIRTUALSCREEN and SM_CYVIRTUALSCREEN to get the virtual size of the screen for multiple monitor systems. Windows newbies—and pros, too—should check out the documentation for GetSystemMetrics to see all the different system metrics (dimensions) you can get. See the Platform SDK for the latest at https://msdn.microsoft.com/library/en-us/sysinfo/sysinfo_8fjn.asp. GetSystemMetrics is a handy function you frequently need to use, and new stuff appears with every version of Windows.

Q

This might be a simple question, but I want to know how I can add pushbuttons on the status bar of an application just like the buttons on the Quick Launch bar in Windows?

Guru
Bangalore, India


Q

How do I put a button and edit control (with up/down buddy) on the status bar in an application like VIRGIL as you did in the March 1997 installment of C++ Q&A in MSJ.

Jef Pavlat


A

The status bar is based on the Windows common control msctls_statusbar32, which does not provide any way to add child windows. Does that mean you're out of luck? Of course not! In Windows, the way to add child windows to some other control or window is not to add them as children, but to add them as siblings. In this case, you have two options: you can create your own "super status bar" control that contains the ordinary status bar and other controls as children (like the way Windows combines a listbox and edit control to produce a combobox); or you can add the button and other controls directly to the mainframe, as siblings of the status bar, toolbar, and view.
      Determining which approach is better depends on how complicated your design is and what you plan to do. If you plan to add many controls and/or reuse the combined status bar/button/edit control in other windows or apps, you'll do better creating a combined control. If all you want to do is add a single button in one window, adding it to the main frame is probably more expedient. Whichever approach you take, you'll have to write some code to position your controls adjacent to one another.

Figure 2 Status Bar Button
Figure 2 Status Bar Button

      I wrote a little program, StatBarButn, that shows how (see Figure 2). All the action happens in CMainFrame, which is shown in Figure 3. CMainFrame has a data member to hold the button (see Figure 4). This code shrinks the status bar and positions the button to fill the shrunken area. Since the button ID is ID_APP_ABOUT—the same as the menu command for Help | About—there's no need to do anything special to connect the button to its command handler. Naturally, if you wanted some other command, you'd have to create an ID and use it. StatBarButn uses the default font; if you're doing this for a real app, you should use SystemParamtersInfo(SPI_GETNONCLIENTMETRICS) to get the current message or menu font.

Figure 5 Removing Size Grip Handles
Figure 5 Removing Size Grip Handles

      When I first wrote StatBarButn, I ended up with a status bar that looked like Figure 5. Notice the size grip handles sitting to the left of the button. Oops. To get rid of the handles, you have to create a status bar without the SBARS_SIZEGRIP style. Alas, turning the style off after the status bar is created has no effect. Evidently Windows looks at the style only when the status bar is created. MFC lets you pass style flags when you call CStatusBar::Create, but MFC contains the following hardcoded lines:

  // in CStatusBar::CreateEx
  
if (pParentWnd->GetStyle() & WS_THICKFRAME)
dwStyle |= SBARS_SIZEGRIP;
dwStyle |= dwCtrlStyle;
return CWnd::Create(... dwStyle ...);

 

That is, MFC insists on SBARS_SIZEGRIP if your main frame has WS_THICKFRAME (which normal frame windows do). What to do?
      The proper way would be to derive a new class, CMyStatusBar, and write your own Create function that doesn't use SBARS_SIZEGRIP, but the following simple hack achieves the same effect:

  // turn off WS_THICKFRAME 
  
ModifyStyle(WS_THICKFRAME,0);
m_wndStatusBar.Create(this); // turn WS_THICKFRAME back on
ModifyStyle(0,WS_THICKFRAME);

 

MFC thinks the frame has no thick border, so it leaves SBARS_SIZEGRIP off; now the status bar has no grip handles. It's an ugly solution, but it works.
      What if you want size grip handles? Well, then I'm afraid you're hosed. In this case, I think the simplest approach would be to implement a combo status bar, with two child status bars, one on either side of your button(s) and whatever other controls you want. The status bar on the left would have no grip handles; the status bar on the right would have nothing but the grip handles. To be honest, I'm not sure this will work if the status bar is a child of some new window instead of the frame itself; you may have to reroute messages or change the window owner—if you try this at home, please let me know!
      There's one more detail to take care of to make everything fly: what happens if the user hides the status bar? Oops again. As it stands, the button will remain. To hide the button, you have to add some code in the handler for ID_VIEW_STATUS_BAR. Figure 3 shows the details.
      If you do go the route of the combo status bar, you'd create the status bar, button, and other controls as children of a new window class, and position the windows during an OnSize. RecalcLayout is an MFC thing peculiar to frame windows. If the button or any other control is smaller than the whole window, you'll have to write some code in your combo status bar to paint the areas between the children. You can do this by handling WM_ERASEBKGND, or simply set the background brush to the appropriate system color (for example, COLOR_3DFACE) when you register your window class. Last of all, you'll have to override OnCmdMsg to route messages to the parent window.

Send questions and comments for Paul to cppqa@microsoft.com.

Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at askpd@pobox.com or https://www.dilascia.com.