I'm using this code to capture a process window in the background:
IntPtr = Process.GetProcessByName("memu")[0].MainWindowHandle;
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;
}
This code is capture an Android emulator called MEmu, it is using DirectX to render the content. But this code stopped to work after Windows 10 updated to version 16299 (it was working normally before), it still working on Windows 7 with Aero mode enabled.
When I use this method in the Windows 10 Pro v16299.X it simply return a white image or it returns the emulator "loading screen", not the running content. On Windows 7, if I remove the Aero mode it will act the same, capturing the "loading screen", so looks like somehow the way the transparency works in the new windows 10 pro update changed.
I've tried everything, tried install some modules to force Aero Mode to work on Windows 10, tried PrintWindow to capture the screen in the background, but still the same.
Any ideas what could be happening? Or a possible solution? Or what changed in this last Windows 10 Pro version that could break that code?
Thank you!
For me this is working on Windows 10 11.02.2021:
static void hflipAndSwapRandB(unsigned char* data, int w, int h, int dim)
{
int y = 0;
int x = 0;
int d;
unsigned char swap = 0;
int hh = h / 2;
for (y = 0; y < hh; y++)
{
for (x = 0; x < w; x++)
{
for (d = 0; d < dim; d++)
{
swap = data[y * dim * w + dim * x + d];
data[y * dim * w + dim * x + d] = data[(h - 1 - y) * dim * w + dim * x + dim - 1 - d];
data[(h - 1 - y) * dim * w + dim * x + dim - 1 - d] = swap;
}
}
}
}
static void copyScreen(unsigned char* pixels_out, int x, int y, int width, int height)
{
HDC hScreenDC = GetDC(GetDesktopWindow());
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
if (width == -1 || height == -1)
{
width = GetDeviceCaps(hScreenDC, HORZRES);
height = GetDeviceCaps(hScreenDC, VERTRES);
}
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, x, y, width, height, hScreenDC, x, y, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width-x;
bi.biHeight = height-y;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
GetDIBits(hMemoryDC, hBitmap, 0, height-y, pixels_out, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
hflipAndSwapRandB(pixels_out, width, height, 3);
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
DeleteObject(hBitmap);
}
Hopefully this will solve the problem. There is a method for capturing the screen that is built in to the .net framework that may work. Not sure if it will capture DirectX content, but it may be worth a try.
Please note that this solution captures the current screen, but you will probably be able to modify it to capture only the area you are interested in.
I found this solution here: https://www.c-sharpcorner.com/UploadFile/2d2d83/how-to-capture-a-screen-using-C-Sharp/
private void CaptureMyScreen()
{
try
{
//Creating a new Bitmap object
Bitmap captureBitmap = new Bitmap(1024, 768, PixelFormat.Format32bppArgb);
//Bitmap captureBitmap = new Bitmap(int width, int height, PixelFormat);
//Creating a Rectangle object which will
//capture our Current Screen
Rectangle captureRectangle = Screen.AllScreens[0].Bounds;
//Creating a New Graphics Object
Graphics captureGraphics = Graphics.FromImage(captureBitmap);
//Copying Image from The Screen
captureGraphics.CopyFromScreen(captureRectangle.Left,captureRectangle.Top,0,0,captureRectangle.Size);
//Saving the Image File (I am here Saving it in My E drive).
captureBitmap.Save(#"E:\Capture.jpg",ImageFormat.Jpeg);
//Displaying the Successfull Result
MessageBox.Show("Screen Captured");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I've had the same white window problem when capturing another application's window. I could capture the whole desktop though. One way around this was to capture the desktop portion occupied by the app, another solution was to elevate my program to run as administrator. Running as administrator allowed me to capture a specific app window (in my case a .Net 4.8 framework app the only change needed was requireAdministrator in app manifest requestedExecutionLevel).
public static System.Drawing.Image CaptureWindow(IntPtr handle, bool capturedesktop = false, bool capturemouse = true)
{
if (IsWindowVisible(handle))
{
IntPtr hdcSrc = GetWindowDC(handle);
RECT windowRect;
GetWindowRect(handle, out windowRect);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
if (capturedesktop)
hdcSrc = GetWindowDC(GetDesktopWindow());
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
if (capturedesktop)
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, windowRect.left, windowRect.top, GDI32.TernaryRasterOperations.SRCCOPY);
else
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.TernaryRasterOperations.SRCCOPY);
if (capturemouse)
{
try
{
CURSORINFO cursorInfo = new CURSORINFO();
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
IntPtr hicon;
if (GetCursorInfo(ref cursorInfo))
{
if (cursorInfo.flags == CURSOR_SHOWING)
{
if ((hicon = CopyIcon(cursorInfo.hCursor)) != IntPtr.Zero)
{
ICONINFO iconInfo;
if (GetIconInfo(hicon, out iconInfo))
{
using (Bitmap maskBitmap = System.Drawing.Image.FromHbitmap(iconInfo.hbmMask))
{
DrawIconEx(hdcDest, cursorInfo.ptScreenPos.X - iconInfo.xHotspot - windowRect.left, cursorInfo.ptScreenPos.Y - iconInfo.yHotspot - windowRect.top, cursorInfo.hCursor, 0, 0, 0, IntPtr.Zero, 0x0003);
GDI32.DeleteObject(iconInfo.hbmMask);
DestroyIcon(hicon);
}
}
else
{
DestroyIcon(hicon);
}
}
}
}
} catch
{
}
}
GDI32.SelectObject(hdcDest, hOld);
GDI32.DeleteDC(hdcDest);
ReleaseDC(handle, hdcSrc);
System.Drawing.Image img = System.Drawing.Image.FromHbitmap(hBitmap);
GDI32.DeleteObject(hBitmap);
return img;
} else
{
return null;
}
}
Related
I am trying to make a screenshot to the window of a process in Windows 7 64 bit, the problem is that I always get error in the following line:
var bmp = new Bitmap (width, height, PixelFormat.Format32bppArgb);
Saying "invalid parameters", I made a throw to see the errors and width and height are always 0.
Before in 32 bits it worked well, but now in 64 bits it does not work anymore.
The code :
public void CaptureApplication()
{
string procName = "firefox";
var proc = Process.GetProcessesByName(procName)[0];
var rect = new User32.Rect();
User32.GetWindowRect(proc.MainWindowHandle, ref rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bmp);
graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
bmp.Save("c:\\tmp\\test.png", ImageFormat.Png);
}
private class User32
{
[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 GetWindowRect(IntPtr hWnd, ref Rect rect);
}
How do I fix this error?
Getting the process of firefox returns an array of processes and you are looking for the one with an rectangle with realistic sizes which is the main process.
Process[] procs = Process.GetProcessesByName(procName);
var rect = new User32.Rect();
int width = 0;
int height = 0:
foreach (Process proc in procs)
{
User32.GetWindowRect(proc.MainWindowHandle, ref rect);
width = rect.right - rect.left;
height = rect.bottom - rect.top;
// break foreach if an realistic rectangle found => main process found
if (width != 0 && height != 0)
{
break;
}
}
im trying to send screenshots over socket so i use unsafe pointers to send only the differences:
private unsafe Bitmap GetDiffBitmap(Bitmap bmp, Bitmap bmp2)
{
bmpRes = new Bitmap(1920, 1080,bmp.PixelFormat);
bmData = bmp.LockBits(new Rectangle(0, 0, 1920, 1080), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
bmDataRes = bmpRes.LockBits(new Rectangle(0, 0, bmpRes.Width, bmpRes.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
IntPtr scan0 = bmData.Scan0;
IntPtr scan02 = bmData2.Scan0;
IntPtr scan0Res = bmDataRes.Scan0;
int stride = bmData.Stride;
int stride2 = bmData2.Stride;
int strideRes = bmDataRes.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
//for(int y = 0; y < nHeight; y++)
System.Threading.Tasks.Parallel.For(0, nHeight, y =>
{
//define the pointers inside the first loop for parallelizing
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
byte* p2 = (byte*)scan02.ToPointer();
p2 += y * stride2;
byte* pRes = (byte*)scan0Res.ToPointer();
pRes += y * strideRes;
for (int x = 0; x < nWidth; x++)
{
//always get the complete pixel when differences are found
if (p[0] != p2[0] || p[1] != p2[1] || p[2] != p2[2])
{
pRes[0] = p2[0];
pRes[1] = p2[1];
pRes[2] = p2[2];
//alpha (opacity)
pRes[3] = p2[3];
}
p += 4;
p2 += 4;
pRes += 4;
}
});
bmp.UnlockBits(bmData);
bmp2.UnlockBits(bmData2);
bmpRes.UnlockBits(bmDataRes);
return bmpRes;
}
this is the call on the client:
private void startSend()
{
Bitmap curr;
Bitmap pre = screenshot();
byte []bmpBytes = imageToByteArray(pre);
SendVarData(handler, bmpBytes);// this is the first send of the whole screen
while (true)
{
curr = screenshot();
Bitmap diff = GetDiffBitmap(pre, curr);//generate differences.
bmpBytes = imageToByteArray(diff);
SendVarData(handler, bmpBytes);//sending the diff image.
pre = curr;
}
}
SendVarData is a method which send the bytes array over the socket it is not the problem here-leave it.
this is how i get the data in the server side:
public void startListening()
{
Bitmap merge = new Bitmap(1920, 1080);
Graphics g = Graphics.FromImage(merge);
Bitmap prev = byteArrayToImage(ReceiveVarData(client.Client)) as Bitmap;//getting the first full size image.
theImage.Image = prev;//assisning it to picturebox.
while (true)
{
byte[]data = ReceiveVarData(client.Client);
Bitmap curr = byteArrayToImage(data) as Bitmap;//here is the diffrent image
//merge and apply differences
g.DrawImage(prev, 0, 0,1920, 1080);
g.DrawImage(curr, 0, 0, 1920,1080);
theImage.Image = merge;
count++;
prev = merge;
}
}
my problem is that eventhough i merge the two images with the Graphics.Draw it still(after the first dataReceive) looks like not full... this is actually what i see on the server..
i dont know what's wrong here... can anyone light my eyes? :D
#DmitriTrofimov
if (p[0] != p2[0] || p[1] != p2[1] || p[2] != p2[2])
{
pRes[0] = p2[0];
pRes[1] = p2[1];
pRes[2] = p2[2];
//alpha (opacity)
pRes[3] = p2[3];
}
else
pRes[0] = 0;
This is what you get when you keep pixel opacity. You should set Alpha to zero on the pixels that are not different and make sure you are working with 32-bit images (refer to PixelFormat parameter of Bitmap constructor).
P.S. Make sure you compress the diff bitmap otherwise it's no use.
I have a problem of Pixel.
I wanted to combine Bitmap's RGBA. And I got It.
But, haapend some problem.
First of all, show you my Code.
My Code:
private Bitmap getImg (IntPtr hWnd, int width, int height, int startX, int startY) {
IntPtr hDC = Win32.GetDC(hWnd);
IntPtr hMemDC = Win32.CreateCompatibleDC(hDC);
IntPtr hBitmap = Win32.CreateCompatibleBitmap(hDC, width, height);
if (hBitmap != IntPtr.Zero) {
IntPtr hOld = (IntPtr) Win32.SelectObject(hMemDC, hBitmap);
Win32.BitBlt(hMemDC, 0, 0, width, height, hDC, startX, startY, 13369376);
Win32.SelectObject(hMemDC, hOld);
Win32.DeleteDC(hMemDC);
Win32.ReleaseDC(hWnd, hDC);
Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap);
Win32.DeleteObject(hBitmap);
GC.Collect();
return bmp;
}
return null;
}
private void checkPixel (Bitmap img) {
try {
if (img != null) {
long value = 0;
for (int x = 0; x < img.Width; x++) {
for (int y = 0; y < img.Height; y++) {
Color pixel = img.GetPixel(x, y);
value += pixel.A + pixel.R + pixel.G + pixel.B;
}
}
Console.WriteLine("value : " + value);
}
} catch (Exception e) {
Console.WriteLine(e);
}
}
// And.. I used this way
Bitmap img = getImg(hWnd, width, height, startX, startY);
if (img != null) {
checkPixel(img);
}
So.. This code is working cool my computer.
But, work in another computer, "value" is different than my "value".
BMP is same. (Game Screen)
I don't understand this problem.
How can I solve this problem ?
Do you have a better idea ?
I use this method to get thumbnails of files (keeping transparency...):
public static Image GetIcon(string fileName, int size)
{
IShellItem shellItem;
Shell32.SHCreateItemFromParsingName(fileName, IntPtr.Zero, Shell32.IShellItem_GUID, out shellItem);
IntPtr hbitmap;
((IShellItemImageFactory)shellItem).GetImage(new SIZE(size, size), 0x0, out hbitmap);
// get the info about the HBITMAP inside the IPictureDisp
DIBSECTION dibsection = new DIBSECTION();
Gdi32.GetObjectDIBSection(hbitmap, Marshal.SizeOf(dibsection), ref dibsection);
int width = dibsection.dsBm.bmWidth;
int height = dibsection.dsBm.bmHeight;
// create the destination Bitmap object
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
unsafe
{
// get a pointer to the raw bits
RGBQUAD* pBits = (RGBQUAD*)(void*)dibsection.dsBm.bmBits;
// copy each pixel manually
for (int x = 0; x < dibsection.dsBmih.biWidth; x++)
{
for (int y = 0; y < dibsection.dsBmih.biHeight; y++)
{
int offset = y * dibsection.dsBmih.biWidth + x;
if (pBits[offset].rgbReserved != 0)
{
bitmap.SetPixel(x, y, Color.FromArgb(pBits[offset].rgbReserved, pBits[offset].rgbRed, pBits[offset].rgbGreen, pBits[offset].rgbBlue));
}
}
}
}
Gdi32.DeleteObject(hbitmap);
return bitmap;
}
But sometimes the image is upside down. When getting the same image for 2nd, 3rd time it's not upside down. Is there any way to determine wether it is upside down or not? If there was any solution, the code below should work:
if (isUpsideDown)
{
int offset = (dibsection.dsBmih.biHeight - y - 1) * dibsection.dsBmih.biWidth + x;
}
else
{
int offset = y * dibsection.dsBmih.biWidth + x;
}
I came across the same problem. Images from the clipboard where upside down. I managed to find out, that you can check the Stride value to see if the image is reversed:
BitmapData d = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
bmp.UnlockBits(d);
if (d.Stride > 0)
{
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
}
If the Stride value is greater than zero, the image is reversed.
Andy
I'm currently working on a small library that enables you to get icons from files and folders. Now, I don't care if it only works on win8+ (cause that's the place I'm going to use it), however, I've run in to a tiny problem with regards to transparency. If you take a look at the following image:
The one I generate (from my library) is to the left, windows explorer is to the right.
Now, as you might see, first off there is 2 black lines in the upper right of the one I generate, second, there is a difference in the background color. So what I'm wondering is this; is there no way to get the exact same image used by windows explorer, or am I simply doing it wrong?
My code (with exception to structs/externs etc. for shortness) bellow, entire code here.
public static class Icon
{
public static Image GetIcon(string fileName, int size)
{
IShellItem shellItem;
Shell32.SHCreateItemFromParsingName(fileName, IntPtr.Zero, Shell32.IShellItem_GUID, out shellItem);
IntPtr hbitmap;
((IShellItemImageFactory)shellItem).GetImage(new SIZE(size, size), 0x0, out hbitmap);
// get the info about the HBITMAP inside the IPictureDisp
DIBSECTION dibsection = new DIBSECTION();
Gdi32.GetObjectDIBSection(hbitmap, Marshal.SizeOf(dibsection), ref dibsection);
int width = dibsection.dsBm.bmWidth;
int height = dibsection.dsBm.bmHeight;
// zero out the RGB values for all pixels with A == 0
// (AlphaBlend expects them to all be zero)
for (int i = 0; i < dibsection.dsBmih.biWidth * dibsection.dsBmih.biHeight; i++)
{
IntPtr ptr = dibsection.dsBm.bmBits + (i * Marshal.SizeOf(typeof(RGBQUAD)));
var rgbquad = (RGBQUAD)Marshal.PtrToStructure(ptr, typeof(RGBQUAD));
if (rgbquad.rgbReserved == 0)
{
rgbquad.rgbBlue = 0;
rgbquad.rgbGreen = 0;
rgbquad.rgbRed = 0;
}
else
{
;
}
Marshal.StructureToPtr(rgbquad, ptr, false);
}
// create the destination Bitmap object
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
// get the HDCs and select the HBITMAP
Graphics graphics = Graphics.FromImage(bitmap);
IntPtr hdcDest = graphics.GetHdc();
IntPtr hdcSrc = Gdi32.CreateCompatibleDC(hdcDest);
IntPtr hobjOriginal = Gdi32.SelectObject(hdcSrc, hbitmap);
// render the bitmap using AlphaBlend
BLENDFUNCTION blendfunction = new BLENDFUNCTION(BLENDFUNCTION.AC_SRC_OVER, 0, 0xFF, BLENDFUNCTION.AC_SRC_ALPHA);
Gdi32.AlphaBlend(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, width, height, blendfunction);
// clean up
Gdi32.SelectObject(hdcSrc, hobjOriginal);
Gdi32.DeleteDC(hdcSrc);
graphics.ReleaseHdc(hdcDest);
graphics.Dispose();
Gdi32.DeleteObject(hbitmap);
return bitmap;
}
}
It seems copying pixel by pixel was the solution. The following seems to be pixel-perfect equal to the explorer one.
public static Image GetIcon(string fileName, int size)
{
IShellItem shellItem;
Shell32.SHCreateItemFromParsingName(fileName, IntPtr.Zero, Shell32.IShellItem_GUID, out shellItem);
IntPtr hbitmap;
((IShellItemImageFactory)shellItem).GetImage(new SIZE(size, size), 0x0, out hbitmap);
// get the info about the HBITMAP inside the IPictureDisp
DIBSECTION dibsection = new DIBSECTION();
Gdi32.GetObjectDIBSection(hbitmap, Marshal.SizeOf(dibsection), ref dibsection);
int width = dibsection.dsBm.bmWidth;
int height = dibsection.dsBm.bmHeight;
// create the destination Bitmap object
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
for (int x = 0; x < dibsection.dsBmih.biWidth; x++)
{
for (int y = 0; y < dibsection.dsBmih.biHeight; y++)
{
int i = y * dibsection.dsBmih.biWidth + x;
IntPtr ptr = dibsection.dsBm.bmBits + (i * Marshal.SizeOf(typeof(RGBQUAD)));
var rgbquad = (RGBQUAD)Marshal.PtrToStructure(ptr, typeof(RGBQUAD));
if (rgbquad.rgbReserved != 0)
bitmap.SetPixel(x, y, Color.FromArgb(rgbquad.rgbReserved, rgbquad.rgbRed, rgbquad.rgbGreen, rgbquad.rgbBlue));
}
}
Gdi32.DeleteObject(hbitmap);
return bitmap;
}