I used once BitBlt to save a screenshot to an image file (.Net Compact Framework V3.5, Windows Mobile 2003 and later). Worked fine. Now I want to draw a bitmap to a form. I could use this.CreateGraphics().DrawImage(mybitmap, 0, 0), but I was wondering if it would work with BitBlt like before and just swap the params. So I wrote:
[DllImport("coredll.dll")]
public static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
(and further down:)
IntPtr hb = mybitmap.GetHbitmap();
BitBlt(this.Handle, 0, 0, mybitmap.Width, mybitmap.Height, hb, 0, 0, 0x00CC0020);
But the form stays plain white. Why is that? Where is the error I commited?
Thanks for your opinions. Cheers, David
this.Handle is a Window handle not a device context.
Replace this.Handle with this.CreateGraphics().GetHdc()
Of course you'll need to destroy the graphics object etc...
IntPtr hb = mybitmap.GetHbitmap();
using (Graphics gfx = this.CreateGraphics())
{
BitBlt(gfx.GetHdc(), 0, 0, mybitmap.Width, mybitmap.Height, hb, 0, 0, 0x00CC0020);
}
In addition hb is a Bitmap Handle not a device context so the above snippet still won't work. You'll need to create a device context from the bitmap:
using (Bitmap myBitmap = new Bitmap("c:\test.bmp"))
{
using (Graphics gfxBitmap = Graphics.FromImage(myBitmap))
{
using (Graphics gfxForm = this.CreateGraphics())
{
IntPtr hdcForm = gfxForm.GetHdc();
IntPtr hdcBitmap = gfxBitmap.GetHdc();
BitBlt(hdcForm, 0, 0, myBitmap.Width, myBitmap.Height, hdcBitmap, 0, 0, 0x00CC0020);
gfxForm.ReleaseHdc(hdcForm);
gfxBitmap.ReleaseHdc(hdcBitmap);
}
}
}
You mean something along these lines?
public void CopyFromScreen(int sourceX, int sourceY, int destinationX,
int destinationY, Size blockRegionSize,
CopyPixelOperation copyPixelOperation)
{
IntPtr desktopHwnd = GetDesktopWindow();
if (desktopHwnd == IntPtr.Zero)
{
throw new System.ComponentModel.Win32Exception();
}
IntPtr desktopDC = GetWindowDC(desktopHwnd);
if (desktopDC == IntPtr.Zero)
{
throw new System.ComponentModel.Win32Exception();
}
if (!BitBlt(hDC, destinationX, destinationY, blockRegionSize.Width,
blockRegionSize.Height, desktopDC, sourceX, sourceY,
copyPixelOperation))
{
throw new System.ComponentModel.Win32Exception();
}
ReleaseDC(desktopHwnd, desktopDC);
}
FYI, this is right out of the SDF.
EDIT: It's not real clear in this snippet, but hDC in the BitBlt is the HDC of the target bitmap (into which you wish to paint).
Are you sure that this.Handle refers to a valid device context? Have you tried checking the return value of the BitBlt function?
Try the following:
[DllImport("coredll.dll", EntryPoint="CreateCompatibleDC")]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("coredll.dll", EntryPoint="GetDC")]
public static extern IntPtr GetDC(IntPtr hwnd);
IntPtr hdc = GetDC(this.Handle);
IntPtr hdcComp = CreateCompatibleDC(hdc);
BitBlt(hdcComp, 0, 0, mybitmap.Width, mybitmap.Height, hb, 0, 0, 0x00CC0020);
Related
I want to save a image of a completed Form in Outlook. I am using VSTO Outlook Addin. I am able to capture a full screen image, but I am having no luck with the form region by its self. Does anyone have any ideas?
var bmpScreenshot = new Bitmap(this.Width,
this.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var grxScreenshot = Graphics.FromImage(bmpScreenshot);
grxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0,
0,
Screen.PrimaryScreen.Bounds.Size,
CopyPixelOperation.SourceCopy);
string outputFileName = #"C:\Users\63530\Desktop\image.png";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
{
bmpScreenshot.Save(memory, ImageFormat.Png);
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
This is how I capture any window content. I hope it helps you, but it is not clear what is this you want to capture. This code works even if the window is not in foreground.
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);
public static Bitmap GetSnapshot(IntPtr hWnd) // capture a window by its handle
{
Int32 windowLeft;
Int32 windowTop;
Int32 windowWidth;
Int32 windowHeight;
if (hWnd == IntPtr.Zero) return null;
if (!GetWindowRect(hWnd, out windowLeft, out windowTop, out windowWidth, out windowHeight)) return null;
Bitmap bmp = new Bitmap(windowWidth, windowHeight, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
PrintWindow(hWnd, hdcBitmap, 0); // from user32.dll
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
return bmp;
}
private static bool GetWindowRect(IntPtr hWnd, out Int32 left, out Int32 top, out Int32 width, out Int32 height)
{
left = 0;
top = 0;
width = 0;
height = 0;
RECT rct = new RECT();
if (!GetWindowRect(hWnd, ref rct)) return false; // from user32.dll
left = rct.Left;
top = rct.Top;
width = rct.Right - rct.Left + 1;
height = rct.Bottom - rct.Top + 1;
return true;
}
public struct RECT
{
public Int32 Left;
public Int32 Top;
public Int32 Right;
public Int32 Bottom;
}
I'm using this code
public static Bitmap PrintWindow(IntPtr hwnd)
{
RECT rc;
GetWindowRect(hwnd, out rc);
Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format24bppRgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
return bmp;
}
In order to capture screenshot of a program, it works well but it takes only the basic visual style (please check image below, Left one is the captured by the code above, Right one is captured by Alt+Prntscr)
So.. is there anyway to capture screenshot of a program with visual style?
Here are some links you can try:
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.drawtobitmap.aspx
http://www.pinvoke.net/default.aspx/user32.printwindow
What I use is:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern long BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
private Bitmap memoryImage;
...
private void CaptureWindow()
{
Graphics mygraphics = this.CreateGraphics();
Size s = this.Size;
memoryImage = new Bitmap(s.Width, s.Height, mygraphics);
Graphics memoryGraphics = Graphics.FromImage(memoryImage);
IntPtr dc1 = mygraphics.GetHdc();
IntPtr dc2 = memoryGraphics.GetHdc();
//BitBlt(dc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, dc1, 0, 0, 13369376);
BitBlt(dc2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, dc1, 0, 0, 0x00CC0020);
mygraphics.ReleaseHdc(dc1);
memoryGraphics.ReleaseHdc(dc2);
}
where memoryImage is the bitmap to print.
I'd like to get the specific region by using way to get DesktopWindow handle like below code.
[DllImport("user32.dll")]
static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
static extern IntPtr GetDCEx(IntPtr hwnd, IntPtr hrgn, uint flags);
[DllImport("user32.dll")]
static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc);
public void ScreenShot()
{
try
{
IntPtr hwnd = GetDesktopWindow();
IntPtr hdc = GetDCEx(hwnd, IntPtr.Zero, 1027);
Point temp = new Point(40, 40);
Graphics g = Graphics.FromHdc(hdc);
Bitmap bitmap = new Bitmap(mPanel.Width, mPanel.Height, g);
g.CopyFromScreen(PointToScreen(temp) , PointToScreen(PictureBox.Location) , PictureBox.Size);
}
This code actually works, but I'd like to get a copied image which is made from the process of CopyFromScreen. I have tried using code like Graphics.FromImage(bitmap), however I could not get the image that I wanted... I mean,, copied Image.
I could not find the way to get a Bitmap image when I use Graphics object fromHdc.
I have to use DC.... Is there any proper way??
You are going the wrong way here, you don't need to get the desktop handle, CopyFromScreen will copy whatever is on screen now to the target graphics so you need to create graphics object from the image.
The following code create an 500x500 image of the top left of the screen.
public static void ScreenShot()
{
var destBitmap = new Bitmap(500, 500);
using (var destGraph = Graphics.FromImage(destBitmap))
{
destGraph.CopyFromScreen(new Point(), new Point(), destBitmap.Size);
}
destBitmap.Save(#"c:\bla.png");
}
and if you really have HDC you need to use BitBlt from gdi32:
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
Anyone know why this keeps returning a blank image? I found this function here.
I pass in a handle to a notepad process/window.
public static Image DrawToBitmap(IntPtr handle)
{
Bitmap image = new Bitmap(500, 500, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics graphics = Graphics.FromImage(image))
{
IntPtr hDC = graphics.GetHdc();
SendMessage(new HandleRef(graphics, handle), WM_PRINT, hDC, PRF_CHILDREN);
graphics.ReleaseHdc(hDC);
}
return image;
}
I make use of the above like so:
Image myimage = DrawToBitmap(handle);
myimage.Save("C:\\here.png", ImageFormat.Png);
Thanks all for any help
Update
I think I have managed to get the error code from SendMessage using the below:
if (SendMessage(handle, WM_PRINT, hDC, PRF_CLIENT))
{
Console.WriteLine("Success!");
}
else
{
Console.WriteLine("Error: " + Marshal.GetLastWin32Error());
}
I get an error of 8 and I found it means not enough memory? I have over 500MB free! Maybe I am understanding this wrong?
Instead of SendMessage you could just use PrintWindow
Here is a sample
public static Image DrawToBitmap(IntPtr handle)
{
RECT rect = new RECT();
GetWindowRect(handle, ref rect);
Bitmap image = new Bitmap(rect.Right - rect.Left, rect.Bottom - rect.Top);
using (Graphics graphics = Graphics.FromImage(image))
{
IntPtr hDC = graphics.GetHdc();
PrintWindow(new HandleRef(graphics, handle), hDC, 0);
graphics.ReleaseHdc(hDC);
}
return image;
}
#region Interop
[DllImport("USER32.DLL")]
private static extern bool PrintWindow(HandleRef hwnd, IntPtr hdcBlt, int nFlags);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion
Try passing PRF_CLIENT or PRF_NONCLIENT to SendMessage in addition to PRF_CHILDREN.
Also, how are you deriving the handle that you are passing to the function?
Any method to output the screenshot of an active form?
Even simpler answer supported by .NET:
Control.DrawToBitmap.
Use the Control.DrawToBitmap() method. For example:
private void timer1_Tick(object sender, EventArgs e) {
var frm = Form.ActiveForm;
using (var bmp = new Bitmap(frm.Width, frm.Height)) {
frm.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save(#"c:\temp\screenshot.png");
}
}
Try this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
namespace ScreenshotCapturer
{
public partial class Form1 : Form
{
private static Bitmap bmpScreenshot;
private static Graphics gfxScreenshot;
public Form1()
{
InitializeComponent();
}
private void btnCapture_Click(object sender, EventArgs e)
{
// If the user has choosed a path where to save the screenshot
if (saveScreenshot.ShowDialog() == DialogResult.OK)
{
// Hide the form so that it does not appear in the screenshot
this.Hide();
// Set the bitmap object to the size of the screen
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
// Create a graphics object from the bitmap
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
// Take the screenshot from the upper left corner to the right bottom corner
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
// Save the screenshot to the specified path that the user has chosen
bmpScreenshot.Save(saveScreenshot.FileName, ImageFormat.Png);
// Show the form again
this.Show();
}
}
}
}
Here's an extension method you can use:
#region Interop
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr hdc, PRF_FLAGS drawingOptions);
const uint WM_PRINT = 0x317;
[Flags]
enum PRF_FLAGS : uint
{
CHECKVISIBLE = 0x01,
CHILDREN = 0x02,
CLIENT = 0x04,
ERASEBKGND = 0x08,
NONCLIENT = 0x10,
OWNED = 0x20
}
#endregion
public static Image CaptureImage(this Control control)
{
Image img = new Bitmap(control.Width, control.Height);
using (Graphics g = Graphics.FromImage(img))
{
SendMessage(
control.Handle,
WM_PRINT,
g.GetHdc(),
PRF_FLAGS.CLIENT | PRF_FLAGS.NONCLIENT | PRF_FLAGS.ERASEBKGND);
}
return img;
}
Form inherits from Control, so you can use it on a form too.
Because Control.DrawToBitmap has some limitations (most notably drawing child controls in reverse order) I have made this alternative. The variable _control is the WinForms Control/Form that you want to copy.
using (Bitmap bitmap = new Bitmap(width, height)) {
using (Graphics gb = Graphics.FromImage(bitmap))
using (Graphics gc = Graphics.FromHwnd(_control.Handle)) {
IntPtr hdcDest = IntPtr.Zero;
IntPtr hdcSrc = IntPtr.Zero;
try {
hdcDest = gb.GetHdc();
hdcSrc = gc.GetHdc();
BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, SRC_COPY);
} finally {
if (hdcDest != IntPtr.Zero) gb.ReleaseHdc(hdcDest);
if (hdcSrc != IntPtr.Zero) gc.ReleaseHdc(hdcSrc);
}
}
bitmap.Save(...);
}
[DllImport("gdi32.dll", EntryPoint = "BitBlt")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(
[In()] System.IntPtr hdc, int x, int y, int cx, int cy,
[In()] System.IntPtr hdcSrc, int x1, int y1, uint rop);
private const int SRC_COPY = 0xCC0020;
this.Opacity = 0;
Rectangle bounds = Screen.GetBounds(Point.Empty);
// create the bitmap to copy the screen shot to
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
// now copy the screen image to the graphics device from the bitmap
using (Graphics gr = Graphics.FromImage(bitmap))
{
gr.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
this.Opacity = 100;
Now just
gr.Save(#"c\pic.jpg",ImageFormat.jpg);
This will save a screenshot of your active window to your C: drive.
The SendKeys.Send("") is used to send keypresses to the computer. In this case % is used to press Alt and {PRTSC} obviously to press the Print Screen button on your keyboard.
Hope this helps someone!
private void printScreenButton_Click(object sender, EventArgs e)
{
SendKeys.Send("%{PRTSC}");
Image img = Clipboard.GetImage();
img.Save(#"C:\\testprintscreen.jpg");
}
here is:
public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hObjectSource,
int nXSrc, int nYSrc, int dwRop);
[DllImport("gdi32.dll")]
public static extern bool StretchBlt(
IntPtr hdcDest, // handle to destination DC
int nXOriginDest, // x-coord of destination upper-left corner
int nYOriginDest, // y-coord of destination upper-left corner
int nWidthDest, // width of destination rectangle
int nHeightDest, // height of destination rectangle
IntPtr hdcSrc, // handle to source DC
int nXOriginSrc, // x-coord of source upper-left corner
int nYOriginSrc, // y-coord of source upper-left corner
int nWidthSrc, // width of source rectangle
int nHeightSrc, // height of source rectangle
int dwRop // raster operation code
);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth,
int nHeight);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
private void run_test()
{
Rectangle rc = new Rectangle();
Image img = ScreenToImage(ref rc, false);
img.Save(#"C:\Users\ssaamm\Desktop\Capt44ure.JPG", System.Drawing.Imaging.ImageFormat.Jpeg);
img.Dispose();
}
private Image ScreenToImage(ref Rectangle rcDest, bool IsPapaer)
{
IntPtr handle = this.Handle;
//this.Handle
// get te hDC of the target window
IntPtr hdcSrc = GetWindowDC(handle);
// get the size
RECT windowRect = new RECT();
GetWindowRect(handle, ref windowRect);
int nWidth = windowRect.right - windowRect.left;
int nHeight = windowRect.bottom - windowRect.top;
if (IsPapaer)
{
float fRate = (float)rcDest.Width / nWidth;
//float fHeight = nHeight * fRate;
//rcDest.Height = (int)(nHeight * fRate);
//rcDest.Width = (int)(rcDest.Width);// * fRate);
rcDest.X = 0;
rcDest.Y = 0;
rcDest.Height = (int)(nHeight * fRate);
//rcDest.Width = (int)(nWidth * fRate);
}
else
{
rcDest.X = 0;
rcDest.Y = 0;
rcDest.Height = nHeight;
rcDest.Width = nWidth;
}
// create a device context we can copy to
IntPtr hdcDest = CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = CreateCompatibleBitmap(hdcSrc, rcDest.Width, rcDest.Height);
// select the bitmap object
IntPtr hOld = SelectObject(hdcDest, hBitmap);
// bitblt over
StretchBlt(hdcDest, rcDest.X, rcDest.Y, rcDest.Width, rcDest.Height, hdcSrc, 0, 0, nWidth, nHeight, SRCCOPY);
// restore selection
SelectObject(hdcDest, hOld);
// clean up
DeleteDC(hdcDest);
ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
Image img = Image.FromHbitmap(hBitmap);
// free up the Bitmap object
DeleteObject(hBitmap);
return img;
}