5

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;
    }
}
McMillan Cheng
  • 382
  • 1
  • 6
  • 20
Kuubs
  • 1,300
  • 1
  • 14
  • 42
  • It isn't dpiAware, that doesn't kick in until you go higher than 125%. It is rather odd you get anything at all, the process needs time to start and create and paint its window. ShowWindow() is not enough to ensure that was done. Boilerplate is to use Process.WaitForInputIdle(), you may have to cool your heels longer with Thread.Sleep() since this process behaves odd. – Hans Passant Dec 26 '14 at 20:54
  • The code I have is just a snippet posted here. Odd thing is, everything else works good, but the screenshotting is something I cannot get right cause of this weird bug. I'm gonna try out the solution posted below, I hope that will work things out. – Kuubs Dec 26 '14 at 21:12
  • Also want to comment further. The process is already running, and if I hardcode the values (1600 * 1000) in the code it all works like a sunshine. So that means it can be done dynamically right? – Kuubs Dec 26 '14 at 21:36
  • What is `RECT` actually and how do you crate it? Where to you set its size? – t3chb0t Dec 27 '14 at 18:39
  • See above I added the code from the RECT so you have a more clear understanding from what I am doing. – Kuubs Dec 27 '14 at 19:15

1 Answers1

5

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;
}
t3chb0t
  • 16,340
  • 13
  • 78
  • 118
  • I don't really know how I can use this in the snipped above.. Could you elaborate how this can be implemented in the code snippet above? – Kuubs Dec 26 '14 at 21:16
  • I updated the example. You write you need the current dpi values. This small helper function will get them for you. – t3chb0t Dec 26 '14 at 21:20
  • Thanks im gonna try this out! – Kuubs Dec 26 '14 at 21:28
  • So I tried it out, it gives me the values 96 for width and height. That means I have an DPI of 96. But what does this tell me? How can this help me getting the right values of the process window? Sorry I'm asking these questions but I'm kind of stuck here.. :( – Kuubs Dec 26 '14 at 21:34
  • This means your dpi settings are at 100%. For example on my machine I'm using for the test user a value of 125% dpi which equals to 120dpi --> 120 (x%) / 96 (100%) = 1.25 * 100 = 125%. Could you show your new modified code? – t3chb0t Dec 26 '14 at 21:49
  • I'm still not exactly sure how I would do this. Like you see above I create the graphics from a Bitmap I create from the Width and Height from the Rect. See above the new code with your code in it – Kuubs Dec 27 '14 at 18:23
  • I've added an example under _Usage_. – t3chb0t Dec 27 '14 at 18:29
  • This is wrong in your code because you're using the `Graphics` from the bitmap which gives you it's resolution. You need to use the helper function so that you get the resolution of the screen. – t3chb0t Dec 27 '14 at 18:30
  • However I'm not sure if you should create the bitmap after you scaled the Rect. If it doesn't work in the current version move the two lines after the rect scale. – t3chb0t Dec 27 '14 at 18:38
  • Thanks again, sstill not working though. I'm running some tests and it still gives the 1280*800 resolution, I'm implemented exactly as you did, altough I had to change some things to get it working (using a new Float because the Rect.Width and Height are ints instead of floats) – Kuubs Dec 27 '14 at 18:44
  • 1
    Can I changetip you by the way, you're helping me very well and I just want to thank you for it! – Kuubs Dec 27 '14 at 18:45
  • I could probably help more if you showed more code ;-) It's hard without knowing what the `RECT` is and how you exacly initialize it. – t3chb0t Dec 27 '14 at 18:48
  • Ok, I'm gonna post all the code... The scale gives a value of 1 by the way, that fully explains why the resolution is still the same.. Let me make the changes of the post... – Kuubs Dec 27 '14 at 18:49
  • Ahh, now I think I know... is RECT the struct from the Windows API? – t3chb0t Dec 27 '14 at 18:57
  • I've provided you with all the necessary for this problem! Yes I think it is from the Windows API! – Kuubs Dec 27 '14 at 18:57
  • I've added a demo for the console that I've just tested on both 100% and 125% dpi and in both cases the screenshot had the right size. I see the scaling is actually not necassary at all. Unfortunatelly I cannot tell now why your code isn't working. – t3chb0t Dec 27 '14 at 19:33
  • I think the problem is the process I'm trying to screenshot. I think its not possible on the way I'm trying to do so I'm gonna do something different. Thanks for the help, can I tip you if you don't mind? With changetip? – Kuubs Dec 27 '14 at 23:10