tags:

views:

917

answers:

2

With Windows Presentation Foundation, if I have an HWND, how can I capture it's window as an image that I can manipulate and display?

+6  A: 

You can:

  1. CreateBitmap() to create a hBitmap
  2. Call GetDC() on the hWnd
  3. BitBlt() the contents to the hBitmap
  4. ReleaseDC()
  5. Call Imaging.CreateBitmapSourceFromHBitmap() to create a managed BitmapSource
  6. DeleteObject() on the hBitmap
  7. Use the BitmapSource as desired

Steps 1-4 and 6 use the Win32 API (GDI to be precise), Steps 5 and 7 are done using WPF

Ray Burns
Thanks! I've got it all setup and compiling without issue. I'm still note sure how to apply the BitmapSource to my <Image Name="windowimage" Stretch="Fill" Width="200" Height="200" />
directedition
In code: Image.Source = myImageSource. In XAML: <Image Source="{Binding myImageSource}" />
Ray Burns
It's `DeleteObject()` http://msdn.microsoft.com/en-us/library/dd183539(VS.85).aspx not Release
Ciantic
@Ciantic: Thanks for catching my mistake. I've corrected my answer.
Ray Burns
+1  A: 

While above answer is great, this would have saved a ton of time too:

Using section:

using System;
using System.Windows;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Windows.Media.Imaging;

Code:

/// <summary>
/// Captures screenshot using WINAPI.
/// </summary>
static public class CaptureScreenshot
{
    /// <summary>
    /// Capture the screenshot.
    /// <param name="area">Area of screenshot.</param>
    /// <returns>Bitmap source that can be used e.g. as background.</returns>
    /// </summary>
    public static BitmapSource Capture(Rect area)
    {
        IntPtr screenDC = GetDC(IntPtr.Zero);
        IntPtr memDC = CreateCompatibleDC(screenDC);
        IntPtr hBitmap = CreateCompatibleBitmap(screenDC, (int)SystemParameters.VirtualScreenWidth, (int)SystemParameters.VirtualScreenHeight);
        SelectObject(memDC, hBitmap); // Select bitmap from compatible bitmap to memDC

        // TODO: BitBlt may fail horribly
        BitBlt(memDC, 0, 0, (int)area.Width, (int)area.Height, screenDC, (int)area.X, (int)area.Y, TernaryRasterOperations.SRCCOPY);
        BitmapSource bsource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

        DeleteObject(hBitmap);
        ReleaseDC(IntPtr.Zero, screenDC);
        ReleaseDC(IntPtr.Zero, memDC);
        return bsource;
    }

    #region WINAPI DLL Imports

    [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
    static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

    [DllImport("gdi32.dll")]
    private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

    [DllImport("gdi32.dll", SetLastError = true)]
    private static extern IntPtr CreateCompatibleDC(IntPtr hdc);

    [DllImport("gdi32.dll")]
    private static extern bool DeleteObject(IntPtr hObject);

    [DllImport("gdi32.dll")]
    private static extern IntPtr CreateBitmap(int nWidth, int nHeight, uint cPlanes, uint cBitsPerPel, IntPtr lpvBits);

    [DllImport("user32.dll")]
    private static extern IntPtr GetDC(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    private enum TernaryRasterOperations : uint
    {
        /// <summary>dest = source</summary>
        SRCCOPY = 0x00CC0020,
        /// <summary>dest = source OR dest</summary>
        SRCPAINT = 0x00EE0086,
        /// <summary>dest = source AND dest</summary>
        SRCAND = 0x008800C6,
        /// <summary>dest = source XOR dest</summary>
        SRCINVERT = 0x00660046,
        /// <summary>dest = source AND (NOT dest)</summary>
        SRCERASE = 0x00440328,
        /// <summary>dest = (NOT source)</summary>
        NOTSRCCOPY = 0x00330008,
        /// <summary>dest = (NOT src) AND (NOT dest)</summary>
        NOTSRCERASE = 0x001100A6,
        /// <summary>dest = (source AND pattern)</summary>
        MERGECOPY = 0x00C000CA,
        /// <summary>dest = (NOT source) OR dest</summary>
        MERGEPAINT = 0x00BB0226,
        /// <summary>dest = pattern</summary>
        PATCOPY = 0x00F00021,
        /// <summary>dest = DPSnoo</summary>
        PATPAINT = 0x00FB0A09,
        /// <summary>dest = pattern XOR dest</summary>
        PATINVERT = 0x005A0049,
        /// <summary>dest = (NOT dest)</summary>
        DSTINVERT = 0x00550009,
        /// <summary>dest = BLACK</summary>
        BLACKNESS = 0x00000042,
        /// <summary>dest = WHITE</summary>
        WHITENESS = 0x00FF0062
    }

    [DllImport("gdi32.dll")]
    private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

    #endregion
}

Notice that this takes screenshot of given area in the screen and not of window. It works for my purposes, you probably have to modify it for yours :)

Ciantic
Thanks. I did something very similar. I would .hide() all the windows I don't want, screencap the one I do, and restore the others after.
directedition