0

I followed Huw Collingbourne program in creating a screen capture program with C#. However, I noticed a couple of weird items, and whether I use his created program or my modified program does the same. Specifically I created a program that opens a window that allows you to capture the area. I think it has to do with sittings on my computer, but need to know how to anticipate and fix this if others are going to use my screen capture program! If my display for windows 10 is set to 100% I get a little more than the selected window and if I set display to 125% text then I get a lot of the selected area. Leaving at default size I should be 555, 484 in size. but I capture much larger.

public partial class Form1 : Form
{
    //https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowrect
    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    private static extern IntPtr GetDesktopWindow();
    [DllImport("user32.dll")]
    private static extern bool GetWindowRect(IntPtr hWnd, ref Rectangle lpRect);

    //ICON info
    //https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getcursorinfo
    [DllImport("user32.dll")]
    private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
    [DllImport("user32.dll")]
    private static extern bool GetCursorInfo(out CURSORINFO pci);


    public struct POINT
    {
        public Int32 x;
        public Int32 y;
    }

    public struct ICONINFO
    {
        public bool fIcon;
        public Int32 xHotspot;
        public Int32 yHotspot;
        public IntPtr hbmMask;
        public IntPtr hbmColor;
    }

    public struct CURSORINFO
    {
        public Int32 cbSize;
        public Int32 flags;
        public IntPtr hCursor;
        public Point ptScreenPos;
    }

    GrabRegionForm grabForm;

    public void GrabRect(Rectangle rect)
    {
        int rectWidth = rect.Width - rect.Left;
        int rectHeight = rect.Height - rect.Top;
        Bitmap bm = new Bitmap(rectWidth, rectHeight);
        Graphics g = Graphics.FromImage(bm);
        g.CopyFromScreen(rect.Left, rect.Top, 0, 0, new Size(rectWidth, rectHeight));
        DrawMousePointer(g, Cursor.Position.X - rect.Left, Cursor.Position.Y - rect.Top);
        this.pb_screengrab.Image = bm;
        Clipboard.SetImage(bm);
    }
}

public partial class GrabRegionForm : Form
{
    public Rectangle formRect;
    private Form1 mainForm;
    [DllImport("user32.dll")]
    public static extern bool ReleaseCapture();
    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
    private void buttonOK_Click(object sender, EventArgs e)
    {
        formRect = new Rectangle(this.Left, this.Top, this.Left + this.Width, this.Top + this.Height);
        this.Hide();
        mainForm.GrabRect(formRect);
        Close();
    }
}

ScreenGrab with Display at 100%

ScreenGrab with Display at 125%

Area showing capture window

Area Actually Captured

John B
  • 120
  • 1
  • 9
  • 1
    See the notes here: [Image is not drawn at the correct spot](https://stackoverflow.com/questions/51453564/image-is-not-drawn-at-the-correct-spot?answertab=active#tab-top) – Jimi Dec 18 '18 at 10:15
  • 1
    Also, if your application is not DPIAware, it's subject to virtualization, meaning that the System will scale it automatically. This implies that all screen related measures and DPI values are *virtualized*, too. If this is the case, see here: [How to configure an app to run correctly on a machine with a high DPI setting](https://stackoverflow.com/questions/13228185/how-to-configure-an-app-to-run-correctly-on-a-machine-with-a-high-dpi-setting-e?answertab=active#tab-top). Some notes I've written on the subject [here](https://stackoverflow.com/a/50276714/7444103). – Jimi Dec 18 '18 at 13:09
  • Thanks Jimi that worked perfectly!!! – John B Dec 20 '18 at 21:05
  • Correction. Thanks Jimi that worked perfectly for Region both screens, and screen capture Display 1. but unfortunately the 2nd display screen capture fails. – John B Dec 20 '18 at 21:24
  • private void GrabScreen() { Rectangle rect = Screen.AllScreens[Cb_selectedScreen.SelectedIndex].Bounds; Bitmap bm = new Bitmap(rect.Width, rect.Height); Graphics g = Graphics.FromImage(bm); g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size); DrawMousePointer(g, Cursor.Position.X, Cursor.Position.Y); this.pb_screengrab.Image = bm; Clipboard.SetImage(bm); MakeTopMost(); ToPlayOrNot(); – John B Dec 20 '18 at 21:25
  • 1
    Read these notes: [Using SetWindowPos with multiple monitors](https://stackoverflow.com/questions/53012896/using-setwindowpos-with-multiple-monitors?answertab=active#tab-top) and re-read the notes in the first link. You need to set the Bitmap DPI according to the source DPI. Thus, you need the screen DPI and use that information to setup your Bitmap DPI. Otherwise, the default 96 dpi will be used. In Windows 10, the default (main) screen DPI, – Jimi Dec 20 '18 at 21:43
  • Also, your thanks were for something that *worked perfectly*. But you didn't say what worked. You need a DPIAware application **and** setting up the Bitmap correctly. – Jimi Dec 20 '18 at 21:54
  • I really appreciate your help. I will check those out. the first link "Image is not drawn at the correct spot " is a bit confusing as it is talking of an email from a file. While mine is a screen shot. Trying to figure out how to convert my capture to what you had I was using Bitmap bm = new Bitmap(rect.Width, rect.Height); and then g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size);. Sorry I am still a novice at this stuff and graphics it appears is a very weak arae. – John B Dec 20 '18 at 21:58
  • There are definitely no emails mentioned in the first link :) – Jimi Dec 20 '18 at 21:59
  • When I grab the screen bounds it is grabing 2400x1350, which is odd because my screen is 1920x1080 without the DPI setting changes. Using the Screen.AllScreens appears to be getting the wrong boundaries. I even attempted to feed that into the Screen.FromRectangle, and that was a fail because it had the wrong numbers to begin with. Basically with the primary screen it gives the grey area to the right and bottom, and on the 2nd screen to the left it gives grey area to the left and bottom. – John B Dec 20 '18 at 22:21
  • woohoo!!! It is now fixed! I had to sign out and back into windows with the default DPI. From what I can tell is VS2017 was carrying the larger selection over!!! THANK YOU JIMI for you outstanding help!!!!!! – John B Dec 20 '18 at 22:29

1 Answers1

0

IF using earlier than 4.7 and not windows 10 follow Jimi's examples and ensure you sign out and back into windows.

From Jimi https://stackoverflow.com/users/7444103/jimi How to configure an app to run correctly on a machine with a high DPI setting How to configure an app to run correctly on a machine with a high DPI setting (e.g. 150%)?

From Jimi https://stackoverflow.com/users/7444103/jimi Using SetWindowPos with multiple monitors Using SetWindowPos with multiple monitors

If I target my app for windows 10 Only it is incredibly simple now. Microsoft made it even easier to change DPI settings with using 4.7 if using Windows 10.

https://learn.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms

Declare compatibility with Windows 10. Then add the following to the app.manifest file in the XML under the commend for Windows 10 compatibility.

supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"

Enable per-monitor DPI awareness in the app.config file. Windows Forms introduces a new element to support new features and customizations added starting with the .NET Framework 4.7. To take advantage of the new features that support high DPI, add the following to your application configuration file.

Go to the XML line for System.Windows.Forms.ApplicationConfigurationSection

add key="DpiAwareness" value="PerMonitorV2"
John B
  • 120
  • 1
  • 9