1

I have code that takes a screenshot...

Size ssSize;
int ssX, ssY, ssWidth, ssHeight;
Bitmap thisScreenshot;
Graphics gfxScreenshot;

public Image Screenshot()
{
ssX = Screen.PrimaryScreen.Bounds.X;
ssY = Screen.PrimaryScreen.Bounds.Y;
ssWidth = Screen.PrimaryScreen.Bounds.Width;
ssHeight = Screen.PrimaryScreen.Bounds.Height;
ssSize = Screen.PrimaryScreen.Bounds.Size;
thisScreenshot = new Bitmap(ssWidth,ssHeight);
gfxScreenshot = Graphics.FromImage(thisScreenshot);
return((Image)gfxScreenshot.CopyFromScreen(ssX, ssY, 0, 0, ssSize));
}

On W7, the resulting image includes the pixels of the calling window; but on XP it does not. I would like the image to always include the pixels of the calling process/window. Any clue how I can force this?

UPDATE1: I've done more experimentation with this, and as a result I'm more confused... I took the above code and created a totally separate application so that there is no relationship between this and the application that I was originally launching it from. Strangely enough, I am STILL not seeing the window of that application in the screenshot. So now I have no relationship between the process doing the screenshot and the window that I want included in the screenshot; yet, that window is still not included. I did try the PRNT-SCRN button and that does include the window. Note that this is only a problem on XP.

Ed.
  • 928
  • 1
  • 10
  • 23
  • I've actually tried this two different ways. In the first (original) case the above method is part of the invoking process whose window I want to capture (as part of the overall screen). In the second case I start a process and invoke it from there. In both cases XP does not include the parent window. My guess is that for some reason all ancestors of the invoking process are not included in the screen capture. Hopefully there is a way around this. – Ed. Apr 19 '12 at 17:16
  • Groping in the dark here ... are you certain that the calling program's window is actually being displayed at the time you call this function? If you do this in Form.Create, for example, it's possible that the window hasn't been rendered yet. – Jim Mischel Apr 19 '12 at 18:00
  • Good thought, but no. I bring up the application and just have a button (essentially) that I click to do the screenshot. – Ed. Apr 19 '12 at 18:10

1 Answers1

4

Set your form's Opacity property to 100 and right-click the TransparencyKey property and select Reset. That ensures that your window is no longer a layered window and won't be missing from the screenshot.

If you want to keep these properties then you'll have to work around in a bug in Graphics.CopyFromScreen(). The overload that uses CopyPixelOperation with the CaptureBlt operation is required to capture layered windows. But won't work due to a bug in the argument validation code. The workaround isn't pretty but functional:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e) {
            Size sz = Screen.PrimaryScreen.Bounds.Size;
            IntPtr hDesk = GetDesktopWindow();
            IntPtr hSrce = GetWindowDC(hDesk);
            IntPtr hDest = CreateCompatibleDC(hSrce);
            IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
            IntPtr hOldBmp = SelectObject(hDest, hBmp);
            bool b = BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
            Bitmap bmp = Bitmap.FromHbitmap(hBmp);
            SelectObject(hDest, hOldBmp);
            DeleteObject(hBmp);
            DeleteDC(hDest);
            ReleaseDC(hDesk, hSrce);
            bmp.Save(@"c:\temp\test.png");
            bmp.Dispose();
        }

        // P/Invoke declarations
        [DllImport("gdi32.dll")]
        static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int
        wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
        [DllImport("user32.dll")]
        static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
        [DllImport("gdi32.dll")]
        static extern IntPtr DeleteDC(IntPtr hDc);
        [DllImport("gdi32.dll")]
        static extern IntPtr DeleteObject(IntPtr hDc);
        [DllImport("gdi32.dll")]
        static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
        [DllImport("gdi32.dll")]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll")]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr ptr);
    }
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Bingo! Your first suggestion (opacity=100, reset transparency) did the trick. I had the transparency key set, and prior to this I was dabbling with opacity, so apparently I didn't properly "undo" that. Thanks for your help! – Ed. Apr 19 '12 at 19:02