【问题标题】:DPI aware and RectDPI 感知和 Rect
【发布时间】:2014-12-26 20:17:57
【问题描述】:

我遇到了一个小问题,我似乎无法找到答案。我有一个应用程序可以获取某些进程并获取它的窗口大小。唯一的问题是它占用了(用户看到的)实际屏幕尺寸的百分比。

我想制作应用程序的屏幕截图,但如果我使用窗口的 Rect,我会得到比实际更小的屏幕,因为分辨率为 125%。这意味着我输出的原始分辨率 (1280 * 800) 比我的屏幕分辨率 (1600 * 1000) 小,您可以理解这个小问题使我的程序不可靠。我的问题是如何解决这个问题?

我创建了一个清单,我将 DPIAware 设置为 true。我还在调试中禁用了 Visual Studio 托管。但这无济于事。我仍然得到相同的值和相同的问题。这是我实际截图的代码 sn-p:

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

这给了我1280 * 800的截图,不足以覆盖整个过程,就是1600 * 1000。一切都关闭了,因为屏幕坐标不正确。如果我将所有内容乘以 1,25 就可以了,但这不是解决方案,因为我不知道其他 PC 上的 DPI 设置是什么。

编辑 3:

我将发布包含 RECT 的完整代码。

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

【问题讨论】:

  • 它不是 dpiAware,直到你超过 125% 才会启动。你得到任何东西都相当奇怪,这个过程需要时间来启动并创建和绘制它的窗口。 ShowWindow() 不足以确保已完成。 Boilerplate 是使用 Process.WaitForInputIdle(),你可能不得不用 Thread.Sleep() 冷却你的脚跟,因为这个过程表现得很奇怪。
  • 我拥有的代码只是一个发布在这里的sn-p。奇怪的是,其他一切都很好,但截图是我无法得到这个奇怪错误的正确原因。我将尝试下面发布的解决方案,希望能解决问题。
  • 还想进一步评论。该过程已经在运行,如果我在代码中对值 (1600 * 1000) 进行硬编码,它就会像阳光一样工作。那么这意味着它可以动态完成吗?
  • RECT 实际上是什么,你如何创建它?你在哪里设置它的大小?
  • 见上文我添加了 RECT 中的代码,以便您对我所做的事情有更清晰的了解。

标签: c# bitmap screenshot rect dpi-aware


【解决方案1】:

您可以通过

获取当前的DPI设置

属性。

这样你应该能够正确计算它(这是我在我的一个项目中的做法)。

只需通过创建一个虚拟的Form 和一个Graphics 对象来获取这些(如果您的代码是在表单上下文之外执行的):

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


用法:

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

演示

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

【讨论】:

  • 我真的不知道如何在上面的片段中使用它。你能否详细说明如何在上面的代码 sn-p 中实现它?
  • 我更新了示例。你写你需要当前的 dpi 值。这个小助手函数将为您获取它们。
  • 谢谢我要试试这个!
  • 所以我试了一下,它给了我宽度和高度的值 96。这意味着我的 DPI 为 96。但这告诉我什么?这如何帮助我获得流程窗口的正确值?抱歉,我在问这些问题,但我有点卡在这里.. :(
  • 我可以给你换个小费吗,你对我的帮助很好,我只想谢谢你!
猜你喜欢
  • 2011-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-18
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
相关资源
最近更新 更多