Ask a questionAsk a question
 

AnswerInterop Between WPF and GDI+

  • Sunday, March 26, 2006 3:20 PMZhou Yong Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I want to write a simple application which can capture the whole screen, and this is what I do:
    private void captureButton_Click(Object sender, RoutedEventArgs e)
            {
                Int32 height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
                Int32 width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
                System.Drawing.Bitmap captureBitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
                System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(captureBitmap);
                g.CopyFromScreen(0, 0, 0, 0, captureBitmap.Size);
                g.Dispose();

                captureImage.Source = CreateBitmapSource(captureBitmap);
            }

            private BitmapSource CreateBitmapSource(System.Drawing.Bitmap gdiBitmap)
            {
                IntPtr bitmapHandle = gdiBitmap.GetHbitmap();
                System.Windows.Int32Rect rect = new Int32Rect(0, 0, gdiBitmap.Height, gdiBitmap.Width);
                BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bitmapHandle,
                                                                                                                                    IntPtr.Zero,
                                                                                                                                    rect,
                                                                                                                                    BitmapSizeOptions.FromEmptyOptions());
                return bitmapSource;
            }
    But I get an ArgumentException tell me that "Value does not fall within the expected range."

    Can any body tell me what's wrong with my code?

    Sheva

Answers

  • Tuesday, March 28, 2006 3:30 PMRobert A. WlodarczykMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Have you tried this:

    System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(stream); // or create the System.Drawing.Bitmap however you need to to capture the screen…

    IntPtr hObject = bitmap.GetHbitmap();



    System.Windows.Media.Imaging.BitmapSource image =

    System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(

    hObject,

    IntPtr.Zero,

    System.Windows.Int32Rect.Empty,

    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());



    //Remember to call DeleteObject on hObject here. Otherwise you will be leaking GDI handles.



    Unfortunately there are no WPF native screen capture utilities. There is, however, the RenderTargetBitmap so you can render Avalon content to this off screen surface… this will work for you if you’re just concerned with capturing your WPF application.
  • Tuesday, March 28, 2006 4:34 PMeburke Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    here's how i solved this.  NativeMethods is just a class that uses interop to declare some of the GDI functions.

    IntPtr hwnd = NativeMethods.GetDesktopWindow();

    IntPtr dc = NativeMethods.GetWindowDC(hwnd);

    IntPtr memDC = NativeMethods.CreateCompatibleDC(dc);

    int l = 0;

    int t = 0;

    int w = (int) SystemParameters.PrimaryScreenWidth;

    int h = (int) SystemParameters.PrimaryScreenHeight;

    IntPtr hbm = NativeMethods.CreateCompatibleBitmap(dc, w, h);

    IntPtr oldbm = NativeMethods.SelectObject(memDC, hbm);

    int b = NativeMethods.BitBlt(memDC, l, t, w, h, dc, l, t, 0x00CC0020);

    BitmapSource bms = Imaging.CreateBitmapSourceFromHBitmap(hbm, IntPtr.Zero,

        Int32Rect.Empty,

        BitmapSizeOptions.FromEmptyOptions());

    NativeMethods.SelectObject(memDC, oldbm);

    NativeMethods.DeleteObject(hbm);

    NativeMethods.DeleteDC(memDC);

    NativeMethods.ReleaseDC(hwnd, dc);

    ImageBrush imageBrush = new ImageBrush();

    imageBrush.ImageSource = bms;

    this.overlay.Background = imageBrush;

All Replies

  • Sunday, March 26, 2006 3:33 PMChaz.RioterDecker Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hi Sheva,

    Try this article that introduces the capture of desktop screenshots using GDI+. I hope this will help you:

    http://www.codeproject.com/csharp/DesktopCaptureWithMouse.asp

     

  • Sunday, March 26, 2006 3:57 PMZhou Yong Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Nice link, Chaz, but I just want to do it in WPF.

    Sheva
  • Tuesday, March 28, 2006 2:15 AMNick Kramer - MSFT Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I'm a little confused, you said you wanted to do this using WPF and not GDI+, but your sample code used quite a bit of GDI+ (System.Drawing.*), could you elaborate? 

    Also, when you got the argument exception, which method were you calling, and what was the call stack?  Thanks.

  • Tuesday, March 28, 2006 2:24 PMZhou Yong Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I think the title says all. basically what I want to do is to capture the whole screen pixels into a System.Drawing.Bitmap object, and the use System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap() method to transform this GDI+ object into a WPF BitmapSource object, so I can use it in my WPF app. I use System.Drawing.Graphics.CopyFromScreen() method to copy the screen pixels into a GDI+ plus object, and when trying to use CreateBitmapSourceFromHBitmap() to transform it to a BitmapSouce, I got this exception:
    System.ArgumentException was unhandled
      Message="Value does not fall within the expected range."

    I guess the exception is probably resulted from the second parameter for CreateBitmapSourceFromHBitmap() method, the signature of this method is:
    public static BitmapSource CreateBitmapSourceFromHBitmap (
    IntPtr bitmap,
    IntPtr palette,
    Int32Rect sourceRect,
    BitmapSizeOptions sizeOptions
    )
    and this is how i use this method:
    BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bitmapHandle, IntPtr.Zero, rect, BitmapSizeOptions.FromEmptyOptions());

    Note that I pass an IntPtr.Zero value for the palette parameter, probably this is the actual root of evil:)

    BTW: Does anybody know if there is any class or method in WPF which can copy the whole screen pixels into the BimapSource object?

    Sheva                                                                                                           


  • Tuesday, March 28, 2006 3:30 PMRobert A. WlodarczykMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Have you tried this:

    System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(stream); // or create the System.Drawing.Bitmap however you need to to capture the screen…

    IntPtr hObject = bitmap.GetHbitmap();



    System.Windows.Media.Imaging.BitmapSource image =

    System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(

    hObject,

    IntPtr.Zero,

    System.Windows.Int32Rect.Empty,

    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());



    //Remember to call DeleteObject on hObject here. Otherwise you will be leaking GDI handles.



    Unfortunately there are no WPF native screen capture utilities. There is, however, the RenderTargetBitmap so you can render Avalon content to this off screen surface… this will work for you if you’re just concerned with capturing your WPF application.
  • Tuesday, March 28, 2006 4:34 PMeburke Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    here's how i solved this.  NativeMethods is just a class that uses interop to declare some of the GDI functions.

    IntPtr hwnd = NativeMethods.GetDesktopWindow();

    IntPtr dc = NativeMethods.GetWindowDC(hwnd);

    IntPtr memDC = NativeMethods.CreateCompatibleDC(dc);

    int l = 0;

    int t = 0;

    int w = (int) SystemParameters.PrimaryScreenWidth;

    int h = (int) SystemParameters.PrimaryScreenHeight;

    IntPtr hbm = NativeMethods.CreateCompatibleBitmap(dc, w, h);

    IntPtr oldbm = NativeMethods.SelectObject(memDC, hbm);

    int b = NativeMethods.BitBlt(memDC, l, t, w, h, dc, l, t, 0x00CC0020);

    BitmapSource bms = Imaging.CreateBitmapSourceFromHBitmap(hbm, IntPtr.Zero,

        Int32Rect.Empty,

        BitmapSizeOptions.FromEmptyOptions());

    NativeMethods.SelectObject(memDC, oldbm);

    NativeMethods.DeleteObject(hbm);

    NativeMethods.DeleteDC(memDC);

    NativeMethods.ReleaseDC(hwnd, dc);

    ImageBrush imageBrush = new ImageBrush();

    imageBrush.ImageSource = bms;

    this.overlay.Background = imageBrush;

  • Wednesday, March 29, 2006 2:10 PMZhou Yong Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks eBurke:)

    Sheva