Click here to Skip to main content
15,892,809 members
Articles / Desktop Programming / Windows Forms

How to extract images from child windows of running applications

Rate me:
Please Sign up or sign in to vote.
2.94/5 (11 votes)
31 Aug 2009CPOL3 min read 39.3K   498   23   25
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.

C#
[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:

C#
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.

C#
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.

C#
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)


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHmm - have you tried the opposite? Pin
mikejobu235-Feb-10 15:06
mikejobu235-Feb-10 15:06 
AnswerRe: Hmm - have you tried the opposite? Pin
Jon Burchel24-Feb-10 20:12
Jon Burchel24-Feb-10 20:12 
GeneralWebpage image extraction Pin
sonu.saini.76@gmail.com26-Aug-09 6:47
sonu.saini.76@gmail.com26-Aug-09 6:47 
GeneralRe: Webpage image extraction Pin
Jon Burchel31-Aug-09 10:57
Jon Burchel31-Aug-09 10:57 
GeneralGreat Work! Pin
rbunn8381520-Mar-09 16:47
rbunn8381520-Mar-09 16:47 
This is exactly what I needed to solve a current problem I have been struggling with. Your effort is greatly appreciated! Smile | :)
GeneralVery Nice! Pin
C Grant Anderson3-Feb-09 10:26
professionalC Grant Anderson3-Feb-09 10:26 
GeneralVery much appreciated Pin
RadiantThunder16-Sep-08 0:40
RadiantThunder16-Sep-08 0:40 
GeneralRe: Very much appreciated Pin
Jon Burchel16-Sep-08 2:32
Jon Burchel16-Sep-08 2:32 
GeneralRe: Very much appreciated Pin
rpopple1-Mar-09 4:54
rpopple1-Mar-09 4:54 
GeneralRe: Very much appreciated Pin
Jon Burchel1-Mar-09 12:10
Jon Burchel1-Mar-09 12:10 
GeneralRe: Very much appreciated Pin
rpopple1-Mar-09 16:25
rpopple1-Mar-09 16:25 
GeneralRe: Very much appreciated Pin
Jon Burchel1-Mar-09 19:28
Jon Burchel1-Mar-09 19:28 
GeneralRe: Very much appreciated Pin
rpopple2-Mar-09 13:28
rpopple2-Mar-09 13:28 
Generaltry posting some code explanation it makes for a better article Pin
Sacha Barber30-Jun-08 20:53
Sacha Barber30-Jun-08 20:53 
GeneralRe: try posting some code explanation it makes for a better article Pin
Jon Burchel1-Jul-08 19:11
Jon Burchel1-Jul-08 19:11 
GeneralRe: try posting some code explanation it makes for a better article Pin
Sacha Barber1-Jul-08 20:10
Sacha Barber1-Jul-08 20:10 
GeneralGarbage Pin
tonyt30-Jun-08 16:53
tonyt30-Jun-08 16:53 
GeneralInconsiderate Pin
tonyt30-Jun-08 16:18
tonyt30-Jun-08 16:18 
GeneralRe: Inconsiderate Pin
Jon Burchel30-Jun-08 17:15
Jon Burchel30-Jun-08 17:15 
GeneralRe: Inconsiderate [modified] Pin
Ilíon1-Jul-08 1:19
Ilíon1-Jul-08 1:19 
GeneralRe: Inconsiderate Pin
Jon Burchel1-Jul-08 19:14
Jon Burchel1-Jul-08 19:14 
GeneralRe: Inconsiderate Pin
Ilíon2-Jul-08 2:20
Ilíon2-Jul-08 2:20 
GeneralRe: Inconsiderate Pin
Jon Burchel4-Jul-08 21:35
Jon Burchel4-Jul-08 21:35 
GeneralRe: Inconsiderate Pin
Ilíon5-Jul-08 0:00
Ilíon5-Jul-08 0:00 
AnswerRe: Inconsiderate Pin
Jon Burchel5-Jul-08 16:37
Jon Burchel5-Jul-08 16:37 

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

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