Search code examples
c#bitmapscreenshotrectdpi-aware

DPI aware and Rect


I got a small problem which I can't seem to find the answer of. I have a application that gets certain processes and gets the window size of it. Only the problem is it takes a percentage of the actual screen size (which the user sees).

I want to make a screenshot of the application, but if I use the Rect of the window I get a smaller screen than it is, because the resolotion is 125%. This means my original resolution which gets outputted (1280 * 800) is smaller then my screen resolution (1600 * 1000) you can understand that this small hiccup makes my program unreliable. My question is how to fix this?

I've created a manifest which I set DPIAware to true. Also I disabled the Visual Studio hosting in the debug. But it doesn't help. I still get the same values and the same problem. Here is my code snippet for the actual screenshotting:

RECT Rect = new RECT();
System.Diagnostics.Process[] p = System.Diagnostics.Process.GetProcessesByName("Process");
ShowWindow(p[0].MainWindowHandle, 9);
SetForegroundWindow(p[0].MainWindowHandle);

if (GetWindowRect(p[0].MainWindowHandle, ref Rect))
{
    var bmp = new Bitmap(Rect.Width, Rect.Height);
    var graphics = Graphics.FromImage(bmp);
    graphics.CopyFromScreen(Rect.Left, Rect.Top, 0, 0, new Size(Rect.Width, Rect.Height), CopyPixelOperation.SourceCopy);
    bmp.Save(@"C:\Screenshots\temp1.png");
}

This gives me an screenshot of 1280 * 800, not enough to cover the whole process, which is 1600 * 1000. Everything is off because the screen coordinates are not right. If I multiply everything by 1,25 it will be alright but that is not a solution because I don't know what the DPI settings on other PC's are.

EDIT 3:

I'll post the full code with the RECT in it.

RECT Rect = new RECT();
System.Diagnostics.Process[] p = System.Diagnostics.Process.GetProcessesByName("LoLPatcherUx");
ShowWindow(p[0].MainWindowHandle, 9);
SetForegroundWindow(p[0].MainWindowHandle);

if (GetWindowRect(p[0].MainWindowHandle, ref Rect))
{
    int processH = Rect.Bottom - Rect.Top;
    int processW = Rect.Right - Rect.Left;

    float processWidth;
    float processHeight;                            

    SizeF dpi = GetCurrentDpi();
    // Calc the scale.
    SizeF scale = new SizeF()
    {
        Width = dpi.Width / 96f,
        Height = dpi.Height / 96f                                  
    };

    // Scale the rectangle.
    processWidth = Rect.Width * scale.Width;
    processHeight = Rect.Height * scale.Height;

    var bmp = new Bitmap(lolPatcherBreedte, lolPatcherHoogte);
    Graphics graphics = Graphics.FromImage(bmp);
    graphics.CopyFromScreen(Rect.Left, Rect.Top, 0, 0, new Size(processW, processH));

    bmp.Save(@"C:\Screenshots\temp1.png");
}

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

Solution

  • You can get the current DPI setting via the

    properties.

    This way you should be able to calculate it correctly (it's how I do it in one of my projects).

    Just get these by creating a dummy Form and a Graphics object from it (if your code is being executed outside of a Form's context):

    public static SizeF GetCurrentDpi()
    {
        using (Form form = new Form())
        using (Graphics g = form.CreateGraphics())
        {
            var result = new SizeF()
            {
                Width = g.DpiX,
                Height = g.DpiY
            };
            return result;
        }
    }
    

    Control.CreateGraphics Method


    Usage:

    if (GetWindowRect(p[0].MainWindowHandle, ref Rect))
    {
        var bmp = new Bitmap(Rect.Width, Rect.Height);
        Graphics graphics = Graphics.FromImage(bmp);
    
        // Use the helper function to get the current dpi.
        SizeF dpi = GetCurrentDpi();
    
        // Calc the scale.
        SizeF scale = new SizeF()
        {
            Width = dpi.Width / 96f,
            Height = dpi.Height / 96f
        };
    
        // Scale the rectangle.
        Rect.Width *= scale.Width;
        Rect.Height *= scale.Height;
    
        graphics.CopyFromScreen(Rect.Left, Rect.Top, 0, 0, new Size(Rect.Width, Rect.Height), CopyPixelOperation.SourceCopy);
        bmp.Save(@"C:\Screenshots\temp1.png");
    }
    

    Demo:

    class Program
    {
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect);
    
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
    
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);
    
        static void Main(string[] args)
        {
    
            RECT rect = new RECT();
            Process[] processes = System.Diagnostics.Process.GetProcessesByName("iexplore");
            Process iexplore = processes.First();
    
            ShowWindow(iexplore.MainWindowHandle, ShowWindowCommands.Restore);
            SetForegroundWindow(iexplore.MainWindowHandle);
            var result = GetWindowRect(iexplore.MainWindowHandle, ref rect);
    
            RectangleF rectF = new RectangleF()
            {
                Location = new PointF(rect.Left, rect.Top),
                Size = new SizeF(rect.Right - rect.Left + 1, rect.Bottom - rect.Top + 1)
            };            
    
            var bmp = new Bitmap((int)rectF.Width, (int)rectF.Height);
            Graphics graphics = Graphics.FromImage(bmp);
            graphics.CopyFromScreen((int)rectF.Left, (int)rectF.Top, 0, 0, new Size((int)rectF.Width, (int)rectF.Height), CopyPixelOperation.SourceCopy);
            bmp.Save(@"C:\temp\screenshot1.jpg", ImageFormat.Jpeg);          
        }      
    }
    
    enum ShowWindowCommands
    {
        Hide = 0,
        Normal = 1,
        ShowMinimized = 2,
        Maximize = 3,
        ShowMaximized = 3,
        ShowNoActivate = 4,
        Show = 5,
        Minimize = 6,
        ShowMinNoActive = 7,
        ShowNA = 8,
        Restore = 9,
        ShowDefault = 10,
        ForceMinimize = 11
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }