I'm using this code to capture the screen + cursor:
new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand();
var success = Native.BitBlt(_compatibleDeviceContext, 0, 0, Width, Height, _windowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt);
if (!success)
return FrameCount;
try
{
var cursorInfo = new Native.CursorInfo();
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
if (Native.GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == Native.CursorShowing)
{
var hicon = Native.CopyIcon(cursorInfo.hCursor);
if (hicon != IntPtr.Zero)
{
if (Native.GetIconInfo(hicon, out var iconInfo))
{
frame.CursorX = cursorInfo.ptScreenPos.X - Left;
frame.CursorY = cursorInfo.ptScreenPos.Y - Top;
//if (frame.CursorX > 0 && frame.CursorY > 0)
Native.DrawIconEx(_compatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, 0, IntPtr.Zero, 0x0003);
}
Native.DeleteObject(iconInfo.hbmColor);
Native.DeleteObject(iconInfo.hbmMask);
}
Native.DestroyIcon(hicon);
}
Native.DeleteObject(cursorInfo.hCursor);
}
}
catch (Exception)
{
//LogWriter.Log(e, "Impossible to get the cursor");
}
frame.DataLength = _byteLength;
frame.Data = new byte[_byteLength];
//If saving to disk as bitmap, it works.
//System.Drawing.Image.FromHbitmap(_compatibleBitmap).Save(frame.Path);
//If getting the image as pixel array, the square where the cursor is located is all transparent.
Native.GetDIBits(_windowDeviceContext, _compatibleBitmap, 0, (uint)Height, frame.Data, ref _infoHeader, Native.DibColorMode.DibRgbColors);
For some reason, when the cursor is the I-beam (text cursor), the image region where it's supposed to be located is all transparent.
The RGB info is there, but the alpha bit are 0
.
(The right image is what the frame will look like after all pixel alpha bits are set to 255, manually).
If the cursor is the arrow, it works normally.
But if I get the Bitmap
from the handle and then save to disk, the image has no transparent hole, as expected.
What's going on?
Is it something with DrawIconEx
or with FromHbitmap
?
Maybe FromHbitmap
always sets the alpha to 255 for all pixels?
Maybe GetDiBits
merges the two images (screenshot + cursor) differently?
As a quick fix, I understand that I can detect if the cursor is an I-Beam (masked monochrome type) and manually fix it while saving the pixel array.
//Set to fix all alpha bits back to 255.
frame.RemoveAnyTransparency = iconInfo.hbmMask != IntPtr.Zero;
And:
if (frame.RemoveAnyTransparency)
for (var i = 3; i < _byteLength; i += 4)
frame.Data[i] = 255;
Well, I suspect this is a special case. Beacuse I checked a lot of information and didn't find similar cases.
But there are two good ways to deal with this situation.
biBitCount = 24
Copy the cursor data into the DIBSection's bitmap data, using the alpha channel byte to blend(alpha = 255).
Create a device context and select the DIBSection into it.
Use BitBlt()
to copy from the new device context to the paint device context.