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.
Related
Morning,
I was trying to screenshot a background window on Windows 11.
The usual methods such as BitBlt and PrintWindow are not working on Windows 11 and will always result in a top most window being capture although passing specific window handle as argument.
Those methods are not working in Windows 11 but flawless on Windows 10.
public static Bitmap PrintWindow(IntPtr hwnd)
{
RECT rc;
GetWindowRect(hwnd, out rc);
Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
return bmp;
}
And using BitBlt
public static Bitmap BitBltWindow(IntPtr hwnd) {
RECT rc;
GetClientRect(hwnd, out rc);
IntPtr hdcFrom = GetDC(hwnd);
IntPtr hdcTo = CreateCompatibleDC(hdcFrom);
int Width = rc.right;
int Height = rc.bottom;
Bitmap bmp = null;
IntPtr hBitmap = CreateCompatibleBitmap(hdcFrom, Width, Height);
if (hBitmap != IntPtr.Zero) {
IntPtr hLocalBitmap = SelectObject(hdcTo, hBitmap);
BitBlt(hdcTo, 0, 0, Width, Height, hdcFrom, 0, 0, CopyPixelOperation.SourceCopy);
SelectObject(hdcTo, hLocalBitmap);
DeleteDC(hdcTo);
ReleaseDC(hwnd, hdcFrom);
bmp = Image.FromHbitmap(hBitmap);
DeleteObject(hBitmap);
return bmp;
}
}
I also tested this in OBS and their BitBlt also fails where the Windows 10 1803 method works fine. But I haven't found out how to replicate this in C# with NET6.0 for windows.
As it relies on WinRT.GraphicsCapture and
var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem));
that isn't available for in the latest Net6.0 Target.
See: https://github.com/mika-sandbox/dotnet-window-capture/blob/master/Source/WinRT.GraphicsCapture/GraphicsCapture.cs#L6-L131
Are there any alternatives to capture a window?
Regards Artur
I have searched all the topics in stackoverflow and tried all the suggestions, but Printwindow keeps giving me black, partial or practically blank screens. I refer to inactive / minimized applications especially.
I was reading that it could be due to some apps that handle this functionality badly, but this also happens with explorer.exe, and I want to hope that the problem is not attributable to this.
My goal is to create a window selector similar to what appears in Zoom when you do screen sharing.
Is there any alternative method?
For completeness I report the function I used, even if it is the same as reported on some question in stackoverflow.
(two proven methods)
public static Bitmap GetScreenshot(IntPtr hwnd)
{
RECT rc;
GetWindowRect(new HandleRef(null, hwnd), out rc);
Bitmap bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap;
try
{
hdcBitmap = gfxBmp.GetHdc();
}
catch
{
return null;
}
bool succeeded = PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
if (!succeeded)
{
gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
}
IntPtr hRgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, hRgn);
Region region = Region.FromHrgn(hRgn);//err here once
if (!region.IsEmpty(gfxBmp))
{
gfxBmp.ExcludeClip(region);
gfxBmp.Clear(Color.Transparent);
}
gfxBmp.Dispose();
return bmp;
}
public static Bitmap PrintWindow(IntPtr hwnd)
{
RECT rc;
GetWindowRect(hwnd, out rc);
Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
return bmp;
}
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 have to capture content and only content of one window. Like that:
(captured by ScreenCaptor)
But my program captures that:
I use this code:
IntPtr ParenthWnd = GetForegroundWindow();
if (!ParenthWnd.Equals(IntPtr.Zero))
{
IntPtr prevChild = IntPtr.Zero;
IntPtr currChild = IntPtr.Zero;
while (true)
{
currChild = FindWindowEx(ParenthWnd, prevChild, null, null);
if (currChild == IntPtr.Zero) break;
result.Add(currChild);
label3.Text += currChild.ToString() + " _ ";
prevChild = currChild;
}
}
then I choose my child window, e.g:
handle = result[0];
and finally capture screenshot:
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);
}
of course I cannot crop captured image, because don't know size of each 'border'
thanks in advance
a little bit late but maby it does someone help:
private const int GWL_STYLE = -16; //hex constant for style changing
private const int WS_BORDER = 0x00800000; //window with border
private const int WS_CAPTION = 0x00C00000; //window with a title bar
private const int WS_SYSMENU = 0x00080000; //window with no borders etc.
private const int WS_MINIMIZEBOX = 0x00020000; //window with minimizebox
public static Bitmap printWindow(IntPtr hwnd)
{
RECT rc;
GetWindowRect(hwnd, out rc);
Bitmap bmp;
//make window borderless
SetWindowLong(hwnd, GWL_STYLE, WS_SYSMENU);
SetWindowPos(hwnd, -2, rc.X, rc.Y, rc.Width, rc.Height, 0x0040);
DrawMenuBar(hwnd);
bmp = new Bitmap(800, 800, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
//restore window
SetWindowLong(hwnd, GWL_STYLE, WS_CAPTION | WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX);
DrawMenuBar(hwnd);
ShowWindowAsync(hwnd, 1); //1 = Normal
return bmp;
}
Another solution is to print the window and remove the borders from the bmp:
[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);
public Bitmap printWindow()
{
RECT clientRect;
GetClientRect(WindowHandle, out clientRect);
RECT windowRect;
GetWindowRect(WindowHandle, out windowRect);
int borderSize = (windowRect.Width - clientRect.Width) / 2;
int titleBarSize = (windowRect.Height - clientRect.Height) - borderSize;
PrintWindow(MainWindowHandle, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
gfxBmp.Dispose();
Bitmap bmp = bmp.Clone(new Rectangle(borderSize, titleBarSize, bmp.Width - 2*borderSize, bmp.Height - titleBarSize-borderSize), PixelFormat.Format32bppRgb);
return bmp;
}
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);