なぜこれが起こるのか正確に説明することはできませんが、それを回避する方法を示すことができると思います.
ICONINFO 構造体には、カーソルのマスク ビットマップとカラー ビットマップをそれぞれ含む hbmMask と hbmColor の 2 つのメンバーが含まれています (公式ドキュメントについては、ICONINFOの MSDN ページを参照してください)。
デフォルト カーソルに対して GetIconInfo() を呼び出すと、以下に示すように、ICONINFO 構造体に有効なマスク ビットマップとカラー ビットマップの両方が含まれます (注: 画像の境界を明確に示すために赤い境界線が追加されています)。
デフォルトのカーソル マスク ビットマップ
デフォルトのカーソル カラー ビットマップ
Windows が既定のカーソルを描画するとき、マスク ビットマップは最初に AND ラスター操作で適用され、次にカラー ビットマップが XOR ラスター操作で適用されます。これにより、カーソルが不透明になり、背景が透明になります。
ただし、I ビーム カーソルに対して GetIconInfo() を呼び出すと、以下に示すように、ICONINFO 構造体には有効なマスク ビットマップのみが含まれ、カラー ビットマップは含まれません (注: ここでも、画像の境界を明確に示すために赤い境界線が追加されています):
I ビーム カーソル マスク ビットマップ
ICONINFO のドキュメントによると、I ビーム カーソルはモノクロ カーソルです。マスク ビットマップの上半分は AND マスクで、マスク ビットマップの下半分は XOR ビットマップです。Windows が I ビーム カーソルを描画するとき、最初にこのビットマップの上半分が AND ラスター操作でデスクトップ上に描画されます。ビットマップの下半分は、XOR ラスター操作で上に描画されます。画面上では、カーソルはその背後にあるコンテンツの反転として表示されます。
リンクした元の記事のコメントの1つに、これについて言及されています。デスクトップでは、ラスター操作がデスクトップ コンテンツに適用されるため、カーソルは正しく表示されます。ただし、投稿されたコードのように、画像が背景なしで描画されると、Windows が実行するラスター操作によって画像が薄くなります。
そうは言っても、この更新された CaptureCursor() メソッドはカラー カーソルとモノクロ カーソルの両方を処理し、カーソルがモノクロの場合はプレーンな黒のカーソル イメージを提供します。
static Bitmap CaptureCursor(ref int x, ref int y)
{
Win32Stuff.CURSORINFO cursorInfo = new Win32Stuff.CURSORINFO();
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
if (!Win32Stuff.GetCursorInfo(out cursorInfo))
return null;
if (cursorInfo.flags != Win32Stuff.CURSOR_SHOWING)
return null;
IntPtr hicon = Win32Stuff.CopyIcon(cursorInfo.hCursor);
if (hicon == IntPtr.Zero)
return null;
Win32Stuff.ICONINFO iconInfo;
if (!Win32Stuff.GetIconInfo(hicon, out iconInfo))
return null;
x = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
y = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
using (Bitmap maskBitmap = Bitmap.FromHbitmap(iconInfo.hbmMask))
{
// Is this a monochrome cursor?
if (maskBitmap.Height == maskBitmap.Width * 2)
{
Bitmap resultBitmap = new Bitmap(maskBitmap.Width, maskBitmap.Width);
Graphics desktopGraphics = Graphics.FromHwnd(Win32Stuff.GetDesktopWindow());
IntPtr desktopHdc = desktopGraphics.GetHdc();
IntPtr maskHdc = Win32Stuff.CreateCompatibleDC(desktopHdc);
IntPtr oldPtr = Win32Stuff.SelectObject(maskHdc, maskBitmap.GetHbitmap());
using (Graphics resultGraphics = Graphics.FromImage(resultBitmap))
{
IntPtr resultHdc = resultGraphics.GetHdc();
// These two operation will result in a black cursor over a white background.
// Later in the code, a call to MakeTransparent() will get rid of the white background.
Win32Stuff.BitBlt(resultHdc, 0, 0, 32, 32, maskHdc, 0, 32, Win32Stuff.TernaryRasterOperations.SRCCOPY);
Win32Stuff.BitBlt(resultHdc, 0, 0, 32, 32, maskHdc, 0, 0, Win32Stuff.TernaryRasterOperations.SRCINVERT);
resultGraphics.ReleaseHdc(resultHdc);
}
IntPtr newPtr = Win32Stuff.SelectObject(maskHdc, oldPtr);
Win32Stuff.DeleteObject(newPtr);
Win32Stuff.DeleteDC(maskHdc);
desktopGraphics.ReleaseHdc(desktopHdc);
// Remove the white background from the BitBlt calls,
// resulting in a black cursor over a transparent background.
resultBitmap.MakeTransparent(Color.White);
return resultBitmap;
}
}
Icon icon = Icon.FromHandle(hicon);
return icon.ToBitmap();
}
コードには、問題になる場合とそうでない場合があるいくつかの問題があります。
- モノクロ カーソルのチェックは、高さが幅の 2 倍であるかどうかを単純にテストします。これは論理的に思えますが、ICONINFO のドキュメントでは、これによってモノクロ カーソルのみを定義することは義務付けられていません。
- 私が使用したメソッド呼び出しの BitBlt() - BitBlt() - MakeTransparent() の組み合わせよりも、カーソルをレンダリングするためのより良い方法がおそらくあるでしょう。