Home > OS >  Metafile size incorrect in console app, correct in Windows Forms
Metafile size incorrect in console app, correct in Windows Forms

Time:03-13

The below code is supposed to generate an Enhanced Windows Metafile (EMF) with a size 200mm by 100mm and a centered rectangle of 180mm by 80mm. When run from a Windows Forms application, it works. When run from a console application, the size (or frame) is incorrect: it's almost exactly double the width and height.

What is different between a console app and a Windows Form app that could be responsible for the different behavior? What change is needed to correct the EMF size/frame_

Both applications run:

  • .NET Core 3.1 (I tried .NET 6 and .NET Framework with the same result)
  • System.Drawing.Common 4.5.1 (I tried newer version with the same result)

I've also tried to use millimeter as the page and frame unit. It had the same effect. Additionally, the size was off by an additional 5%. In all cases I've inserted the EMF file into Microsoft Word and taken the size from there.

As I've experienced that Metafiles have a strange dependence on the used display, it might be relevant that I'm using a HiDPI display at a scale of 200%.

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

Update

If I set the scale for my display to 150%, the incorrect size is about 1.5x the desired size instead of 2x. So it follows the display scaling.

Furthermore, offScreenGraphics.DpiX is 96 for the console app, and 192 and 144 for the Windows Forms app with a display scale of 200% and 150%, respectively.

CodePudding user response:

Thanks to Hans Passant. His link was super helpful.

So even if EMF is a resolution-independent graphics file format, the resolution and DPI of your screen (likely the main screen) will influence the result. Basically, EMF uses the physical units of your screen. So for HiDPI screens, it depends on the screen scaling.

To fix it, the application needs to run in DPI aware mode. See below, for how to achieve it.

If run in the DPI virtualization mode instead, the EMF frame behaves differently from the EMF content and the result is incorrect. That's strange, likely a bug.

I've also noted that none of the results are in line with the EMF and EMF standards published by Microsoft. If they were, they wouldn't properly work in Microsoft Office. Now it nicely works in Microsoft Word.

DPI aware mode

Either call this SetProcessDPIAware() at the start of your application:

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

Or use an application manifest:

<?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>
  • Related