none
BitBlt from desktop, not grabbing everything, anyone got an idea?

    Question

  • I have a program that repeatedly grabs screenshots of one display (on a multi-display machine), and displays the grabbed image on one of the other displays. The main purpose of this screen is to enable live zooming and manipulation of the image on the way, like overlays and highlighting, etc.

    I got a problem with this program, and the problem is that it won't actually grab *everything* on the screen. Seems the screen is divided into layers, where some windows is on layers not available to my program. Instead I get the screen as though those windows where never on the screen in the first place.

    I have accepted that this is how it when it comes to some visual effects, like the shadow beneath menus, etc. but today I experienced the same problem with some debug tooltips in Visual Studio, and since my main reason for building the software was to make it easier to follow what I was doing when I am holding live demonstrations or similar of code and software, I decided that I really need to figure out what the problem really is.

    I have built a complete program that I will post in a reply to this question which will, on a dual-screen (or more) machine, show a form on screen 2, with a mirror of what is on screen 1, updated about every 100 milliseconds. If someone could grab the code and run it, and test it for me, and tell me where I'm going wrong, or anything basically that *might* help, that would be awesome. Basically I'm looking for any ideas at all, to find some way of fixing this.

    To test, you will need to:
    1. grab the code, compile, and run it by starting the .EXE file from Explorer
    2. load up a different project, or make one, that you can stop with a debugger
    3. stop the other project on a line and inspect a variable in your code, when you hold the mouse cursor over the variable, the tooltip will show on your main display, but will not be mirrored onto the second, just as though it was never there to begin with

    I was originally using Graphics.CopyFromScreen, but it suffered from the same problem and also had a GDI handle leak to boot, so that's not a path you need to go down, but any and all ideas other than that is very much welcome.

    http://presentationmode.blogspot.com/
    Friday, June 06, 2008 5:37 PM

Answers

  •  Use a mirror driver to repetitively capture shots of the screen.  See VNC ultra.  Or use a 3rd party app.  Camtasia, I think is one.  Look on SourceForge for open source versions.
    Friday, June 06, 2008 6:00 PM
  • You are definitely not using a mirror driver. 
    Friday, June 06, 2008 6:18 PM
  • I can't speak for vista, but on xp the following might be useful:

    take a look at the gdi code on this page: http://www.codeproject.com/KB/dialog/screencap.aspx

    then you also need to enumerate your display devices properly:
    http://vbnet.mvps.org/index.html?code/enums/enumdisplaydevices.htm

    I have used these to write an app for my dual monitor system, that takes screenshots either from my primary, secondary or both desktops at specified intervals.

    good luck
    Saturday, June 07, 2008 12:02 AM
  • CAPTUREBLT is the key to be able to capture layered windows.  Check this thread.
    Hans Passant.
    Saturday, June 07, 2008 7:27 PM

All replies

  • using System; 
    using System.Windows.Forms; 
    using System.Drawing; 
    using System.Runtime.InteropServices; 
    using System.ComponentModel; 
    namespace TestApplication 
        public class Program 
        { 
            private class MirrorForm : Form 
            { 
                public MirrorForm() 
                { 
                    SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.Opaque, true); 
                } 
            } 
     
            private static MirrorForm _Form; 
            private static Bitmap _Mirror; 
     
            public static void Main() 
            { 
                _Form = new MirrorForm(); 
                _Form.StartPosition = FormStartPosition.Manual; 
                _Form.Bounds = Screen.AllScreens[1].Bounds; 
                _Form.Show(); 
                _Mirror = new Bitmap(_Form.Width, _Form.Height); 
     
                Timer t = new Timer(); 
                t.Tick += new EventHandler(t_Tick); 
                t.Enabled = true
                Application.Run(_Form); 
     
                GC.KeepAlive(t); 
            } 
     
            [DllImport("gdi32", CharSet = CharSet.Unicode, SetLastError = true)] 
            public static extern Boolean BitBlt(IntPtr hdc, Int32 nXDest, Int32 nYDest, Int32 nWidth, 
                Int32 nHeight, IntPtr hdcSrc, Int32 nXSrc, Int32 nYSrc, TernaryRasterOperations dwRop); 
     
            public enum TernaryRasterOperations : uint 
            { 
                SRCCOPY = 0x00CC0020, SRCPAINT = 0x00EE0086, SRCAND = 0x008800C6, SRCINVERT = 0x00660046, 
                SRCERASE = 0x00440328, NOTSRCCOPY = 0x00330008, NOTSRCERASE = 0x001100A6, MERGECOPY = 0x00C000CA, 
                MERGEPAINT = 0x00BB0226, PATCOPY = 0x00F00021, PATPAINT = 0x00FB0A09, PATINVERT = 0x005A0049, 
                DSTINVERT = 0x00550009, BLACKNESS = 0x00000042, WHITENESS = 0x00FF0062, 
            }; 
     
            static void t_Tick(object sender, EventArgs e) 
            { 
                using (Graphics g = Graphics.FromHwnd(_Form.Handle)) 
                { 
                    using (Graphics source = Graphics.FromHwnd(IntPtr.Zero)) 
                    { 
                        HandleRef sourceHdc = new HandleRef(null, source.GetHdc()); 
                        try 
                        { 
                            HandleRef targetHdc = new HandleRef(null, g.GetHdc()); 
                            try 
                            { 
                                Boolean rc = BitBlt( 
                                    targetHdc.Handle, 0, 0, Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height, 
                                    sourceHdc.Handle, Screen.AllScreens[0].Bounds.Left, Screen.AllScreens[0].Bounds.Top, 
                                    TernaryRasterOperations.SRCCOPY); 
                                if (!rc) 
                                    throw new Win32Exception(); 
                            } 
                            finally 
                            { 
                                g.ReleaseHdc(); 
                            } 
                        } 
                        finally 
                        { 
                            source.ReleaseHdc(); 
                        } 
                    } 
     
                } 
            } 
        } 
     


    http://presentationmode.blogspot.com/
    Friday, June 06, 2008 5:38 PM
  • I forgot to mention, you need to build this as a Winforms application, and at least add references to System.Drawing and System.Windows.Forms.

    Again, any help, ideas, shots in the dark, etc. will be appreciated.

    http://presentationmode.blogspot.com/
    Friday, June 06, 2008 5:39 PM
  •  Use a mirror driver to repetitively capture shots of the screen.  See VNC ultra.  Or use a 3rd party app.  Camtasia, I think is one.  Look on SourceForge for open source versions.
    Friday, June 06, 2008 6:00 PM
  • I've looked at the VNC source code, and it appears they're doing the same as I am doing, but then I don't have that much experience with C++ that I can follow all the intricate ways things are happening, like how handles are deallocated automatically, etc.
    http://presentationmode.blogspot.com/
    Friday, June 06, 2008 6:12 PM
  • You are definitely not using a mirror driver. 
    Friday, June 06, 2008 6:18 PM
  • I would gather that a mirror driver would not be possible to write in C#, I will need to look more into this.

    In the meantime, does anyone know if it will be possible to "fix" my current code in some way?

    http://presentationmode.blogspot.com/
    Friday, June 06, 2008 6:20 PM
  • Some things can't be done at the present time using managed code.  Screen shots used to work quite well in the time of Windows 95.  The Vista generation is a new era. 
    Friday, June 06, 2008 6:32 PM
  • I can't speak for vista, but on xp the following might be useful:

    take a look at the gdi code on this page: http://www.codeproject.com/KB/dialog/screencap.aspx

    then you also need to enumerate your display devices properly:
    http://vbnet.mvps.org/index.html?code/enums/enumdisplaydevices.htm

    I have used these to write an app for my dual monitor system, that takes screenshots either from my primary, secondary or both desktops at specified intervals.

    good luck
    Saturday, June 07, 2008 12:02 AM
  • Thanks, I'll look at that, but at first glance it looks as though they are doing something similar to what I'm doing for GDI. I noticed the CAPTUREBLT raster operation there, but apart from making my mouse cursor blink, it didn't help.

    I'll try to grab all the code on that and check if it works better than mine.

    http://presentationmode.blogspot.com/
    Saturday, June 07, 2008 8:55 AM
  • CAPTUREBLT is the key to be able to capture layered windows.  Check this thread.
    Hans Passant.
    Saturday, June 07, 2008 7:27 PM
  • JohnWein said:

     Use a mirror driver to repetitively capture shots of the screen.  See VNC ultra.  Or use a 3rd party app.  Camtasia, I think is one.  Look on SourceForge for open source versions.

    "Worst software product of the year" award

    I'd like to nominate VNC for this fictional award.  I just logged into one of our webservers in Terminal Services and it didn't even break a sweat.  Afterward I logged into     It using VNC and it ground to a halt and sat at 100% CPU usage for about 15 minutes
    .

    http://weblogs.asp.net/acampbell/archive/2004/10/09/240066.aspx


    AlexB
    Sunday, June 08, 2008 1:26 AM
  • That thread-link was exactly what I needed, along with the other link above in this thread.

    Many thanks.

    While this is still not as good as a mirror driver, which I've made a new case for in my tracking system for a future version, it will allow me to fix my program today and not be much worse off in terms in performance.

    The code will make my mouse cursor flicker, but since this program is about copying my laptop screen to a projector display, with image manipulation along the way, I can prevent the mouse cursor from flickering from the audience viewing the projector display, so that is an acceptable solution for me.

    Thanks to everyone that gave me pointers, much appreciated.

    http://presentationmode.blogspot.com/
    Sunday, June 08, 2008 9:08 AM