โปรแกรมจับภาพหน้าจอจะคัดลอกมากกว่าพื้นที่ที่เลือก

ฉันติดตามโปรแกรม Huw Collingbourne ในการสร้างโปรแกรมจับภาพหน้าจอด้วย C# อย่างไรก็ตาม ฉันสังเกตเห็นสิ่งแปลก ๆ สองสามอย่าง และไม่ว่าฉันจะใช้โปรแกรมที่เขาสร้างขึ้นหรือโปรแกรมที่แก้ไขของฉันก็ยังเหมือนเดิม โดยเฉพาะฉันสร้างโปรแกรมที่เปิดหน้าต่างที่ให้คุณจับภาพพื้นที่ได้ ฉันคิดว่ามันเกี่ยวข้องกับการนั่งอยู่บนคอมพิวเตอร์ของฉัน แต่จำเป็นต้องรู้วิธีคาดการณ์และแก้ไขปัญหานี้หากผู้อื่นจะใช้โปรแกรมจับภาพหน้าจอของฉัน! หากจอแสดงผลของฉันสำหรับ Windows 10 ตั้งไว้ที่ 100% ฉันจะได้ค่ามากกว่าหน้าต่างที่เลือกเล็กน้อย และหากฉันตั้งค่าการแสดงผลเป็นข้อความ 125% ฉันจะได้พื้นที่ที่เลือกจำนวนมาก เหลือขนาดเริ่มต้นไว้คือขนาด 555, 484 แต่ฉันจับภาพได้ใหญ่กว่ามาก

public partial class Form1 : Form
{
    //https://docs.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://docs.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 พร้อมจอแสดงผลที่ 100%

ScreenGrab พร้อมจอแสดงผลที่ 125%

พื้นที่ที่แสดงหน้าต่างการจับภาพ

พื้นที่ที่ถูกจับจริง


person John B    schedule 18.12.2018    source แหล่งที่มา
comment
ดูหมายเหตุที่นี่: รูปภาพไม่ใช่ วาดที่จุดที่ถูกต้อง   -  person Jimi    schedule 18.12.2018
comment
นอกจากนี้ หากแอปพลิเคชันของคุณไม่ใช่ DPIAware ก็จะต้องมีการจำลองเสมือน ซึ่งหมายความว่าระบบจะปรับขนาดโดยอัตโนมัติ นี่หมายความว่าการวัดที่เกี่ยวข้องกับหน้าจอทั้งหมดและค่า DPI นั้นเป็น การจำลองเสมือน เช่นกัน หากเป็นกรณีนี้ โปรดดูที่นี่: วิธีกำหนดค่าแอปให้ทำงานอย่างถูกต้องบนเครื่องที่มีการตั้งค่า DPI สูง หมายเหตุบางส่วนที่ฉันเขียนในหัวข้อที่นี่   -  person Jimi    schedule 18.12.2018
comment
ขอบคุณ Jimi ที่ทำงานได้อย่างสมบูรณ์แบบ!!!   -  person John B    schedule 21.12.2018
comment
การแก้ไข ขอบคุณ Jimi ที่ทำงานได้อย่างสมบูรณ์แบบสำหรับภูมิภาคทั้งหน้าจอและการจับภาพหน้าจอจอแสดงผล 1 แต่น่าเสียดายที่การจับภาพหน้าจอจอแสดงผลที่ 2 ล้มเหลว   -  person John B    schedule 21.12.2018
comment
GrabScreen โมฆะส่วนตัว () { สี่เหลี่ยมผืนผ้าสี่เหลี่ยมผืนผ้า = Screen.AllScreens [Cb_selectedScreen.SelectedIndex]. ขอบเขต; บิตแมป bm = บิตแมปใหม่ (rect.Width, rect.Height); กราฟิก 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; คลิปบอร์ด.SetImage(bm); MakeTopMost(); ToPlayOrNot();   -  person John B    schedule 21.12.2018
comment
อ่านหมายเหตุเหล่านี้: การใช้ SetWindowPos กับหลายจอภาพ และ อ่านบันทึกย่อในลิงค์แรกอีกครั้ง คุณต้องตั้งค่าบิตแมป DPI ตาม DPI ของแหล่งที่มา ดังนั้น คุณจำเป็นต้องมี DPI ของหน้าจอ และใช้ข้อมูลนั้นเพื่อตั้งค่า Bitmap DPI ของคุณ มิฉะนั้น จะใช้ค่าเริ่มต้น 96 dpi ใน Windows 10 DPI ของหน้าจอเริ่มต้น (หลัก)   -  person Jimi    schedule 21.12.2018
comment
นอกจากนี้ ขอขอบคุณสำหรับบางสิ่งที่ทำงานได้อย่างสมบูรณ์แบบ แต่คุณไม่ได้บอกว่าอะไรได้ผล คุณต้องมีแอปพลิเคชัน DPIAware และในการตั้งค่าบิตแมปอย่างถูกต้อง   -  person Jimi    schedule 21.12.2018
comment
ผมขอขอบคุณความช่วยเหลือของคุณ. ฉันจะตรวจสอบสิ่งเหล่านั้น ลิงค์แรก ภาพไม่ได้วาดตรงจุดที่ถูกต้อง ทำให้เกิดความสับสนเล็กน้อยเนื่องจากกำลังพูดถึงอีเมลจากไฟล์ ในขณะที่ของฉันเป็นภาพหน้าจอ กำลังพยายามหาวิธีแปลงการจับภาพของฉันเป็นสิ่งที่คุณมี ฉันใช้ Bitmap bm = new Bitmap(rect.Width, rect.Height); จากนั้น g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size); ขออภัย ฉันยังเป็นมือใหม่ในเรื่องนี้ และกราฟิกดูเหมือนว่าจะอ่อนแอมาก   -  person John B    schedule 21.12.2018
comment
ไม่มีอีเมลที่กล่าวถึงในลิงก์แรกอย่างแน่นอน :)   -  person Jimi    schedule 21.12.2018
comment
เมื่อฉันจับขอบหน้าจอ มันจะมีขนาด 2400x1350 ซึ่งแปลกเพราะหน้าจอของฉันมีขนาด 1920x1080 โดยไม่มีการเปลี่ยนแปลงการตั้งค่า DPI การใช้หน้าจอ AllScreens ดูเหมือนจะได้รับขอบเขตที่ผิด ฉันยังพยายามที่จะป้อนสิ่งนั้นลงใน Screen.FromRectangle และนั่นล้มเหลวเพราะมันมีตัวเลขผิดตั้งแต่แรก โดยพื้นฐานแล้วหน้าจอหลักจะให้พื้นที่สีเทาทางด้านขวาและด้านล่าง และในหน้าจอที่ 2 ทางด้านซ้ายจะให้พื้นที่สีเทาทางด้านซ้ายและด้านล่าง   -  person John B    schedule 21.12.2018
comment
วู้ว!!! ตอนนี้ได้รับการแก้ไขแล้ว! ฉันต้องออกจากระบบและกลับเข้าสู่หน้าต่างด้วย DPI เริ่มต้น สิ่งที่ฉันบอกได้คือ VS2017 มีให้เลือกมากมาย!!! ขอบคุณ JIMI สำหรับความช่วยเหลือที่โดดเด่นของคุณ !!!!!!   -  person John B    schedule 21.12.2018


คำตอบ (1)


หากใช้เวอร์ชันเก่ากว่า 4.7 และไม่ใช่ Windows 10 ให้ทำตามตัวอย่างของ Jimi และให้แน่ใจว่าคุณออกจากระบบและกลับเข้าสู่ Windows

จาก Jimi https://stackoverflow.com/users/7444103/jimi วิธีกำหนดค่าแอปให้ทำงานอย่างถูกต้องบนเครื่อง ด้วยการตั้งค่า DPI สูง จะกำหนดค่าแอปให้ทำงานอย่างถูกต้องบนเครื่องที่มีการตั้งค่า DPI สูง (เช่น 150%) ได้อย่างไร

จาก Jimi https://stackoverflow.com/users/7444103/jimi การใช้ SetWindowPos กับจอภาพหลายจอ การใช้ SetWindowPos กับจอภาพหลายจอ

หากฉันกำหนดเป้าหมายแอปของฉันสำหรับ Windows 10 เท่านั้น ตอนนี้มันง่ายอย่างเหลือเชื่อ Microsoft ทำให้การเปลี่ยนการตั้งค่า DPI ง่ายยิ่งขึ้นด้วยการใช้ 4.7 หากใช้ Windows 10

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

ประกาศความเข้ากันได้กับ Windows 10 จากนั้นเพิ่มสิ่งต่อไปนี้ลงในไฟล์ app.manifest ใน XML ภายใต้คำชมสำหรับความเข้ากันได้ของ Windows 10

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

เปิดใช้งานการรับรู้ DPI ต่อจอภาพในไฟล์ app.config Windows Forms แนะนำองค์ประกอบใหม่เพื่อรองรับคุณลักษณะใหม่และการปรับแต่งที่เพิ่มโดยเริ่มจาก .NET Framework 4.7 หากต้องการใช้ประโยชน์จากคุณสมบัติใหม่ที่รองรับ DPI สูง ให้เพิ่มสิ่งต่อไปนี้ลงในไฟล์การกำหนดค่าแอปพลิเคชันของคุณ

ไปที่บรรทัด XML สำหรับ System.Windows.Forms.ApplicationConfigurationSection

add key="DpiAwareness" value="PerMonitorV2"
person John B    schedule 11.02.2019