图元文件大小在控制台应用程序中不正确,在 Windows 表单中更正

Metafile size incorrect in console app, correct in Windows Forms

下面的代码应该生成一个增强的 Windows 图元文件 (EMF),大小为 200mm x 100mm,居中矩形为 180mm x 80mm。当来自 Windows Forms 应用程序的 运行 时,它可以工作。 运行 来自控制台应用程序时,大小(或框架)不正确:它几乎正好是宽度和高度的两倍。

控制台应用程序和可能导致不同行为的 Windows 表单应用程序有何不同?纠正 EMF 需要进行哪些更改 size/frame_

两个应用程序运行:

我也试过使用毫米作为页面和框架单位。它有同样的效果。此外,尺码还减少了 5%。在所有情况下,我都将 EMF 文件插入到 Microsoft Word 中并从那里获取大小。

根据我的经验,图元文件对所用显示器有一种奇怪的依赖性,这可能与我使用 200% 比例的 HiDPI 显示器有关。

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace MetafileDotNot
{
    internal class Program
    {
        static void Main()
        {
            using (Graphics offScreenGraphics = Graphics.FromHwndInternal(IntPtr.Zero))
            {
                float mmToPixel = offScreenGraphics.DpiX / 25.4f;
                using (Metafile metaFile = new Metafile(
                        "test.emf",
                        offScreenGraphics.GetHdc(),
                        new RectangleF(0, 0, 200 * mmToPixel, 100 * mmToPixel),
                        MetafileFrameUnit.Pixel,
                        EmfType.EmfPlusDual
                    ))
                using (Graphics graphics = Graphics.FromImage(metaFile))
                {
                    graphics.PageUnit = GraphicsUnit.Pixel;
                    graphics.FillRectangle(Brushes.DarkBlue, new RectangleF(10 * mmToPixel, 10 * mmToPixel, 180 * mmToPixel, 80 * mmToPixel));
                }
            }
        }
    }
}

更新

如果我将显示器的比例设置为 150%,则不正确的尺寸约为所需尺寸的 1.5 倍,而不是 2 倍。所以它遵循显示缩放。

此外,offScreenGraphics.DpiX 控制台应用程序为 96,Windows 表单应用程序为 192 和 144,显示比例分别为 200% 和 150%。

感谢 Hans Passant。他的 link 非常有帮助。

因此,即使 EMF 是 resolution-independent 图形文件格式,屏幕(可能是主屏幕)的分辨率和 DPI 也会影响结果。基本上,EMF 使用屏幕的物理单位。所以对于HiDPI屏幕,这取决于屏幕缩放。

要修复它,应用程序需要在 DPI 感知 模式下 运行。见下文,了解如何实现它。

如果 运行 改为 DPI 虚拟化 模式,则 EMF 帧的行为与 EMF 内容不同,结果不正确。这很奇怪,可能是一个错误。

我还注意到 none 结果符合 Microsoft 发布的 EMF 和 EMF+ 标准。如果是,它们将无法在 Microsoft Office 中正常工作。现在它在 Microsoft Word 中运行良好。

DPI 感知模式

在您的应用程序开始时调用此 SetProcessDPIAware()

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();

或使用应用程序清单:

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
    <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>true</dpiAware>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>