News | Articles | Libraries | Developer Tools | Books | Forum Links | Search   
Sections:
 

QA: How can I close my application by clicking the smart minimize (X) button?

By Joao Paulo Figueira, August 19, 2003.
Print version

Question

When I press the smart minimize (X) button the application is minimized instead of beeing closed. How can I handle smart minimize button click in my application to close it?

Answer

The button at the right of the Task Bar (the bar at the top of your Pocket PC) is called the "Smart Minimize Button". It is used to remove your application from the foreground, but keeps it running. If you open the Settings Memory applet, you can see all running applications, and close them if you want.

Newcomers to Pocket PC development, and users alike, find this a strange behaviour. After all they are used to close their desktop applications using a similar button, so why shouldn't this one work like that? Microsoft designed this button to work like that to add comfort to the user experience: users shouldn't care about closing their own applications. This is a task for the system.

There is a less-known method of closing well behaved Pocket PC applications: Ctrl-Q. If you issue this key combination (possibly using the SIP), your application should shut down. Thankfully, MFC does this for us, so you don't have to bother implementing it.

Some of us (and even Microsoft in the SQL CE 2.0 Query Analyzer) have implemented an "Exit" menu option in our applications. The handler simply sends a WM_CLOSE message to the main frame window and we are done.

But how do we manage to have the "Smart Minimize Button" close the application for us?

We have to hook it. Here's how it works: First, subclass the task bar window. The new window procedure must be looking for WM_LBUTTONUP messages in the rectangle of the button. When you intercept the message, post a WM_CLOSE message to your main frame. You will have to be careful when subclassing the task bar window procedure: you have to make sure that you will un-subclass it when your application loses the focus (deactivated) or when it is closed (essentially the same thing).

The Recipe for MFC

All the work you need to do is restricted to the CMainFrame class. First, let's look at the definitions you need to add to the header file:

extern HWND g_hWndMain, g_hWndTask; extern WNDPROC g_fnProcTask; static LRESULT CALLBACK TaskWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

The static variables will store the task bar and main frame's window handles. The WNDPROC will store the task bar's original window proc. Now, go to the implementation file (CPP) and add these declarations:

// // Static variables for task bar subclassing // static HWND g_hWndMain = NULL; static HWND g_hWndTask = NULL; static WNDPROC g_fnProcTask = NULL;

Now, add the following protected methods to CMainFrame:

// CMainFrame::HookTaskBar // // Hook into the task bar // BOOL CMainFrame::HookTaskBar() { // // Already hooked? // if(g_fnProcTask) return FALSE; g_hWndTask = ::FindWindow(_T("HHTaskBar"), NULL); if(g_hWndTask) { g_hWndMain = GetSafeHwnd(); g_fnProcTask = (WNDPROC)::GetWindowLong(g_hWndTask, GWL_WNDPROC); ::SetWindowLong(g_hWndTask, GWL_WNDPROC, (LONG)TaskWndProc); } return g_hWndTask != NULL; } // CMainFrame::FreeTaskBar // // Free the task bar // BOOL CMainFrame::FreeTaskBar() { // // Already freed? // if(!g_fnProcTask) return FALSE; ::SetWindowLong(g_hWndTask, GWL_WNDPROC, (LONG)g_fnProcTask); g_fnProcTask = NULL; return TRUE; }

Add an OnActivate handler to the class (WM_ACTIVATE message):

// CMainFrame::OnActivate // // The frame is being activated / deactivated // void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) { CFrameWnd::OnActivate(nState, pWndOther, bMinimized); if(nState == WA_INACTIVE) FreeTaskBar(); else HookTaskBar(); }

Finally, here is the new task bar window proc.

// TaskWndProc // // Handles the WM_LBUTTONUP message // LRESULT TaskWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if(msg == WM_LBUTTONUP) { RECT rc; POINT pt; rc.left = 240 - 26; rc.top = 0; rc.bottom = 26; rc.right = 240; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); if(::PtInRect(&rc, pt)) { ::PostMessage(g_hWndMain, WM_CLOSE, 0, 0); return ::CallWindowProc( g_fnProcTask, hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(200, 0)); } } return ::CallWindowProc(g_fnProcTask, hWnd, msg, wParam, lParam); }

Why the WM_MOUSEMOVE? It simulates the user dragging the stylus out of the button, and thereby restoring it to the normal state.

And that's it.

Related resources:

Discuss

Discuss this article. Here you can write your comments and read comments of other developers.
Rate this article:     Poor Excellent    
 12345 
© 2001-2005 Pocket PC Developer Network, a division of Spb Software House