tipos punteros puntero personalizado para los funcion estilos cursores como clasificacion cambiar aspectos animados c# mouse cursor icons screenshot

c# - personalizado - punteros para mouse animados



C#- Captura de la imagen del cursor del mouse (6)

FONDO

MI PROBLEMA

  • El código funciona bien cuando el cursor del mouse es el puntero normal o el ícono de la mano: el mouse se muestra correctamente en la captura de pantalla
  • Sin embargo, cuando el cursor del mouse se cambia al punto de inserción (el cursor "I-beam"), por ejemplo, escribiendo NOTEPAD, el código no funciona, el resultado es que obtengo una imagen tenue del cursor, como una muy translúcida (gris) versión de ella en lugar del blanco y blanco que cabría esperar.

MI PREGUNTA

  • ¿Cómo puedo capturar la imagen del cursor del mouse cuando la imagen es una de estas imágenes de tipo "I-beam"?
  • NOTA: si hace clic en el artículo original, alguien ofrece una sugerencia, no funciona

FUENTE

Esto es del artículo original.

static Bitmap CaptureCursor(ref int x, ref int y) { Bitmap bmp; IntPtr hicon; Win32Stuff.CURSORINFO ci = new Win32Stuff.CURSORINFO(); Win32Stuff.ICONINFO icInfo; ci.cbSize = Marshal.SizeOf(ci); if (Win32Stuff.GetCursorInfo(out ci)) { if (ci.flags == Win32Stuff.CURSOR_SHOWING) { hicon = Win32Stuff.CopyIcon(ci.hCursor); if (Win32Stuff.GetIconInfo(hicon, out icInfo)) { x = ci.ptScreenPos.x - ((int)icInfo.xHotspot); y = ci.ptScreenPos.y - ((int)icInfo.yHotspot); Icon ic = Icon.FromHandle(hicon); bmp = ic.ToBitmap(); return bmp; } } } return null; }


Aquí hay una versión modificada de la respuesta de Dimitar (utilizando DrawIconEx) que funcionó para mí en varias pantallas:

public class ScreenCapturePInvoke { [StructLayout(LayoutKind.Sequential)] private struct CURSORINFO { public Int32 cbSize; public Int32 flags; public IntPtr hCursor; public POINTAPI ptScreenPos; } [StructLayout(LayoutKind.Sequential)] private struct POINTAPI { public int x; public int y; } [DllImport("user32.dll")] private static extern bool GetCursorInfo(out CURSORINFO pci); [DllImport("user32.dll", SetLastError = true)] static extern bool DrawIconEx(IntPtr hdc, int xLeft, int yTop, IntPtr hIcon, int cxWidth, int cyHeight, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, int diFlags); private const Int32 CURSOR_SHOWING = 0x0001; private const Int32 DI_NORMAL = 0x0003; public static Bitmap CaptureFullScreen(bool captureMouse) { var allBounds = Screen.AllScreens.Select(s => s.Bounds).ToArray(); Rectangle bounds = Rectangle.FromLTRB(allBounds.Min(b => b.Left), allBounds.Min(b => b.Top), allBounds.Max(b => b.Right), allBounds.Max(b => b.Bottom)); var bitmap = CaptureScreen(bounds, captureMouse); return bitmap; } public static Bitmap CapturePrimaryScreen(bool captureMouse) { Rectangle bounds = Screen.PrimaryScreen.Bounds; var bitmap = CaptureScreen(bounds, captureMouse); return bitmap; } public static Bitmap CaptureScreen(Rectangle bounds, bool captureMouse) { Bitmap result = new Bitmap(bounds.Width, bounds.Height); try { using (Graphics g = Graphics.FromImage(result)) { g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size); if (captureMouse) { CURSORINFO pci; pci.cbSize = Marshal.SizeOf(typeof (CURSORINFO)); if (GetCursorInfo(out pci)) { if (pci.flags == CURSOR_SHOWING) { var hdc = g.GetHdc(); DrawIconEx(hdc, pci.ptScreenPos.x-bounds.X, pci.ptScreenPos.y-bounds.Y, pci.hCursor, 0, 0, 0, IntPtr.Zero, DI_NORMAL); g.ReleaseHdc(); } } } } } catch { result = null; } return result; } }


Basado en las otras respuestas, hice una versión sin todas las cosas de la API de Windows (para la parte monocromática) porque las soluciones no funcionaron para todos los cursores monocromáticos. Creo el cursor desde la máscara combinando las dos partes de la máscara.

Mi solución:

Bitmap CaptureCursor(ref Point position) { CURSORINFO cursorInfo = new CURSORINFO(); cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); if (!GetCursorInfo(out cursorInfo)) return null; if (cursorInfo.flags != CURSOR_SHOWING) return null; IntPtr hicon = CopyIcon(cursorInfo.hCursor); if (hicon == IntPtr.Zero) return null; ICONINFO iconInfo; if (!GetIconInfo(hicon, out iconInfo)) return null; position.X = cursorInfo.ptScreenPos.x - iconInfo.xHotspot; position.Y = cursorInfo.ptScreenPos.y - iconInfo.yHotspot; using (Bitmap maskBitmap = Bitmap.FromHbitmap(iconInfo.hbmMask)) { // check for monochrome cursor if (maskBitmap.Height == maskBitmap.Width * 2) { Bitmap cursor = new Bitmap(32, 32, PixelFormat.Format32bppArgb); Color BLACK = Color.FromArgb(255, 0, 0, 0); //cannot compare Color.Black because of different names Color WHITE = Color.FromArgb(255, 255, 255, 255); //cannot compare Color.White because of different names for (int y = 0; y < 32; y++) { for (int x = 0; x < 32; x++) { Color maskPixel = maskBitmap.GetPixel(x, y); Color cursorPixel = maskBitmap.GetPixel(x, y + 32); if (maskPixel == WHITE && cursorPixel == BLACK) { cursor.SetPixel(x, y, Color.Transparent); } else if (maskPixel == BLACK) { cursor.SetPixel(x, y, cursorPixel); } else { cursor.SetPixel(x, y, cursorPixel == BLACK ? WHITE : BLACK); } } } return cursor; } } Icon icon = Icon.FromHandle(hicon); return icon.ToBitmap(); }


Esta es la versión parcheada con todas las correcciones para los errores presentados en esta página:

public static Bitmap CaptureImageCursor(ref Point point) { try { var cursorInfo = new CursorInfo(); cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); if (!GetCursorInfo(out cursorInfo)) return null; if (cursorInfo.flags != CursorShowing) return null; var hicon = CopyIcon(cursorInfo.hCursor); if (hicon == IntPtr.Zero) return null; Iconinfo iconInfo; if (!GetIconInfo(hicon, out iconInfo)) { DestroyIcon(hicon); return null; } point.X = cursorInfo.ptScreenPos.X - iconInfo.xHotspot; point.Y = cursorInfo.ptScreenPos.Y - iconInfo.yHotspot; using (var maskBitmap = Image.FromHbitmap(iconInfo.hbmMask)) { //Is this a monochrome cursor? if (maskBitmap.Height == maskBitmap.Width * 2 && iconInfo.hbmColor == IntPtr.Zero) { var final = new Bitmap(maskBitmap.Width, maskBitmap.Width); var hDesktop = GetDesktopWindow(); var dcDesktop = GetWindowDC(hDesktop); using (var resultGraphics = Graphics.FromImage(final)) { var resultHdc = resultGraphics.GetHdc(); BitBlt(resultHdc, 0, 0, final.Width, final.Height, dcDesktop, (int)point.X + 3, (int)point.Y + 3, CopyPixelOperation.SourceCopy); DrawIconEx(resultHdc, 0, 0, cursorInfo.hCursor, 0, 0, 0, IntPtr.Zero, 0x0003); //TODO: I have to try removing the background of this cursor capture. //Native.BitBlt(resultHdc, 0, 0, final.Width, final.Height, dcDesktop, (int)point.X + 3, (int)point.Y + 3, Native.CopyPixelOperation.SourceErase); resultGraphics.ReleaseHdc(resultHdc); ReleaseDC(hDesktop, dcDesktop); } DeleteObject(iconInfo.hbmMask); DeleteDC(dcDesktop); DestroyIcon(hicon); return final; } DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); DestroyIcon(hicon); } var icon = Icon.FromHandle(hicon); return icon.ToBitmap(); } catch (Exception ex) { //You should catch exception with your method here. //LogWriter.Log(ex, "Impossible to get the cursor."); } return null; }

Esta versión funciona con:

  1. Cursores I-Beam.
  2. Cursores negros
  3. Cursores normales
  4. Cursores invertidos.

Ver trabajando, aquí: https://github.com/NickeManarin/ScreenToGif/blob/master/ScreenToGif/Util/Native.cs#L991


La descripción de una versión translúcida ''gris'' del cursor en forma de I hace que me pregunte si se encuentra con un problema con la escala de la imagen o la posición incorrecta del cursor.

Una de las personas que publicaron en ese sitio proporcionó un enlace (roto) a un informe con un comportamiento peculiar que he rastreado a: http://www.efg2.com/Lab/Graphics/CursorOverlay.htm

Los ejemplos en esa página no están en C # pero el autor de la solución del proyecto de código puede haber estado haciendo algo similar y sé que he arruinado mi escala al usar el objeto gráfico en muchas ocasiones:

En cualquier evento de ImageMouseDown una vez que se carga una imagen, CusorBitmap se dibuja con transparencia en la parte superior del mapa de bits utilizando el método Canvas.Draw. Tenga en cuenta que se necesitan algunos ajustes de coordenadas (reescalado) en caso de que el mapa de bits se alargue para ajustarse al TImage.


Si bien no puedo explicar exactamente por qué sucede esto, creo que puedo mostrar cómo evitarlo.

La estructura ICONINFO contiene dos miembros, hbmMask y hbmColor, que contienen la máscara y los mapas de bits de color, respectivamente, para el cursor (consulte la página de MSDN para ICONINFO para obtener la documentación oficial).

Cuando llama a GetIconInfo () para el cursor predeterminado, la estructura ICONINFO contiene mapas de bits de máscara y color válidos, como se muestra a continuación (Nota: el borde rojo se ha agregado para mostrar claramente los límites de la imagen):

Máscara de cursor predeterminada Imagen de mapa de bits de máscara de cursor predeterminada de mapa de bits http://img4.imageshack.us/img4/1108/arrowmask.png

Imagen predeterminada del mapa de bits del color predeterminado del cursor del Mapa de bits del mapa de bits http://img191.imageshack.us/img191/7680/arrowcolor.png

Cuando Windows dibuja el cursor predeterminado, el mapa de bits de la máscara se aplica primero con una operación de trama AND, luego el mapa de bits de color se aplica con una operación de trama XOR. Esto da como resultado un cursor opaco y un fondo transparente.

Sin embargo, cuando llama a GetIconInfo () para el cursor I-Beam, la estructura ICONINFO solo contiene un mapa de bits de máscara válido y ningún mapa de bits de color, como se muestra a continuación (Nota: de nuevo, el borde rojo se ha agregado para mostrar claramente los límites de la imagen )

I-Beam Cursor Mask Bitmap ibeam máscara de cursor imagen de mapa de bits http://img14.imageshack.us/img14/6025/ibeammask.png

De acuerdo con la documentación de ICONINFO, el cursor I-Beam es entonces un cursor monocromático. La mitad superior del mapa de bits de la máscara es la máscara Y, y la mitad inferior del mapa de bits de la máscara es el mapa de bits XOR. Cuando Windows dibuja el cursor I-Beam, la mitad superior de este mapa de bits se dibuja primero sobre el escritorio con una operación de trama AND. La mitad inferior del mapa de bits se dibuja sobre la parte superior con una operación de trama XOR. En pantalla, el cursor aparecerá como el inverso del contenido detrás de él.

Uno de los comments para el artículo original que vinculó menciona esto. En el escritorio, dado que las operaciones de ráster se aplican sobre el contenido del escritorio, el cursor aparecerá correcto. Sin embargo, cuando la imagen se dibuja sin fondo, como en el código publicado, las operaciones ráster que realiza Windows dan como resultado una imagen difuminada.

Una vez dicho esto, este método actualizado de CaptureCursor () manejará tanto el color como los cursores monocromáticos, y proporcionará una imagen de cursor negro simple cuando el cursor sea monocromo.

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

Hay algunos problemas con el código que pueden o no ser un problema.

  1. La comprobación de un cursor monocromático simplemente prueba si la altura es el doble del ancho. Si bien esto parece lógico, la documentación ICONINFO no exige que solo un cursor monocromo esté definido por esto.
  2. Probablemente haya una mejor manera de representar el cursor que la combinación BitBlt () - BitBlt () - MakeTransparent () de las llamadas a métodos que utilicé.

[StructLayout(LayoutKind.Sequential)] struct CURSORINFO { public Int32 cbSize; public Int32 flags; public IntPtr hCursor; public POINTAPI ptScreenPos; } [StructLayout(LayoutKind.Sequential)] struct POINTAPI { public int x; public int y; } [DllImport("user32.dll")] static extern bool GetCursorInfo(out CURSORINFO pci); [DllImport("user32.dll")] static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon); const Int32 CURSOR_SHOWING = 0x00000001; public static Bitmap CaptureScreen(bool CaptureMouse) { Bitmap result = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format24bppRgb); try { using (Graphics g = Graphics.FromImage(result)) { g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy); if (CaptureMouse) { CURSORINFO pci; pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(CURSORINFO)); if (GetCursorInfo(out pci)) { if (pci.flags == CURSOR_SHOWING) { DrawIcon(g.GetHdc(), pci.ptScreenPos.x, pci.ptScreenPos.y, pci.hCursor); g.ReleaseHdc(); } } } } } catch { result = null; } return result; }