C# - 捕获鼠标光标图像 [英] C# - Capturing the Mouse cursor image

查看:333
本文介绍了C# - 捕获鼠标光标图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景





MY PROBLEM




  • 当鼠标光标为正常指针或手形图标 - 鼠标在屏幕截图上正确呈现

  • 但是,当鼠标光标更改为插入点(I-beam光标)时 - 例如输入NOTEPAD - 不工作 - 结果是我得到一个微弱的光标图像 - 像一个非常半透明的(灰色)版本的它,而不是空白&


      li>如果图像是这些I-beam类型图像之一,我如何捕获鼠标光标图像
    • 注意:如果您点击原始文章有人提出建议 - 它不起作用



    SOURCE



      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 .hCursor);
    if(Win32Stuff.GetIconInfo(hicon,out icInfo))
    {
    x = ci.ptScreenPos.x - ((int)icInfo.xHotspot);
    y = ci.ptScreenPos.y - ((int)icInfo.yHotspot);

    图标ic = Icon.FromHandle(hicon);
    bmp = ic.ToBitmap();
    return bmp;
    }
    }
    }

    return null;
    }


    解决方案



    ICONINFO结构包含两个成员hbmMask和hbmColor,它们包含掩码和颜色位图, (对于 ICONINFO 的MSDN页面)官方文档)。



    当为默认游标调用GetIconInfo()时,ICONINFO结构包含有效的掩码和颜色位图,如下所示(注意:已添加以清楚地显示图像边界):



    默认光标掩码位图 默认光标掩码位图图像http://img4.imageshack.us/img4/1108/arrowmask.png



    默认光标颜色位图 默认光标颜色位图图片http://img191.imageshack.us/img191/7680/arrowcolor.png



    当Windows绘制默认光标时,掩码位图首先应用AND光栅操作,然后用XOR光栅操作应用颜色位图。



    当为I-Beam游标调用GetIconInfo()时,ICONINFO结构只包含一个有效的掩码位图,并且没有颜色位图,如下所示(注意:再次,已添加红色边框以清楚地显示图像边界):



    I-Beam Cursor掩码位图 ibeam游标掩码位图图像http://img14.imageshack.us /img14/6025/ibeammask.png



    根据ICONINFO文档,I-Beam光标是一个单色光标。掩码位图的上半部分是AND掩码,掩码位图的下半部分是XOR位图。当Windows绘制I-Beam光标时,该位图的上半部分首先在具有AND光栅操作的桌面上绘制。然后,使用XOR光栅操作在顶部绘制位图的下半部分。



    评论。在桌面上,由于光栅操作应用于桌面内容,因此光标将显示正确。然而,当在没有背景绘制图像时,如在您发布的代码中,Windows执行的光栅操作会导致褪色的图像。

    也就是说,这更新CaptureCursor()方法将处理彩色和单色游标,当光标为单色时提供纯黑色光标图像。

     静态位图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);

    使用(Bitmap maskBitmap = Bitmap.FromHbitmap(iconInfo.hbmMask))
    {
    //这是一个单色光标吗?
    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());

    使用(Graphics resultGraphics = Graphics.FromImage(resultBitmap))
    {
    IntPtr resultHdc = resultGraphics.GetHdc();

    //这两个操作会在白色背景上产生一个黑色光标。
    //稍后在代码中,调用MakeTransparent()会摆脱白色背景。
    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);

    //从BitBlt调用中删除白色背景,
    //在透明背景上产生一个黑色光标。
    resultBitmap.MakeTransparent(Color.White);
    return resultBitmap;
    }
    }

    图标icon = Icon.FromHandle(hicon);
    return icon.ToBitmap();
    }

    代码有一些问题,可能有问题也可能不是问题。


    1. 检查单色光标只是测试高度是否为宽度的两倍。虽然这似乎是合乎逻辑的,但ICONINFO文档并不强制要求仅由此定义一个单色光标。

    2. 有一种更好的方法来渲染BitBlt() - BitBlt ) - MakeTransparent()我使用的方法调用的组合。


    BACKGROUND

    MY PROBLEM

    • Code works fine when the mouse cursor is the normal pointer or hand icon - the mouse is rendered correctly on the screenshot
    • However, when the mouse cursor is changed to the insertion point (the "I-beam" cursor) - for example typing in NOTEPAD - then code doesn't work - the result is that I get a faint image of the cursor - like a very translucent (gray) version of it instead of the blank & white one would expect.

    MY QUESTION

    • How can I capture the mouse cursor image when the image is one of these "I-beam"-type images
    • NOTE: If you click on the original article someone offers a suggestion - it doesn't work

    SOURCE

    This is from the original article.

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

    解决方案

    While I can't explain exactly why this happens, I think I can show how to get around it.

    The ICONINFO struct contains two members, hbmMask and hbmColor, that contain the mask and color bitmaps, respectively, for the cursor (see the MSDN page for ICONINFO for the official documentation).

    When you call GetIconInfo() for the default cursor, the ICONINFO struct contains both valid mask and color bitmaps, as shown below (Note: the red border has been added to clearly show the image boundaries):

    Default Cursor Mask Bitmap default cursor mask bitmap image http://img4.imageshack.us/img4/1108/arrowmask.png

    Default Cursor Color Bitmap default cursor color bitmap image http://img191.imageshack.us/img191/7680/arrowcolor.png

    When Windows draws the default cursor, the mask bitmap is first applied with an AND raster operation, then the color bitmap is applied with an XOR raster operation. This results in an opaque cursor and a transparent background.

    When you call GetIconInfo() for the I-Beam cursor, though, the ICONINFO struct only contains a valid mask bitmap, and no color bitmap, as shown below (Note: again, the red border has been added to clearly show the image boundaries):

    I-Beam Cursor Mask Bitmap ibeam cursor mask bitmap image http://img14.imageshack.us/img14/6025/ibeammask.png

    According to the ICONINFO documentation, the I-Beam cursor is then a monochrome cursor. The top half of the mask bitmap is the AND mask, and the bottom half of the mask bitmap is the XOR bitmap. When Windows draws the I-Beam cursor, the top half of this bitmap is first drawn over the desktop with an AND raster operation. The bottom half of the bitmap is then drawn over top with an XOR raster operation. Onscreen, The cursor will appear as the inverse of the content behind it.

    One of the comments for the original article that you linked mentions this. On the desktop, since the raster operations are applied over the desktop content, the cursor will appear correct. However, when the image is drawn over no background, as in your posted code, the raster operations that Windows performs result in a faded image.

    That being said, this updated CaptureCursor() method will handle both color and monochrome cursors, supplying a plain black cursor image when the cursor is monochrome.

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

    There are some issues with the code that may or may not be a problem.

    1. The check for a monochrome cursor simply tests whether the height is twice the width. While this seems logical, the ICONINFO documentation does not mandate that only a monochrome cursor is defined by this.
    2. There is probably a better way to render the cursor that the BitBlt() - BitBlt() - MakeTransparent() combination of method calls I used.

    这篇关于C# - 捕获鼠标光标图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆