您的位置:首页 > 编程语言 > C#

浅析如何用C#.NET做屏幕截图软件以及注册全局快捷键(上)1

2010-04-04 18:06 260 查看
最近为了学习C#,决定自己做一个屏幕截图工具,来代替长久以来每次都要按下PrintScreen键然后到CliPBoard里面寻找之麻烦。学以致用~

  用C#做屏幕截图,大致有三种方法。

  1、最managed大概就是使用Graphics.CopyFromScreen()方法,此方法有四个重载,不过经反编可见最后调用的都是:

CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation) 


  调用该方法的优点是代码简单,不用平台调用。不过最大的缺点是:不能截取半透明窗体。你要想截图透明窗体的话,可以加一个参数,它就是:CopyPixelOperation.CaptureBlt。但是,这样的话,只能截图透明窗体,截不到屏幕的其他部分了。所以,该方法已经基本可以放弃了。不过我们还是来研究了一下CopyFromScreen()的内部实现吧,看看问题之所在。下面是该方法的具体实现,reflect自System.Drawing.dll

  CopyFromScreen

public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation)
{
  switch (copyPixelOperation)
  {
    case CopyPixelOperation.NotSourceErase:
    case CopyPixelOperation.NotSourceCopy:
    case CopyPixelOperation.NoMirrorBitmap:
    case CopyPixelOperation.Blackness:
    case CopyPixelOperation.SourceErase:
    ...
    case CopyPixelOperation.SourceCopy:
    case CopyPixelOperation.SourceAnd:
    case CopyPixelOperation.MergePaint:
    case CopyPixelOperation.SourcePaint:
    case CopyPixelOperation.PatCopy:
    case CopyPixelOperation.PatPaint:
    case CopyPixelOperation.Whiteness:
    case CopyPixelOperation.CaptureBlt:
    {
      new UIPermission(UIPermissionWindow.AllWindows).Demand();
      int width = blockRegionSize.Width;
      int height = blockRegionSize.Height;
      using (DeviceContext context = DeviceContext.FromHwnd(IntPtr.Zero))
      {
        HandleRef hSrcDC = new HandleRef(null, context.Hdc);
        HandleRef hDC = new HandleRef(null, this.GetHdc());
        try
        {
          if (SafeNativeMethods.BitBlt(hDC, destinationX, destinationY, width, height, hSrcDC, sourceX, sourceY, (int) copyPixelOperation) == 0)
          {
            throw new Win32Exception();
          }
        }
        finally
        {
          this.ReleaseHdc();
        }
      }
      return;
    }
  }
  throw new InvalidEnumArgumentException("value", (int) copyPixelOperation, typeof(CopyPixelOperation));
}


通过上面代码可知,类库里面最后调用的是Windows 的API :BitBlt(),这就是我们要说的第二种方法。


  2、平台调用,即调用Windows的API函数。其中要用到的最主要的就是BitBlt()。在此,本人先给各位推荐个网站,它就是:www.pinvoke.net,一个包含几乎所有API的wiki,里面一般还含有示例代码~为了便于没有网络的情况下查询,本人已经把该网站拷贝下来了。。耗费了一天时间做成CHM。。。

  先来看看BitBlt()的description:The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context.

  这里,我们只关心它的最后一个参数/// <param name="dwRop">A raster-operation code.</param>

  中文翻译大概叫作:光栅操作码。大概就是控制从source device context 到 destination device context的拷贝方式。它是一个DWORD类型,取值在

// Summary: 
    //     Determines how the source color in a copy pixel operation is combined with 
    //     the destination color to result in a final color. 
    public enum CopyPixelOperation 
    { 
        // Summary: 
        //     The bitmap is not mirrored. 
        NoMirrorBitmap = -2147483648, 
        // 
        // Summary: 
        //     The destination area is filled by using the color associated with index 0 
        //     in the physical palette. (This color is black for the default physical palette.) 
        Blackness = 66, 
        // 
        // Summary: 
        //     The source and destination colors are combined using the Boolean OR operator, 
        //     and then resultant color is then inverted. 
        NotSourceErase = 1114278, 
        // 
        // Summary: 
        //     The inverted source area is copied to the destination. 
        NotSourceCopy = 3342344, 
        // 
        // Summary: 
        //     The inverted colors of the destination area are combined with the colors 
        //     of the source area using the Boolean AND operator. 
        SourceErase = 4457256, 
        // 
        // Summary: 
        //     The destination area is inverted. 
        DestinationInvert = 5570569, 
        // 
        // Summary: 
        //     The colors of the brush currently selected in the destination device context 
        //     are combined with the colors of the destination are using the Boolean XOR 
        //     operator. 
        PatInvert = 5898313, 
        // 
        // Summary: 
        //     The colors of the source and destination areas are combined using the Boolean 
        //     XOR operator. 
        SourceInvert = 6684742, 
        // 
        // Summary: 
        //     The colors of the source and destination areas are combined using the Boolean 
        //     AND operator. 
        SourceAnd = 8913094, 
        // 
        // Summary: 
        //     The colors of the inverted source area are merged with the colors of the 
        //     destination area by using the Boolean OR operator. 
        MergePaint = 12255782, 
        // 
        // Summary: 
        //     The colors of the source area are merged with the colors of the selected 
        //     brush of the destination device context using the Boolean AND operator. 
        MergeCopy = 12583114, 
        // 
        // Summary: 
        //     The source area is copied directly to the destination area. 
        SourceCopy = 13369376, 
        // 
        // Summary: 
        //     The colors of the source and destination areas are combined using the Boolean 
        //     OR operator. 
        SourcePaint = 15597702, 
        // 
        // Summary: 
        //     The brush currently selected in the destination device context is copied 
        //     to the destination bitmap. 
        PatCopy = 15728673, 
        // 
        // Summary: 
        //     The colors of the brush currently selected in the destination device context 
        //     are combined with the colors of the inverted source area using the Boolean 
        //     OR operator. The result of this operation is combined with the colors of 
        //     the destination area using the Boolean OR operator. 
        PatPaint = 16452105, 
        // 
        // Summary: 
        //     The destination area is filled by using the color associated with index 1 
        //     in the physical palette. (This color is white for the default physical palette.) 
        Whiteness = 16711778, 
        // 
        // Summary: 
        //     Windows that are layered on top of your window are included in the resulting 
        //     image. By default, the image contains only your window. Note that this generally 
        //     cannot be used for printing device contexts. 
        CaptureBlt = 1073741824, 
    } 


里面。


  现在继续回来说截取透明窗体的问题,我们知道它跟dwRop参数有关,在codeproject上面可以找到的源码,调用BitBlt()时,都是这样的:

BitBlt( hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, PlatformInvokeGDI32.TernaryRasterOperations.SourceCopy); 


  我想一般的屏幕截图软件也是这样做的,这样做的话,你是不能截取到透明窗体的,我们应该改成这个样子:

BitBlt( hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, (uint)(PlatformInvokeGDI32.TernaryRasterOperations.SourceCopy | PlatformInvokeGDI32.TernaryRasterOperations.CaptureBlt) ); 


  通过试验可知是实现了预期效果。CaptureBlt的作用在上面~

  需要注意的是,当我们使用BitBlt的时候,涉及设备上下文句柄操作,需要用到另外几个API函数来创建和释放资源。贴个例子:

  GetDesktopImage

public static Bitmap GetDesktopImage()
    {
      //In size variable we shall keep the size of the screen.
      SIZE size;
      //Variable to keep the handle to bitmap.
      IntPtr hBitmap;
      //Here we get the handle to the desktop device context.
      IntPtr   hDC = PlatformInvokeUSER32.GetDC(PlatformInvokeUSER32.GetDesktopWindow()); 
      //Here we make a compatible device context in memory for screen device context.
      IntPtr hMemDC = PlatformInvokeGDI32.CreateCompatibleDC(hDC);
      //We pass SM_CXSCREEN constant to GetSystemMetrics to get the X coordinates of screen.
      size.cx = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CXSCREEN);
      //We pass SM_CYSCREEN constant to GetSystemMetrics to get the Y coordinates of screen.
      size.cy = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CYSCREEN);
      //We create a compatible bitmap of screen size using screen device context.
      hBitmap = PlatformInvokeGDI32.CreateCompatibleBitmap(hDC, size.cx, size.cy);
      //As hBitmap is IntPtr we can not check it against null. For this purspose IntPtr.Zero is used.
      if (hBitmap!=IntPtr.Zero)
      {
        //Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
        IntPtr hOld = (IntPtr) PlatformInvokeGDI32.SelectObject(hMemDC, hBitmap);
        //We copy the Bitmap to the memory device context.
        PlatformInvokeGDI32.BitBlt( hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, (uint)(PlatformInvokeGDI32.TernaryRasterOperations.SRCCOPY | PlatformInvokeGDI32.TernaryRasterOperations.CAPTUREBLT) );
        //We select the old bitmap back to the memory device context.
        PlatformInvokeGDI32.SelectObject(hMemDC, hOld);
        //We delete the memory device context.
        PlatformInvokeGDI32.DeleteDC(hMemDC);
        //We release the screen device context.
        PlatformInvokeUSER32.ReleaseDC(PlatformInvokeUSER32.GetDesktopWindow(), hDC);
        //Image is created by Image bitmap handle and stored in local variable.
        Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap); 
        //Release the memory to avoid memory leaks.
        PlatformInvokeGDI32.DeleteObject(hBitmap);
        //This statement runs the garbage collector manually.
        GC.Collect();
        //Return the bitmap 
        return bmp;
      }
      //If hBitmap is null retunrn null.
      return null;
    }


  可见虽然直接的平台调用相比Graphics.CopyFromScreen()速度快,灵活,但是还是相当繁琐的。涉及到句柄操作。这些操作在Graphics.CopyFromScreen()已经替我们做了。

  下面来说第三种方法:

  出处:http://1971ruru.cnblogs.com/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐