Search code examples
c#.netwinformsgraphicsscreenshot

How can I get rid of the Title Bar of a Window when taking a screenshot of it


So right now I am taking a screenshot of an application by doing this:

using (var bitmap = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height))
using (var graphics = Graphics.FromImage(bitmap))
{
    graphics.CopyFromScreen(new Point(Screen.PrimaryScreen.WorkingArea.Left, Screen.PrimaryScreen.WorkingArea.Top), new Point(0, 0), Screen.PrimaryScreen.WorkingArea.Size);
    bitmap.Save(@"C:\Users\Neoray\Desktop\Test.png", ImageFormat.Bmp);
}

This works fine enough but the problem is that I need to get rid of the Title Bar that is in there (doesn't matter if its afterwards by altering the image or when taking the screenshot itself).

Hope someone can help me with this.


Solution

  • As noted in the comments, this method will return the ClientArea of a Process Window, excluding the Caption (Title Bar) only when the Window actually has a Caption. Not all applications do (in WinForms sense).
    Also, no guarantee whatsoever that it can work with a maximized Window which is rendered using DirectX APIs.
    In this case, it would be necessary to implement a completely different method.
    Anyway, it should work correctly with the sample image you're showing.


    The overloaded main method accepts both a Process Name and Process ID.

    Parameters:

    string processName or int processId:
    Specify the process friendly Name (the one shown in Task Manager, it's not case sensitive) or the Process Id (also available in Task Manager).
    It can be easily modified to also accept the Window Title.

    bool clientAreaOnly:
    Switch. Lets you specify whether you want a screenshot of the ClientArea section only or the whole Windows bounds.

    float dpi:
    You can define the Dpi resolution of the final Bitmap.

    Sample usage:

    • Takes a screenshot of the Main Window of a process with Name [Some Process Name]

    • Instructs to only consider the ClientArea (the Window Caption and Borders will be excluded)

    • Specifies that the Bitmap resolution should be 120Dpi

    • Saves a Bitmap in PNG format, named ScreenShot.png, in [Some Path]

      using (var bitmap = ProcessWindowScreenshot("[Some Process Name]", true, 120)) {
          bitmap.Save(@"[Some Path]\ScreenShot.png", ImageFormat.Png);
      }
      

    Sample procedure:

    using System.Diagnostics;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Linq;
    using System.Runtime.InteropServices;
    
    public Bitmap ProcessWindowScreenshot(string processName, bool clientAreaOnly, float dpi)  {
        using (var process = Process.GetProcessesByName(processName)
                                    .FirstOrDefault(p => p.MainWindowHandle != IntPtr.Zero))  {
            if (process == null) return null;
            return GetProcessWindowScreenshot(GetProcessWindowRect(process.Id, clientAreaOnly), dpi);
        }
    }
    
    public Bitmap ProcessWindowScreenshot(int processId, bool clientAreaOnly, float dpi)  {
        return GetProcessWindowScreenshot(GetProcessWindowRect(processId, clientAreaOnly), dpi);
    }
    
    private Bitmap GetProcessWindowScreenshot(Rectangle window, float dpi)
    {
        var bitmap = new Bitmap(window.Width, window.Height, PixelFormat.Format32bppArgb);
        bitmap.SetResolution(dpi, dpi);
        using (var g = Graphics.FromImage(bitmap))
        {
            g.InterpolationMode = InterpolationMode.High;
            g.CopyFromScreen(window.Location, Point.Empty, window.Size, CopyPixelOperation.SourceCopy);
            return bitmap;
        };
    }
    
    private Rectangle GetProcessWindowRect(int processId, bool isClientArea)
    {
        IntPtr hWnd = Process.GetProcessById(processId).MainWindowHandle;
        if (isClientArea) {
            return GetWindowClientRectangle(hWnd);
        }
        else {
            return GetWindowRectangle(hWnd);
        }
    }
    

    Windows API declarations and helpers methods:

    [UIPermission(SecurityAction.Demand, Action = SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)]
    public static Rectangle GetWindowRectangle(IntPtr handle)  {
        NativeMethods.GetWindowRect(handle, out RECT rect);
        return rect.ToRectangle();
    }
    
    [UIPermission(SecurityAction.Demand, Action = SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)]
    public static Rectangle GetWindowClientRectangle(IntPtr handle)  {
        NativeMethods.GetClientRect(handle, out RECT rect);
        NativeMethods.ClientToScreen(handle, out POINT point);
        return rect.ToRectangleOffset(point);
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    
        public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
        public Rectangle ToRectangleOffset(POINT p) => Rectangle.FromLTRB(p.x, p.y, Right + p.x, Bottom + p.y);
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int x;
        public int y;
    }
    
    internal static class NativeMethods
    {
        [DllImport("User32.dll", SetLastError = true)]
        internal static extern bool ClientToScreen(IntPtr hWnd, out POINT point);
    
        [DllImport("User32.dll", SetLastError = true)]
        internal static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
    
        [DllImport("User32.dll", SetLastError = true)]
        internal static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
    }