1

I am trying to take screenshots of every screen on my windows 10 machine. My system has 2 monitors which are of multiple DPIs.

  • My primary monitor has "scale and layout" set to 200%.
  • My secondary has "scale and layout" set to 100%. enter image description here

I am trying to use C# to take a screenshot of every single monitor. However my scaled monitor is reporting the wrong coordinates: Everything is halved. As a result, the primary monitor's screenshot is only 1/4th of the screen area (width/2,height/2).

Here is my code:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;

namespace CsSourceClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Screen[] screens;
            screens = Screen.AllScreens;


            int i = 0;

            foreach (var screen in System.Windows.Forms.Screen.AllScreens)
            {
                var savePath = "monitor" + i+".jpg";
                var x = screen.Bounds.Location.X;
                var y = screen.Bounds.Location.Y;
                var w = screen.Bounds.Width;
                var h = screen.Bounds.Height;
                Console.Out.WriteLine("Monitor: " + i + " extents:" + x + "," + y + " " + w + "," + h);
                using (Bitmap bmp = new Bitmap(w, h))
                {
                    using (Graphics g = Graphics.FromImage(bmp))
                    {
                        g.CopyFromScreen(x, y, 0, 0, bmp.Size);
                    }

                    bmp.Save(savePath, ImageFormat.Jpeg);
                    Console.Out.WriteLine("saved: " + savePath);
                }
                i++;
            }



        }
    }
}

PS: I get different values based on what I set "dpiaware" in the manifest file: https://social.msdn.microsoft.com/Forums/en-US/054c1d49-9f24-4617-aa83-9bc4f1bb4a5d/use-appmanifest-file-to-make-winforms-application-dpi-aware?forum=vbgeneral

If I don't set dpiaware, then the primary monitor's bounds get halved (cropping out 3/4ths of the screen). If I set dpiaware to true, then the secondary monitor's bounds get doubled (leaving big black areas in the screenshot).

eshalev
  • 3,033
  • 4
  • 34
  • 48
  • If run your code in console application (on .NET 4.7) I get expected results (correct screenshots of both monitors). – Evk Oct 30 '17 at 13:22
  • Thanks @Evk, Which monitor on your setup has scaled DPI? The primary or the secondary? I think it might matter that the primary is the scaled one. If it is the primary that is scaled, are you using a manifest with "", or just the the defaults? – eshalev Oct 30 '17 at 13:33
  • I use defaults (not touched manifest), my primary is scaled to 150%, secondary (smaller) is at 100% (note that app is console application, not winforms, if that matters). – Evk Oct 30 '17 at 13:35
  • It is truly mystifying to me why somebody would ask this question, know that the manifest matters, but yet think it is good idea to not show the manifest he uses. It is wrong. – Hans Passant Oct 30 '17 at 13:37
  • @Evk My app is also a console app. I tried scaling my bigger primary to 150% (instead of 200%), and got the same bug. One more question: My primary is on the left (x=0), and my secondary is on the right (x=2880). Is your setup the same? – eshalev Oct 30 '17 at 14:03
  • @Hans Passant I specified that I tried both manifest "" options: Getting opposite (but bad) results with both settings. With dpiaware=false, my primary monitor bounds are too small. With dpiaware=true my secondary monitor bounds are too large. I also tried to run the console window on different monitors (that did not have any effect). – eshalev Oct 30 '17 at 14:04
  • Nobody can tell from the question which "both" out of the 3 possible values you tried. Not True/PM probably. – Hans Passant Oct 30 '17 at 14:12
  • @Hans Passant I am afraid I've lost you. I've tried true and false. (I've also tried not specifying a manifest, which I assume is an implicit false). What is the 3rd possible value? – eshalev Oct 30 '17 at 14:19
  • @eshalev true and false are not only possible values for this element: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374191(v=vs.85).aspx. It can also contain "true/pm" or "per monitor". – Evk Oct 30 '17 at 14:24
  • Well that was @HansPassant suggestion, not mine. Still interesting why it works "out of the box" for me, maybe .NET 4.7 made this a default behavior? – Evk Oct 30 '17 at 14:38

2 Answers2

4

As mentioned in the comments, the solution is to set the app.manifest dpiAware tag to "true/pm". The effect is different than just setting it to true.

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
    </windowsSettings>
  </application>
eshalev
  • 3,033
  • 4
  • 34
  • 48
0

Yes, adding the manifest is the way to go. I tried all the solutions, and ended up adding a app.manifest to my project with below content:

<?xml version="1.0" encoding="utf-8"?>

<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">

  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
    </windowsSettings>
  </application>

</assembly>

Finally no need to import any ddl or what so ever.