Click here to Skip to main content
Click here to Skip to main content

Screen capture, window resizing utility with source code

, 1 Jul 2005
Rate this:
Please Sign up or sign in to vote.
This utility uses Lim Bio Liong's excellent Spy++ style Window Finder code to build a screen capture utility (featuring text capture as well as bitmaps) and window resizer/mover. It also demonstrates stay on top and expanding/contracting (i.e. more detail / less detail) dialog boxes.

New - 25th June 2005

  • Added a 'Center' button, which centers the found window on the desktop. (Useful for recording application demos etc..)
  • Now remembers its last position on the desktop and starts there next time.
  • Previous changes

Sample Image - winscraper.gif

Contracted view

Introduction

Lim Bio Liong's excellent CodeProject article (MS Spy++ style Window Finder) does all the hard work relating to picking a window to work with for this utility. I claim no credit whatever for this.

However, Lim Bio Liong's sample project launches the window finder dialog from an SDI application and is provided principally to assist in demonstrating the window finder code.

As a developer, I wanted a simple, free tool that would do all of the following:

  • 'Scrape' the text off a dialog box control or message box, so that it can be pasted as text into an email or whatever. (How often have I dismissed the error box without accurately noting down its contents?)
  • Capture images of controls or windows from my application for documentation purposes etc..
  • Resize my application or web browser windows to a specific size for testing alternate screen resolutions or consistent documentation of my application.

That's what the tool presented in this article sets out to do. As a full time developer I expect to make a lot of use of it from now on!

Along the way, the source code also demonstrates 'stay on top' functionality, and the dialog box expands and contracts to show more or less detail.

Architecture

The first thing I wanted to do was to turn the original SDI app + dialog box into a dialog box application, as the SDI bit was inappropriate to my design.

As an MFC developer, the easiest way was:

  1. Create an MFC dialog box application.
  2. Remove the app wizard created dialog box cpp and h files from the project.
  3. Remove the reference to the dialog box h file in the application cpp file.
  4. Remove the standard dialog box launch code from the application's InitInstance() and replace it with the code needed to launch the Win32 dialog, i.e.:
InitialiseResources();

long lRet = (long)DialogBox

(

(HINSTANCE)m_hInstance, // handle to application instance
(LPCTSTR)MAKEINTRESOURCE
(IDD_DIALOG_SEARCH_WINDOW), // identifies dialog box template 
(HWND)NULL, // handle to owner window 
(DLGPROC)SearchWindowDialogProc // pointer to dialog box procedure 
);

It was also necessary to move some of the global declarations out of the SDI app file and into the windowfinder.cpp file. The result is that the application's InitApp() initializes the resources required for the dialog and then launches the dialog.

Scraping Text from Dialog and Message Boxes

The code in DisplayInfoOnFoundWindow() in windowfinder.cpp receives a handle to the selected window. With this handle, it is simple to get the window's text:

char winTxt[5000];
memset(winTxt, 0, 5000);
SendMessage(hwndFoundWindow, WM_GETTEXT, 5000, (LPARAM)winTxt);

(The MSDN documentation states that this method is preferable to GetWindowText() when getting text from a window outside your application.)

However, some controls do not yield their contents in this way. So, for example, to get the text from a listbox control, we use the following:

  if(strcmp(szClassName, "ListBox") == 0)
  {
      CListBox lb;                // Instantiate a MFC listbox object
      lb.Attach(hwndFoundWindow);        
             // Attach the listbox's HWND to our MFC object
      nItems=lb.GetCount();
      for(count=0; count < nItems; count++)
      {
          lb.GetText(count, strItem);
          strItem+="\r\n";
          strcat(winTxt, strItem);
      }
      lb.Detach();   // Must remember to detach, 
                       // otherwise when lb goes out of scope
                        // it will destroy the listbox prematurely!
  }

(It would be nice to capture the text from more complex controls, such as tree controls, using a similar approach but to date such an approach eludes me. Even myTreeCtrl.GetItemText(myTreeCtrl.GetRootItem() returns an empty string, for some reason.)

Capturing the Window to the Windows Clipboard

This is processed in SearchWindowDialogProc() - (search windowfinder.cpp for "if(wID == IDC_CAPTURE_BUTTON)").

A global is already set to the handle of the target window before SearchWindowDialogProc() is called, so we already have a handle to the window to capture.

We need to temporarily hide our application in case it overlaps the capture target, then we capture the screen area to a compatible bitmap and insert it into the clipboard, finally restoring our application's window.

The biggest challenge here was that I initially thought you should use the device context of the window you want to capture to create the compatible bitmap - however this led to the 'wrong' area being captured. Using "hdc=GetDC(HWND_DESKTOP)" gets things capturing correctly.

Moving and Resizing the Target Window

The code in DisplayInfoOnFoundWindow() in windowfinder.cpp receives a handle to the selected window. With this handle, it is simple to resize the target window (where h and w are the desired height and width of the target window):

WINDOWPLACEMENT wp;
GetWindowPlacement(g_hwndFoundWindow, &wp);
// Adjust width and height to required
wp.rcNormalPosition.right=wp.rcNormalPosition.left+w;
wp.rcNormalPosition.bottom=wp.rcNormalPosition.top+h;
// Set window placement
SetWindowPlacement(g_hwndFoundWindow, &wp);

Similarly, moving the window to a screen location is easily accomplished via the Get/SetWindowPlacement() Win32 functions.

Stay on Top

Making the dialog stay on top of all other windows is easy, but not documented in a sufficiently clear enough way for my simple mind to understand in the MS docs:

To set the stay on top property:

SetWindowPos( hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, 
    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW );

To remove it:

SetWindowPos( hwndDlg, HWND_NOTOPMOST, 0, 0, 0, 0, 
    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW );

More / Less Detail on Dialog

We can make the dialog show more or less detail by resizing it using the position of a visible (or hidden) control on the dialog as a marker.

The following code block handles expanding / contracting the dialog:

WINDOWPLACEMENT dlgwp;
GetWindowPlacement(hwndDlg, &dlgwp);
CRect rect;
UINT nBtnState=IsDlgButtonChecked(hwndDlg, IDC_EXPANDVIEW_CHECK);
if(nBtnState == BST_CHECKED) // Expanded view
{
// Get window placement of bottom groupbox, 
// which is the marker for the expanded view
GetWindowRect(GetDlgItem(hwndDlg, IDC_DETAILS_STATIC), &rect);
dlgwp.rcNormalPosition.bottom=rect.bottom+8;
SetWindowPlacement(hwndDlg, &dlgwp);
}
else // Shrunk view
{
// Get window placement of window text static, which 
// is the marker for the expanded view
GetWindowRect(GetDlgItem(hwndDlg, IDC_WINTXT_STATIC), &rect);
dlgwp.rcNormalPosition.bottom=rect.top+2;
SetWindowPlacement(hwndDlg, &dlgwp);
}

The IDC_DETAILS_STATIC ID refers to the static groupbox labeled "Details:" at the bottom of the expanded application window.

The IDC_WINTXT_STATIC ID refers to the static groupbox labeled "Window Text" on the dialog.

As you can see, we get the initial dimensions of the dialog and marker statics using GetWindowPlacement() and GetWindowRect() and adjust the WINDOWPLACEMENT's rcNormalPosition members. Finally we use the adjusted WINDOWPLACEMENT structure to set the new size of the dialog.

Conclusion

Hopefully what we have here is a reasonably useful little tool for software / web site developers. Along the way, I learned a little more about Win32 (but think I will stick with MFC given the choice!). I hope you find the tool useful, even if the source code is a mixture of Lim Bio Liong's original work and my quick and dirty (but hell, they seem to work!) hacks.

Finally, if you do find the tool useful, I'd really appreciate it if you could kindly take the time to click on the link to http://www.powerprogrammer.co.uk/ at the top right of the tool's dialog. Oh, and also, how about giving this article a nice rating so that others can be alerted to the benefits too! Thanks!

History

  • Updated source and exe on 5 January 2004
  • Updated source and exe on 25 June 2005

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

peterboulton
Web Developer Data Perceptions
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
QuestionIs there a way to convert this to VB.Net? Pinmembercaeanis24-Feb-08 8:58 
AnswerRe: Is there a way to convert this to VB.Net? Pinmemberpeterboulton25-Feb-08 0:01 
GeneralRe: Is there a way to convert this to VB.Net? Pinmembercaeanis25-Feb-08 4:24 
GeneralRe: Is there a way to convert this to VB.Net? Pinmemberpeterboulton25-Feb-08 4:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140814.1 | Last Updated 1 Jul 2005
Article Copyright 2003 by peterboulton
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid