26

I have a bit of code to capture windows desktop app contents and save to a Bitmap object in .NET. It uses User32.dll and Gdi32.dll (BitBlt) and works just fine. However, the code produces all-black bitmaps when I give the code a handle to a window that holds a Windows Store app. I'm not sure if this is a security feature or what. I cannot use the ScreenCapture api as the contents of the window, after being resized, are almost always taller/larger than the screen. Has anyone had any luck capturing window contents even when they're larger than the screen, for a Windows Store app?

EDIT: Just as a note I am trying to capture a different program's window, not my own program. My program can be assumed to be a Windows Console application in .NET 4.6.1 / C#

Also, I know that this must be possible somehow in Windows APIs, because the Aero Peek feature, where if you hover over the taskbar on the running program's icon shows the full height of the window, including offscreen components. (see tall window on right, set to 6000px much higher than my display)

see tall window on right, set to 6000px much higher than my display

Richthofen
  • 2,076
  • 19
  • 39
  • I can't close as a duplicate because of the bounty, but have you seen: [Capture screen of Windows store App](http://stackoverflow.com/questions/15883332/capture-screen-of-windows-store-app) – chue x Jun 24 '16 at 20:01
  • Do you own that app? Because in the general case, if an app has portions off-screen, nothing can guarantee that what's off-screen can be rendered physically. In the general case, you can only capture what's displayed on the screen (with graphics.CopyFromScreen for example). – Simon Mourier Jun 25 '16 at 08:01
  • No, I do not own the app. I am trying to capture the windows store app from a Console application written in .NET 4.6.1. – Richthofen Jun 27 '16 at 19:53
  • Have a look at the provided solution below. – Mavi Domates Jun 30 '16 at 15:00
  • @Richtofen : Any luck? I am trying to get a screen shot of running windows store apps from another app. – JD. Apr 03 '17 at 15:16

2 Answers2

3

As of Windows 8.1, you can use Windows.UI.Xaml.Media.Imaging.RenderTargetBitmap to render elements to a bitmap. There are a couple of caveats to this:

  1. You can capture elements that are offscreen, as long as they are in the XAML visual tree and have Visibility set to Visible and not Collapsed.
  2. Some elements, like video, won't be captured.

See the API for more details:

https://msdn.microsoft.com/library/windows/apps/xaml/windows.ui.xaml.media.imaging.rendertargetbitmap.aspx

Daniel A. Thompson
  • 1,904
  • 1
  • 17
  • 26
  • Unfortunately I cannot use the RenderTargetBitmap because I don't have access to the XAML tree of the app. I am running the capture from a separate app than the one I'm trying to capture. – Richthofen Jun 27 '16 at 19:54
  • Ah.. as far as I know there is no way to do this. I would clarify your question to make it clear that you're trying to capture the window contents from a separate app. Good luck though - this would be interesting to me as well if it's possible. – Daniel A. Thompson Jun 27 '16 at 19:58
3

This might do the trick. Basically get the window handle to the app, call the native functions on it to figure out the app window position, provide those do the graphics class and copy from the screen.

class Program
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindow(string strClassName, string strWindowName);

    [DllImport("user32.dll")]
    public static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);

    public struct Rect
    {
        public int Left { get; set; }
        public int Top { get; set; }
        public int Right { get; set; }
        public int Bottom { get; set; }
    }


    static void Main(string[] args)
    {
        /// Give this your app's process name.
        Process[] processes = Process.GetProcessesByName("yourapp");
        Process lol = processes[0];
        IntPtr ptr = lol.MainWindowHandle;
        Rect AppRect = new Rect();
        GetWindowRect(ptr, ref AppRect);
        Rectangle rect = new Rectangle(AppRect.Left, AppRect.Top, (AppRect.Right - AppRect.Left), (AppRect.Bottom - AppRect.Top));
        Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
        Graphics g = Graphics.FromImage(bmp);
        g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);

        // make sure temp directory is there or it will throw.
        bmp.Save(@"c:\temp\test.jpg", ImageFormat.Jpeg);
    }
}
Mavi Domates
  • 4,262
  • 2
  • 26
  • 45
  • So, this will probably work for cases where you only need screen content. However in my case my window is larger than the screen (screen height 2000 pixels but the window height / content is something around 6000 pixels). – Richthofen Jun 30 '16 at 16:02
  • Similar to selenium's printscreen (although the window I am trying to capture is not a web browser) I want to capture the entire buffer of window contents in one go. – Richthofen Jun 30 '16 at 16:03
  • Ah got it. So you actually need the inner content of the app which needs scrolling normally, you want that to be extracted to an image. – Mavi Domates Jun 30 '16 at 16:04