Screenshot background window without BitBlt - c#

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

Related

(C#) PrintWindow api returns black or partial images. Alternative methods?

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;
}

facing issue of "Parameter is not valid" inside BufferCB

I am creating desktop recording (screen recording) application using Directshow.NET and C#. I am almost done, application is able to record desktop screen. To paint mouse pointer in recording video I have implemented BufferCB from SampleGrabber and with the help of my another post Fliped cursor icon on desktop recording using directshow i am able to paint mouse pointer in correct orientation
Here is my code of BufferCB :
[System.Security.Permissions.SecurityPermission(
System.Security.Permissions.SecurityAction.LinkDemand, Flags =
System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)]
int ISampleGrabberCB.BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
if (!wait)
{
wait = true;
Rectangle imageBounds = new Rectangle(0, 0, m_videoWidth, m_videoHeight);
Bitmap bitmap = new Bitmap(m_videoWidth, m_videoHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(imageBounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr ptr = bitmapData.Scan0;
bitmap.UnlockBits(bitmapData);
CopyMemory(ptr, pBuffer, (uint)BufferLen);
bitmap.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipY);
using (Graphics g = Graphics.FromImage(bitmap))
{
CURSORINFO cursorInfo;
cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out cursorInfo) && cursorInfo.flags == CURSOR_SHOWING)
{
IntPtr iconPointer = CopyIcon(cursorInfo.hCursor);
ICONINFO iconInfo;
int iconX, iconY;
if (GetIconInfo(iconPointer, out iconInfo))
{
// calculate the correct position of the cursor
iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
//GETTING ARGUMENTEXCEPTION AT BELOW LINE
IntPtr hdc = g.GetHdc();
DrawIcon(hdc, iconX, iconY, cursorInfo.hCursor);
g.ReleaseHdc(hdc);
}
}
g.DrawImage(companylogo, m_videoWidth - 100 , 20);
}
bitmap.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipY);
bitmapData = bitmap.LockBits(imageBounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
ptr = bitmapData.Scan0;
bitmap.UnlockBits(bitmapData);
CopyMemory(pBuffer, ptr, (uint)BufferLen);
bitmap.Dispose();
wait = false;
}
return 0;
}
Mouse pointer is getting paint on video but after some time of recording I am getting ArgumentException "Parameter is not valid." at line of code IntPtr hdc = g.GetHdc();
Can anyone help me put to solve this?
StackTrace:
at System.Drawing.Graphics.GetHdc()
Look at the answer in this question.
They discuss the same error message.

Take screenshot of a program with visual style

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.

C# Bitblit from Bitmap to control (Compact Framework)

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);

Create a semi-transparent cursor from an image

Is it possible to create a cursor from an image and have it be semi-transparent?
I'm currently taking a custom image and overylaying the mouse cursor image. It would be great if I could make this semi-transparent, but not necessary. The sales guys love shiny.
Currently doing something like this:
Image cursorImage = customImage.GetThumbnailImage(300, 100, null, IntPtr.Zero);
cursorImage.SetResolution(96.0F, 96.0F);
int midPointX = cursorImage.Width / 2;
int midPointY = cursorImage.Height / 2;
Bitmap cursorMouse = GetCursorImage(cursorOverlay);
Graphics cursorGfx = Graphics.FromImage(cursorImageCopy);
cursorGfx.DrawImageUnscaled(cursorMouse, midPointX, midPointY);
Cursor tmp = new Cursor(cursorImage.GetHicon());
alt text http://members.cox.net/dustinbrooks/drag.jpg
I've tried following example, and it was working fine...
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
IntPtr ptr = bmp.GetHicon();
IconInfo tmp = new IconInfo();
GetIconInfo(ptr, ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
ptr = CreateIconIndirect(ref tmp);
return new Cursor(ptr);
}
And i've put this on button click event (you can call from where you like):
Bitmap b = new Bitmap("D:/Up.png");
this.Cursor = CreateCursor(b, 5, 5);
And the Up.png image is saved with 75% opacity in AdobePhotoshop.
On the top of my head (I would try that first):
create new bitmap with same size as original, but with ARGB structure
drawimage: existing bitmap to the new bitmap
access raw bitmap data, and replace A bytes with 128
You should have nice semitransparent bitmap there.
If performance allows, you can scan for fully transparent pixels and set A to zero for them!
If you want to set transparency of a custom mouse cursor bitmap 'on the fly' you may find this function helpful. It uses a color matrix to set the amount of transparency to any given bitmap and will return the modified one. To have just a touch of transparency the TranspFactor should be between 225 and 245, just try it out. (You need to import System.Drawing and System.Drawing.Imaging)
public static Bitmap GetBMPTransparent(Bitmap bmp, int TranspFactor)
{
Bitmap transpBmp = new Bitmap(bmp.Width, bmp.Height);
using (ImageAttributes attr = new ImageAttributes()) {
ColorMatrix matrix = new ColorMatrix { Matrix33 = Convert.ToSingle(TranspFactor / 255) };
attr.SetColorMatrix(matrix);
using (Graphics g = Graphics.FromImage(transpBmp)) {
g.DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, attr);
}
}
return transpBmp;
}
that is very easy, I don't use API.
the code is
Bitmap img = new Bitmap(new Bitmap(#"image.png"), 30, 30); //this is the size of cursor
Icon icono = Icon.FromHandle(img.GetHicon()); //create the Icon object
Cursor = new Cursor(icono.Handle); //the icon Object has the stream to create a Cursor.
I hope that is your solution

Categories