技术文章翻译(五) -- 利用.NET显示当前屏幕内容

本人声明

1.本栏仅为归档自己看到的优秀文章;
2.文章版权归原作者所有;
3.因为个人水平有限,翻译难免有错误,请多多包涵。

原文地址

https://www.codeguru.com/csharp/.net/net_general/displaying-the-computer-screen-using-.net.html

文章正文

利用.NET显示当前屏幕内容

作者: Hannes DuPreez 发表于:2018.07.5

概述

在论坛上,有一个问题经常被人提出,那就是如何在窗体的PictureBox控件上显示当前计算机屏幕上的内容。今天,我将向您做出解答。现在就让我们开始吧!

实例

打开Visual Studio程序,并创建一个C#或Visual Basic.NET 的Windows窗体应用程序。按照1所示,设计你的窗体;按照图2所示,设置窗体的Timer控件的属性。
这里写图片描述
图1 窗体设计

这个窗体中包含一个占满整个窗体显示区域的PictureBox控件和一个定时器控件。

这里写图片描述
图2 定时器属性

增加一个名为clsGDI的类。并将如下所示的命名空间添加到clsGDI类中。

  • C#
using System;
using System.Runtime.InteropServices;
  • VB.NET
Imports System
Imports System.Runtime.InteropServices

这些命名空间能够让您以适当的方式调用Windows API函数。接下来完成clsGDI类的编写。

  • C#
public class clsGDI
{

   public const int SRCCOPY = 13369376;

   [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
   public static extern IntPtr DeleteDC(IntPtr hDc);

   [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
   public static extern IntPtr DeleteObject(IntPtr hDc);

   [DllImport("gdi32.dll", EntryPoint = "BitBlt")]
   public static extern bool BitBlt(IntPtr hdcDest, int xDest,
      int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc,
      int ySrc, int RasterOp);

   [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")]
   public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc,
      int nWidth, int nHeight);

   [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")]
   public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

   [DllImport("gdi32.dll", EntryPoint = "SelectObject")]
   public static extern IntPtr SelectObject(IntPtr hdc,
      IntPtr bmp);

}
  • VB.NET
  Public Const SRCCOPY As Integer = 13369376

   <DllImport("gdi32.dll", EntryPoint:="DeleteDC")>
   Public Shared Function DeleteDC(ByVal hDc As IntPtr) _
      As IntPtr
   End Function

   <DllImport("gdi32.dll", EntryPoint:="DeleteObject")>
   Public Shared Function DeleteObject(ByVal hDc As IntPtr) _
      As IntPtr
   End Function

   <DllImport("gdi32.dll", EntryPoint:="BitBlt")>
   Public Shared Function BitBlt(ByVal hdcDest As IntPtr, _
      ByVal xDest As Integer, ByVal yDest As Integer, ByVal wDest _
      As Integer, ByVal hDest As Integer, ByVal hdcSource As _
      IntPtr, ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal _
      RasterOp As Integer) As Boolean
   End Function

   Public Shared Function CreateCompatibleBitmap(ByVal hdc As _
      IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) _
      As IntPtr
   End Function

   <DllImport("gdi32.dll", EntryPoint:="CreateCompatibleDC")>
   Public Shared Function CreateCompatibleDC(ByVal hdc As IntPtr) _
      As IntPtr
   End Function
   <DllImport("gdi32.dll", EntryPoint:="SelectObject")>
   Public Shared Function SelectObject(ByVal hdc As IntPtr, _
      ByVal bmp As IntPtr) As IntPtr
   End Function

DeleteDC函数用于删除指定的设备上下文。 DeleteObject函数可以删除画笔,画刷,字体,位图,区域或调色板对象;在执行此操作时,它释放与该对象关联的所有系统资源。 BitBlt函数对指定的源设备环境区域中的像素进行位块转换,以传送到目标设备环境。 CreateCompatibleBitmap函数创建与设备兼容的位图对象。 CreateCompatibleDC函数创建与指定设备兼容的内存设备上下文。

添加一个名为clsUSER的类,并为该类添加必要的命名空间来引入Windows API函数。

  • C#*
using System;
using System.Runtime.InteropServices;
  • VB.NET
Imports System
Imports System.Runtime.InteropServices

添加clsUSER类的其余代码。

  • C#
  public class clsUSER
   {

      public const int SM_CXSCREEN = 0;
      public const int SM_CYSCREEN = 1;

      [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
      public static extern IntPtr GetDesktopWindow();

      [DllImport("user32.dll", EntryPoint = "GetDC")]
      public static extern IntPtr GetDC(IntPtr ptr);

      [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
      public static extern int GetSystemMetrics(int abc);

      [DllImport("user32.dll", EntryPoint = "GetWindowDC")]
      public static extern IntPtr GetWindowDC(Int32 ptr);

      [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
      public static extern IntPtr ReleaseDC(IntPtr hWnd,
         IntPtr hDc);

   }

   public struct SIZE
   {
      public int x;
      public int y;
   }
  • VB.NET
Public Class clsUSER

   Public Const SM_CXSCREEN As Integer = 0
   Public Const SM_CYSCREEN As Integer = 1

   <DllImport("user32.dll", EntryPoint:="GetDesktopWindow")>
   Public Shared Function GetDesktopWindow() As IntPtr
   End Function
   <DllImport("user32.dll", EntryPoint:="GetDC")>
   Public Shared Function GetDC(ByVal ptr As IntPtr) As IntPtr
   End Function
   <DllImport("user32.dll", EntryPoint:="GetSystemMetrics")>
   Public Shared Function GetSystemMetrics(ByVal abc As Integer) _
      As Integer
   End Function
   <DllImport("user32.dll", EntryPoint:="GetWindowDC")>
   Public Shared Function GetWindowDC(ByVal ptr As Int32) As IntPtr
   End Function
   <DllImport("user32.dll", EntryPoint:="ReleaseDC")>
   Public Shared Function ReleaseDC(ByVal hWnd As IntPtr, _
      ByVal hDc As IntPtr) As IntPtr
   End Function

End Class

Public Structure SIZE

   Public x As Integer
   Public y As Integer

End Structure

GetDesktopWindow函数获取桌面窗口的句柄,该窗口覆盖整个屏幕范围,并且是绘制其他窗口的顶部区域。 GetDC函数获取特定窗口的客户区域或整个屏幕的设备上下文的句柄。 GetSystemMetrics函数获取指定的系统单位或系统配置参数。 GetWindowDC函数获取整个窗口的设备上下文,包括菜单、滚动条和标题栏。

SIZE是一个包含x和y坐标的公共结构体。

添加一个新类,并将其命名为clsCapScreen。将以下命名空间添加到类中,让其可以使用绘图命名空间内的绘图功能。

  • C#
using System;
using System.Drawing;
  • VB.NET
Imports System
Imports System.Drawing

将GetDesktopImage函数添加到clsCapScreen类中。 clsCapScreen类仅包含该功能。

  • C#
  public static Bitmap GetDesktopImage()
      {

         SIZE ScreenSize;

         IntPtr bitScreenBMP;
         IntPtr iDC = clsUSER.GetDC(clsUSER.GetDesktopWindow());
         IntPtr iMemDC = clsGDI.CreateCompatibleDC(iDC);

         ScreenSize.x = clsUSER.GetSystemMetrics
            (clsUSER.SM_CXSCREEN);
         ScreenSize.y = clsUSER.GetSystemMetrics
            (clsUSER.SM_CYSCREEN);

         bitScreenBMP = clsGDI.CreateCompatibleBitmap(iDC,
            ScreenSize.x, ScreenSize.y);

         if (bitScreenBMP != IntPtr.Zero)
         {

            IntPtr hPrev = (IntPtr)clsGDI.SelectObject(iMemDC,
               bitScreenBMP);

            clsGDI.BitBlt(iMemDC, 0, 0, ScreenSize.x, ScreenSize.y,
               iDC, 0, 0, clsGDI.SRCCOPY);
            clsGDI.SelectObject(iMemDC, hPrev);
            clsGDI.DeleteDC(iMemDC);

            clsUSER.ReleaseDC(clsUSER.GetDesktopWindow(), iDC);

            Bitmap bmpRes = Image.FromHbitmap(bitScreenBMP);

            clsGDI.DeleteObject(bitScreenBMP);

            GC.Collect();

            return bmpRes;

         }

         return null;

      }
  • VB.NET
  Public Shared Function GetDesktopImage() As Bitmap

      Dim ScreenSize As SIZE

      Dim bitScreenBMP As IntPtr
      Dim iDC As IntPtr = clsUSER.GetDC(clsUSER.GetDesktopWindow())
      Dim iMemDC As IntPtr = clsGDI.CreateCompatibleDC(iDC)

      ScreenSize.x = clsUSER.GetSystemMetrics(clsUSER.SM_CXSCREEN)
      ScreenSize.y = clsUSER.GetSystemMetrics(clsUSER.SM_CYSCREEN)

      bitScreenBMP = clsGDI.CreateCompatibleBitmap(iDC, _
         ScreenSize.x, ScreenSize.y)

      If bitScreenBMP <> IntPtr.Zero Then

         Dim hPrev As IntPtr = CType(clsGDI.SelectObject(iMemDC, _
            bitScreenBMP), IntPtr)

         clsGDI.BitBlt(iMemDC, 0, 0, ScreenSize.x, ScreenSize.y, _
            iDC, 0, 0, clsGDI.SRCCOPY)
         clsGDI.SelectObject(iMemDC, hPrev)
         clsGDI.DeleteDC(iMemDC)

         clsUSER.ReleaseDC(clsUSER.GetDesktopWindow(), iDC)

         Dim bmpRes As Bitmap = Image.FromHbitmap(bitScreenBMP)

         clsGDI.DeleteObject(bitScreenBMP)

         GC.Collect()

         Return bmpRes

      End If

      Return Nothing

   End Function

GetDesktopImage函数的优点是它包含了clsGDI和clsUSER类的API。它首先创建一个要绘制的设备上下文。 在clsUSER类中,调用GetSystemMetrics来计算屏幕的尺寸。如果屏幕分辨率不为零,则该函数使用clsGDIclass中的BitBlt来复制屏幕的内容。在成功复制后,内存中的图形对象将会被删除。

在您的窗体中,为Timer控件的Tick事件添加以下代码:
- C#

     private void tmrTime_Tick(object sender, EventArgs e)
      {

         picScreen.Image = clsCapScreen.GetDesktopImage();

      }
  • VB.NET
   Private Sub tmrTime_Tick(sender As Object, e As EventArgs) _
         Handles tmrTime.Tick

      picScreen.Image = clsCapScreen.GetDesktopImage()

   End Sub

在运行时,您的程序结果将如图3所示。请记住:此代码将每秒运行一次,或者根据您设置Timer的间隔来循环运行,因此如果您的运行结果看起来让人有点迷惑,请不要惊慌。
这里写图片描述
图3:运行结果

结论

使用Windows API的Graphic功能非常简单,只要您确保你能处理好正在使用的内存资源即可。您需要经常清理未使用的内存。使用计时器来复制屏幕并不是必须的;这只是一个例子。现在您知道如何在PictureBox中显示当前屏幕的内容了。希望在以后的文章中,我也可以给你介绍鼠标及其事件。

关于作者

Hannes DuPreez
Hannes du Preez是2008年-2017年度的Visual Basic的MVP。他热爱技术并喜欢Visual Basic和C#语言。他喜欢撰写文章,并证明了Visual Basic比大多数人认为的更加强大。欢迎与其交流:ojdupreez1978@gmail.com。

相关文章

Microsoft Officially Announces the Release of Visual Studio 2013
In-app File Management Changes in Windows 8.1 and Visual Studio 2013
Microsoft Visual Studio 2013 to Deliver 1080p Display Support

栏目导航
上一篇:技术文章翻译(四) – 现代C++避免产生BUG的相关技术
下一篇:技术文章翻译(六) – 基于GTK+创建一个GUI程序

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值