Interop Between WPF and GDI+
- 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
- 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. 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
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
- Nice link, Chaz, but I just want to do it in WPF.
Sheva 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.
- 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 (
and this is how i use this method:
IntPtr bitmap,
IntPtr palette,
Int32Rect sourceRect,
BitmapSizeOptions sizeOptions
)
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 - 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. 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;- Thanks eBurke:)
Sheva


