|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThis article demonstrates yet another effective use of the WindowFinder utility that I presented in my CodeProject article early in January this yearIt also incorporates the screen capture routine presented by Joseph M. Newcomer in his CodeProject article The demo app is a Sys Tray utility that activates a context menu when doubled-clicked. Using the demo app, you are able to capture any window or control anywhere on the screen and save the screen-captured bitmap into the clipboard. After that, you'll be able to paste the bitmap from the clipboard onto any bitmap editing tool of your choice. Although my main intention is to give a demonstration of one possible use of the Window Finder utility, such a screen capture utility is pretty useful all on its own. Although you can always use the "PrintScreen" button or the "Alt+PrintScreen" buttons, this utility allows you to precisely select windows and controls. Thus saving you the trouble of having to perform further bitmap area selection and cutting and pasting. Usage
Summary Of How It WorksPlease read through my "MS Spy++ Window Finder" article to understand how the window finder part of this demo app works. I have made extensive re-use of the source codes of the WindowFinder app. The following is a summary of the enhancements and transformations that were made to the original WindowFinder source codes :
Detailed Explanation Of How It WorksIn order to make a System Tray app, I have removed theWS_VISIBLE style from the main window of the application and explicitly hidden it after creation :
// Enhancement to WindowFinder - main window will not have // WS_VISIBLE style. dwStyle = WS_OVERLAPPEDWINDOW; // Create the main window. g_hwndMainWnd = CreateWindow ( szMainWindowClassName, // name of window class WINDOW_SNAPSHOT_MAIN_WINDOW_TITLE, // title dwStyle, // window style - normal CW_USEDEFAULT, // X coordinate - let Windows decide CW_USEDEFAULT, // Y coordinate - let Windows decide CW_USEDEFAULT, // width - let Windows decide CW_USEDEFAULT, // height - let Windows decide NULL, // no parent window NULL, // no override of class menu hInstance, // handle for this instance NULL // no additional arguments ); ... ... ... // Display the window. ShowWindow(g_hwndMainWnd, SW_HIDE); // Enhancement to WindowFinder - hide the main window. UpdateWindow(g_hwndMainWnd); The main window will still be required to process messages for the Sys Tray icon. I have also defined a new function BOOL InitialiseShellModules()
{
NOTIFYICONDATA nid;
BOOL bRetTemp = FALSE;
BOOL bRet = TRUE;
g_hIconSysTray = (HICON)LoadImage
(
(HINSTANCE)g_hInst,
// handle of the instance that contains the image
(LPCTSTR)MAKEINTRESOURCE(IDI_ICON_SYS_TRAY),
// name or identifier of image
(UINT)IMAGE_ICON, // type of image
(int)16, // desired width
(int)16, // desired height
(UINT)0 // load flags
);
memset (&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndMainWnd;
nid.uID = ICON_ID;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
nid.hIcon = g_hIconSysTray;
nid.uCallbackMessage = WM_SYS_TRAY_MESSAGE;
strcpy (nid.szTip, WINDOW_SNAPSHOT_TOOL_TIP);
bRetTemp = Shell_NotifyIcon
(
(DWORD)NIM_ADD, // message identifier
(PNOTIFYICONDATA)&nid // pointer to structure
);
g_dwLastError = GetLastError ();
return bRet;
}
The function contains basic sys tray initialization and startup code. The counterpart of As can be seen in case WM_SYS_TRAY_MESSAGE: { lRet = 0; bCallDefWndProc = TRUE; if (wParam == ICON_ID) { // Enhancement - Bio/Andrew Peace. Invoke context menu // only when RIGHT-mouse click. if (lParam == WM_RBUTTONDOWN) { // Enhancement to Window Finder - display context menu only // if we currently allow it. if (g_bAllowContextMenu) { DisplayContextMenu (hwnd); } } // Enhancement - Bio/Andrew Peace. Invoke Search Window Dialog // directly when icon is double-clicked. if (lParam == WM_LBUTTONDBLCLK) { // Enhancement to Window Finder - display the Search Window // Dialog box immediately. If the Search Window Dialog is // already alive, put it in the foreground. if (g_hwndSearchDialog) { SetForegroundWindow (g_hwndSearchDialog); } else { PostMessage (hwnd, WM_START_SEARCH_WINDOW, 0, 0); } } } break; } When the user right-clicks on the icon, we first check the We may, of course, opt to disable the "Take A Snapshot" menu item. This is a good idea, especially if there are any other options in the context menu not related directly to window selection. This will certainly complicate the code further but it can be done. When the user double-clicks on the sys tray icon, to directly start up the Search Window Dialog Box, we first check the global variable If the Search Window Dialog Box is not running at the moment (null value in The various handlers for the context menu items are listed below. They are part of the case WM_COMMAND: { WORD wNotifyCode = HIWORD(wParam); // notification code WORD wID = LOWORD(wParam); // item, control, or accelerator identifier HWND hwndCtl = (HWND)lParam; // handle of control if (wNotifyCode == 0) { // Message is from a menu. // Enhancement to Window Finder - perform // a window snapshot operation. if (wID == IDM_CONTEXT_TAKE_SNAPSHOT) { PostMessage (hwnd, WM_START_SEARCH_WINDOW, 0, 0); } if (wID == IDM_CONTEXT_ABOUT) { MessageBox (NULL, ABOUT_WINDOW_FLOATER, WINDOW_SNAPSHOT_MAIN_WINDOW_TITLE, MB_OK); } if (wID == IDM_CONTEXT_SHUTDOWN) { PostMessage (hwnd, WM_CLOSE, 0, 0); } lRet = 0; bCallDefWndProc = FALSE; } else { bCallDefWndProc = TRUE; } break; } Let us examine the handler for case WM_START_SEARCH_WINDOW : { lRet = 0; bCallDefWndProc = FALSE; StartSearchWindowDialog (hwnd); break; } The Window Search Dialog Box is managed by the The important modification to the original if ((wID == IDOK) && (g_hwndFoundWindow)) { // Action. // Important to hide the dialog and make sure it really disappears // before we take the snapshot. ShowWindow (hwndDlg, SW_HIDE); Sleep(500); // That's why we must wait for a while. CaptureWindowToClipboard (g_hwndFoundWindow); } Here, when the user clicks on the "OK" button, and a window or control has been selected, we perform 3 important steps to capture the screenshot of the selected window/control :
The reasons for taking the 3 steps are explained next. In general, before one captures the screen shot of a window or control, that window or control must be entirely visible. It must not be obscured partially or entirely by another window. Please refer to the discussion thread in Joseph Newcomer's "Screen Capture to the Clipboard" article in which this is discussed. As such, please ensure that the selected window/control is totally visible when you click on the "OK" button of the Search Window dialog box. Note that this is most evident when you are performing debugging because you will be brought to the Visual Studio IDE and the selected window may be obscured. However, anticipating that the Search Window Dialog Box itself could be obscuring the selected window/control, I have made sure that I first hide the dialog box before performing the screen capture. Furthermore, while I was doing testing, I noticed that sometimes, even after I have commanded the dialog to hide itself, I still get an image of the dialog box on top of the selected window in the screenshot. The speed of the hiding of any window really depends on the OS itself. To get round this problem, I have added in the I would also like to touch a little on the BOOL CaptureWindowToClipboard (HWND hwndToCapture)
{
BOOL bRet = FALSE;
// Enhancement - Bio/Ahmed. Check first that "hwndToCapture"
// is a valid window.
if((hwndToCapture) && (::IsWindow (hwndToCapture)))
{
bRet = TRUE;
toClipboard_Bio((CWnd *)CWnd::FromHandle (hwndToCapture), TRUE);
}
return bRet;
}
I basically used the void toClipboard_Bio(CWnd * wnd, BOOL FullWnd) { CDC *dc; if(FullWnd) { /* full window */ dc = new CWindowDC(wnd); //HDC hdc = ::GetWindowDC(wnd->m_hWnd); //dc -> Attach(hdc); } /* full window */ else { /* client area only */ dc = new CClientDC(wnd); //HDC hdc = ::GetDC(wnd->m_hWnd); //dc -> Attach(hdc); } /* client area only */ I have created a new function
In Conclusion
Updates, Enhancements And Bug Fixes
| ||||||||||||||||||||