Click here to Skip to main content
Licence CPOL
First Posted 30 Jun 2008
Views 17,562
Downloads 169
Bookmarked 21 times

How to extract images from child windows of running applications

By | 31 Aug 2009 | Article
Want to get those cool images from some Windows app, but can't find them? This app will help you. Also gets animated GIFs.

Introduction

I have often wanted to replicate the look and feel of another application for whatever reason, and it is a pain trying to get good graphics to match. This little app is just a compilation of other code samples I found and a little tweaking of my own, but I never found anything that does just what it does, so posting it up here for other people. It just lets you type the name of a window visible on the desktop, and then enumerates all the child windows of that window, and will extract images from them if found, and show them. Then, you can save them. I also made a feature to extract an animated GIF from those, although it just steals one frame at a time and then reassembles them together since I could not find a way to directly extract the resource. I just get a capture of the image for each window. It is very simple, very hacky, but it works nicely, and finally helped me get what I wanted in several cases. Hope it helps!

Note: The executable is attached above for those who cannot take time to read the description, so you can just click and play to see what it does. If you want to open the project, it requires Visual Studio 2008. You can download a trial of 2008 for absolutely no money whatsoever.

Background

Several other people's publically posted code is included in the snippets. Sorry I don't know who they are. The GIF Encoder and Animator was written by somebody else on here.

Using the code

It is pretty self explanatory and has just a few functions, so you can just download and use or copy parts of it as useful to you. Sorry if my explanation is sparse, but I figured for the few people searching and needing this, having something is better than having nothing, and when I was searching, I couldn't find it.

Anyway, to explain the code a bit, it uses FindWindowEx from Win32 to get the first window with a title matching the string you put in the textbox.

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, 
                                  string lpszClass, string lpszWindow); 

public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.Dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern IntPtr EnumChildWindows(IntPtr parentHandle, 
                     Win32Callback callback, IntPtr lParam);
 
        public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter); 

        public static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }
            return result;
        }

        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
            {
                throw new InvalidCastException("GCHandle Target " + 
                          "could not be cast as List<IntPtr>");
            }
            list.Add(handle);
            //  You can modify this to check to see if you want
            // to cancel the operation, then return a null here
            return true;
        }

No error checking at all, and just done for demonstration purposes. Then, it uses the Win32 EnumChildWindows with the declared delegate EnumWindowProc to enumerate all the child "windows" from that window, which is all the buttons, controls, images, etc., on the form. It just plugs the handle value from each of these into a listbox control. Then, when you click the listbox control, it gets the handle you clicked on and uses the Graphics .NET object generated from the handle and copies the contents of the child window, BitBlting them into the HDC for a bitmap, subsequently assigned to a PictureBox's Image property. This all occurs in the very short ShowImgFromHandle() function:

private const int SRCCOPY = 0x00CC0020;
private const int CAPTUREBLT = 0x40000000;
[DllImport("gdi32.dll")]

private static extern bool BitBlt(
    IntPtr hdcDest,
    int nXDest,
    int nYDest,
    int nWidth,
    int nHeight,
    IntPtr hdcSrc,
    int nXSrc,
    int nYSrc,
    int dwRop
    ); 

private void ShowImgFromHandle(IntPtr hdlChild)
{
    RECT rct;
    GetWindowRect(hdlChild, out rct);
    Rectangle wndRct = rct;
    Graphics gr = Graphics.FromHwnd(hdlChild);
    pictureBox1.Width = wndRct.Width;
    pictureBox1.Height = wndRct.Height;
    if (wndRct.Height == 0 || wndRct.Width == 0)
        MessageBox.Show("This child window doesn't have a valid image.");
    else
    {
        lblResourceInfo.Text = "Width: " + wndRct.Width.ToString() + 
                               ", Height: " + wndRct.Height.ToString();
        Bitmap bmp = new Bitmap(wndRct.Width, wndRct.Height);
        Graphics grBmp = Graphics.FromImage(bmp);
        IntPtr bmpHdc = grBmp.GetHdc();
        BitBlt(bmpHdc, 0, 0, wndRct.Width, wndRct.Height, 
               gr.GetHdc(), 0, 0, CAPTUREBLT | SRCCOPY);
        grBmp.ReleaseHdc();
        pictureBox1.Image = bmp;
    }
}

Finally, if you click Save Bitmap, the SaveBmp function just uses the PictureBox's Image.Save to save it to file.

private void SaveBmp(string FileName)
{
    File.Delete(FileName);
    System.Drawing.Imaging.ImageFormat imgfmt = ImageFormat.Bmp;
    pictureBox1.Image.Save(FileName, imgfmt);
}

Using the AnimatedGifEncoder, LZWEncoder, and NeuQuant classes I took from another project on this site that was cool, I made a Save Animated GIF button. This just calls the ShowImgFromHandle function above 15 times in 1s, and uses the code from the AnimatedGif class to capture each of those images into an animated GIF it creates, just one screenshot at a time. It sets the interval to the same period of the capture (1s), so there is no jumpiness in the final GIF. I have no idea how the AnimatedGifEncoder works, just took it directly from the other project and appreciate that it was posted. It worked great.

private void SaveAnimatedGif(string FileName)
{
    String outputFilePath = FileName;
    AnimatedGifEncoder e = new AnimatedGifEncoder();
    e.Start(outputFilePath);
    e.SetDelay(1000/15);
    //-1:no repeat,0:always repeat

    e.SetRepeat(0);
    for (int i = 0; i < 15; i++) 
    {
        ShowImgFromHandle((IntPtr)listBox1.Items[listBox1.SelectedIndex]);
        System.Threading.Thread.Sleep(1000 / 15);
        e.AddFrame(pictureBox1.Image);
    }
    e.Finish();
}

I do hope this helps some people. This example is not meant to be a read-to-go app, just posted to show a technique that worked for me when I could not find anything else that could.

Good luck, and hope it is helpful to you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jon Burchel

Software Developer

United States United States

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionHmm - have you tried the opposite? Pinmembermikejobu2315:06 5 Feb '10  
AnswerRe: Hmm - have you tried the opposite? PinmemberJon Burchel20:12 24 Feb '10  
GeneralWebpage image extraction Pinmembersonu.saini.76@gmail.com6:47 26 Aug '09  
GeneralRe: Webpage image extraction PinmemberJon Burchel10:57 31 Aug '09  
GeneralGreat Work! Pinmemberrbunn8381516:47 20 Mar '09  
GeneralVery Nice! PinmemberMember 35312110:26 3 Feb '09  
GeneralVery much appreciated PinmemberRadiantThunder0:40 16 Sep '08  
GeneralRe: Very much appreciated PinmemberJon Burchel2:32 16 Sep '08  
GeneralRe: Very much appreciated Pinmemberrpopple4:54 1 Mar '09  
GeneralRe: Very much appreciated PinmemberJon Burchel12:10 1 Mar '09  
GeneralRe: Very much appreciated Pinmemberrpopple16:25 1 Mar '09  
GeneralRe: Very much appreciated PinmemberJon Burchel19:28 1 Mar '09  
GeneralRe: Very much appreciated Pinmemberrpopple13:28 2 Mar '09  
Generaltry posting some code explanation it makes for a better article PinmvpSacha Barber20:53 30 Jun '08  
GeneralRe: try posting some code explanation it makes for a better article PinmemberJon Burchel19:11 1 Jul '08  
GeneralRe: try posting some code explanation it makes for a better article PinmvpSacha Barber20:10 1 Jul '08  
GeneralGarbage Pinmembertonyt16:53 30 Jun '08  
GeneralInconsiderate Pinmembertonyt16:18 30 Jun '08  
GeneralRe: Inconsiderate PinmemberJon Burchel17:15 30 Jun '08  
GeneralRe: Inconsiderate [modified] PinmemberIlíon1:19 1 Jul '08  
GeneralRe: Inconsiderate PinmemberJon Burchel19:14 1 Jul '08  
GeneralRe: Inconsiderate PinmemberIlíon2:20 2 Jul '08  
GeneralRe: Inconsiderate PinmemberJon Burchel21:35 4 Jul '08  
GeneralRe: Inconsiderate PinmemberIlíon0:00 5 Jul '08  
AnswerRe: Inconsiderate PinmemberJon Burchel16:37 5 Jul '08  

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.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 31 Aug 2009
Article Copyright 2008 by Jon Burchel
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid