获取特定应用程序的屏幕截图

时间:2021-09-30 19:17:50

I know I can get the screenshot of the entire screen using Graphics.CopyFromScreen(). However, what if I just want the screenshot of a specific application?

我知道我可以使用Graphics.CopyFromScreen()获取整个屏幕的屏幕截图。但是,如果我只想要特定应用程序的屏幕截图怎么办?

4 个解决方案

#1


Here's some code to get you started:

这里有一些代码可以帮助您入门:

public void CaptureApplication(string procName)
{
    var proc = Process.GetProcessesByName(procName)[0];
    var rect = new User32.Rect();
    User32.GetWindowRect(proc.MainWindowHandle, ref rect);

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    Graphics graphics = Graphics.FromImage(bmp);
    graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);

    bmp.Save("c:\\tmp\\test.png", ImageFormat.Png);
}

private class User32
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Rect
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
}

It works, but needs improvement:

它有效,但需要改进:

  • You may want to use a different mechanism to get the process handle (or at least do some defensive coding)
  • 您可能希望使用不同的机制来获取进程句柄(或者至少进行一些防御性编码)

  • If your target window isn't in the foreground, you'll end up with a screenshot that's the right size/position, but will just be filled with whatever is in the foreground (you probably want to pull the given window into the foreground first)
  • 如果您的目标窗口不在前景中,您将得到一个正确大小/位置的屏幕截图,但只会填充前景中的任何内容(您可能希望先将给定窗口拉到前台) )

  • You probably want to do something other than just save the bmp to a temp directory
  • 除了将bmp保存到临时目录之外,您可能还想做其他事情

#2


The PrintWindow win32 api will capture a window bitmap even if the window is covered by other windows or if it is off screen:

PrintWindow win32 api将捕获一个窗口位图,即使该窗口被其他窗口覆盖或者它是否在屏幕外:

[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);

public static Bitmap PrintWindow(IntPtr hwnd)    
{       
    RECT rc;        
    GetWindowRect(hwnd, out rc);

    Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);        
    Graphics gfxBmp = Graphics.FromImage(bmp);        
    IntPtr hdcBitmap = gfxBmp.GetHdc();        

    PrintWindow(hwnd, hdcBitmap, 0);  

    gfxBmp.ReleaseHdc(hdcBitmap);               
    gfxBmp.Dispose(); 

    return bmp;   
}

The reference to RECT above can be resolved with the following class:

上面对RECT的引用可以通过以下类来解决:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    private int _Left;
    private int _Top;
    private int _Right;
    private int _Bottom;

    public RECT(RECT Rectangle) : this(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom)
    {
    }
    public RECT(int Left, int Top, int Right, int Bottom)
    {
        _Left = Left;
        _Top = Top;
        _Right = Right;
        _Bottom = Bottom;
    }

    public int X {
        get { return _Left; }
        set { _Left = value; }
    }
    public int Y {
        get { return _Top; }
        set { _Top = value; }
    }
    public int Left {
        get { return _Left; }
        set { _Left = value; }
    }
    public int Top {
        get { return _Top; }
        set { _Top = value; }
    }
    public int Right {
        get { return _Right; }
        set { _Right = value; }
    }
    public int Bottom {
        get { return _Bottom; }
        set { _Bottom = value; }
    }
    public int Height {
        get { return _Bottom - _Top; }
        set { _Bottom = value + _Top; }
    }
    public int Width {
        get { return _Right - _Left; }
        set { _Right = value + _Left; }
    }
    public Point Location {
        get { return new Point(Left, Top); }
        set {
            _Left = value.X;
            _Top = value.Y;
        }
    }
    public Size Size {
        get { return new Size(Width, Height); }
        set {
            _Right = value.Width + _Left;
            _Bottom = value.Height + _Top;
        }
    }

    public static implicit operator Rectangle(RECT Rectangle)
    {
        return new Rectangle(Rectangle.Left, Rectangle.Top, Rectangle.Width, Rectangle.Height);
    }
    public static implicit operator RECT(Rectangle Rectangle)
    {
        return new RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom);
    }
    public static bool operator ==(RECT Rectangle1, RECT Rectangle2)
    {
        return Rectangle1.Equals(Rectangle2);
    }
    public static bool operator !=(RECT Rectangle1, RECT Rectangle2)
    {
        return !Rectangle1.Equals(Rectangle2);
    }

    public override string ToString()
    {
        return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}";
    }

    public override int GetHashCode()
    {
        return ToString().GetHashCode();
    }

    public bool Equals(RECT Rectangle)
    {
        return Rectangle.Left == _Left && Rectangle.Top == _Top && Rectangle.Right == _Right && Rectangle.Bottom == _Bottom;
    }

    public override bool Equals(object Object)
    {
        if (Object is RECT) {
            return Equals((RECT)Object);
        } else if (Object is Rectangle) {
            return Equals(new RECT((Rectangle)Object));
        }

        return false;
    }
}

#3


Based on Alconja's answer, I made a few improvements:

根据Alconja的回答,我做了一些改进:

[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);

private const int SW_RESTORE = 9;

[DllImport("user32.dll")]
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);

[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

public Bitmap CaptureApplication(string procName)
{
    Process proc;

    // Cater for cases when the process can't be located.
    try
    {
        proc = Process.GetProcessesByName(procName)[0];
    }
    catch (IndexOutOfRangeException e)
    {
        return null;
    }

    // You need to focus on the application
    SetForegroundWindow(proc.MainWindowHandle);
    ShowWindow(proc.MainWindowHandle, SW_RESTORE);

    // You need some amount of delay, but 1 second may be overkill
    Thread.Sleep(1000);

    Rect rect = new Rect();
    IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect);

    // sometimes it gives error.
    while (error == (IntPtr)0)
    {
        error = GetWindowRect(proc.MainWindowHandle, ref rect);
    }

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    Graphics.FromImage(bmp).CopyFromScreen(rect.left,
                                           rect.top,
                                           0,
                                           0,
                                           new Size(width, height),
                                           CopyPixelOperation.SourceCopy);

    return bmp;
}

#4


You could look into P/Invoking the win32 way of doing this, an article to this effect... sort of.

您可以查看P /调用win32的方式来做到这一点,这篇文章就是这样的。

Basically, go through the trouble of setting up a DC to a bitmap and send WM_PRINT to the application window in question. Its pretty nasty, all told, but may work for you.

基本上,经历了将DC设置为位图并将WM_PRINT发送到相关应用程序窗口的麻烦。它说得非常讨厌,但可能适合你。

Functions you may need: SendMessage, GetDC, CreateCompatibleBitmp, and SelectObject.

您可能需要的函数:SendMessage,GetDC,CreateCompatibleBitmp和SelectObject。

I can't say I've ever done this before, but this is how I'd attack the problem. (Well, I'd probably do it in pure C but still; roughly the way I'd attack it).

我不能说我以前做过这个,但这就是我解决这个问题的方法。 (好吧,我可能在纯C中做到但仍然;大致就是我攻击它的方式)。

#1


Here's some code to get you started:

这里有一些代码可以帮助您入门:

public void CaptureApplication(string procName)
{
    var proc = Process.GetProcessesByName(procName)[0];
    var rect = new User32.Rect();
    User32.GetWindowRect(proc.MainWindowHandle, ref rect);

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    Graphics graphics = Graphics.FromImage(bmp);
    graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);

    bmp.Save("c:\\tmp\\test.png", ImageFormat.Png);
}

private class User32
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Rect
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
}

It works, but needs improvement:

它有效,但需要改进:

  • You may want to use a different mechanism to get the process handle (or at least do some defensive coding)
  • 您可能希望使用不同的机制来获取进程句柄(或者至少进行一些防御性编码)

  • If your target window isn't in the foreground, you'll end up with a screenshot that's the right size/position, but will just be filled with whatever is in the foreground (you probably want to pull the given window into the foreground first)
  • 如果您的目标窗口不在前景中,您将得到一个正确大小/位置的屏幕截图,但只会填充前景中的任何内容(您可能希望先将给定窗口拉到前台) )

  • You probably want to do something other than just save the bmp to a temp directory
  • 除了将bmp保存到临时目录之外,您可能还想做其他事情

#2


The PrintWindow win32 api will capture a window bitmap even if the window is covered by other windows or if it is off screen:

PrintWindow win32 api将捕获一个窗口位图,即使该窗口被其他窗口覆盖或者它是否在屏幕外:

[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);

public static Bitmap PrintWindow(IntPtr hwnd)    
{       
    RECT rc;        
    GetWindowRect(hwnd, out rc);

    Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);        
    Graphics gfxBmp = Graphics.FromImage(bmp);        
    IntPtr hdcBitmap = gfxBmp.GetHdc();        

    PrintWindow(hwnd, hdcBitmap, 0);  

    gfxBmp.ReleaseHdc(hdcBitmap);               
    gfxBmp.Dispose(); 

    return bmp;   
}

The reference to RECT above can be resolved with the following class:

上面对RECT的引用可以通过以下类来解决:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    private int _Left;
    private int _Top;
    private int _Right;
    private int _Bottom;

    public RECT(RECT Rectangle) : this(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom)
    {
    }
    public RECT(int Left, int Top, int Right, int Bottom)
    {
        _Left = Left;
        _Top = Top;
        _Right = Right;
        _Bottom = Bottom;
    }

    public int X {
        get { return _Left; }
        set { _Left = value; }
    }
    public int Y {
        get { return _Top; }
        set { _Top = value; }
    }
    public int Left {
        get { return _Left; }
        set { _Left = value; }
    }
    public int Top {
        get { return _Top; }
        set { _Top = value; }
    }
    public int Right {
        get { return _Right; }
        set { _Right = value; }
    }
    public int Bottom {
        get { return _Bottom; }
        set { _Bottom = value; }
    }
    public int Height {
        get { return _Bottom - _Top; }
        set { _Bottom = value + _Top; }
    }
    public int Width {
        get { return _Right - _Left; }
        set { _Right = value + _Left; }
    }
    public Point Location {
        get { return new Point(Left, Top); }
        set {
            _Left = value.X;
            _Top = value.Y;
        }
    }
    public Size Size {
        get { return new Size(Width, Height); }
        set {
            _Right = value.Width + _Left;
            _Bottom = value.Height + _Top;
        }
    }

    public static implicit operator Rectangle(RECT Rectangle)
    {
        return new Rectangle(Rectangle.Left, Rectangle.Top, Rectangle.Width, Rectangle.Height);
    }
    public static implicit operator RECT(Rectangle Rectangle)
    {
        return new RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom);
    }
    public static bool operator ==(RECT Rectangle1, RECT Rectangle2)
    {
        return Rectangle1.Equals(Rectangle2);
    }
    public static bool operator !=(RECT Rectangle1, RECT Rectangle2)
    {
        return !Rectangle1.Equals(Rectangle2);
    }

    public override string ToString()
    {
        return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}";
    }

    public override int GetHashCode()
    {
        return ToString().GetHashCode();
    }

    public bool Equals(RECT Rectangle)
    {
        return Rectangle.Left == _Left && Rectangle.Top == _Top && Rectangle.Right == _Right && Rectangle.Bottom == _Bottom;
    }

    public override bool Equals(object Object)
    {
        if (Object is RECT) {
            return Equals((RECT)Object);
        } else if (Object is Rectangle) {
            return Equals(new RECT((Rectangle)Object));
        }

        return false;
    }
}

#3


Based on Alconja's answer, I made a few improvements:

根据Alconja的回答,我做了一些改进:

[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);

private const int SW_RESTORE = 9;

[DllImport("user32.dll")]
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);

[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

public Bitmap CaptureApplication(string procName)
{
    Process proc;

    // Cater for cases when the process can't be located.
    try
    {
        proc = Process.GetProcessesByName(procName)[0];
    }
    catch (IndexOutOfRangeException e)
    {
        return null;
    }

    // You need to focus on the application
    SetForegroundWindow(proc.MainWindowHandle);
    ShowWindow(proc.MainWindowHandle, SW_RESTORE);

    // You need some amount of delay, but 1 second may be overkill
    Thread.Sleep(1000);

    Rect rect = new Rect();
    IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect);

    // sometimes it gives error.
    while (error == (IntPtr)0)
    {
        error = GetWindowRect(proc.MainWindowHandle, ref rect);
    }

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    Graphics.FromImage(bmp).CopyFromScreen(rect.left,
                                           rect.top,
                                           0,
                                           0,
                                           new Size(width, height),
                                           CopyPixelOperation.SourceCopy);

    return bmp;
}

#4


You could look into P/Invoking the win32 way of doing this, an article to this effect... sort of.

您可以查看P /调用win32的方式来做到这一点,这篇文章就是这样的。

Basically, go through the trouble of setting up a DC to a bitmap and send WM_PRINT to the application window in question. Its pretty nasty, all told, but may work for you.

基本上,经历了将DC设置为位图并将WM_PRINT发送到相关应用程序窗口的麻烦。它说得非常讨厌,但可能适合你。

Functions you may need: SendMessage, GetDC, CreateCompatibleBitmp, and SelectObject.

您可能需要的函数:SendMessage,GetDC,CreateCompatibleBitmp和SelectObject。

I can't say I've ever done this before, but this is how I'd attack the problem. (Well, I'd probably do it in pure C but still; roughly the way I'd attack it).

我不能说我以前做过这个,但这就是我解决这个问题的方法。 (好吧,我可能在纯C中做到但仍然;大致就是我攻击它的方式)。