Прямоугольник окна диалогового окна C#

Я подключаю диалоговые окна (CommonDialog и MessageBox), чтобы центрировать их в родительском Form после их отображения. Все работает как шарм в том, что касается MessageBox, но когда диалог наследуется от CommonDialog (OpenFileDialog, SaveFileDialog и т. д.), прямоугольник, который я получаю, всегда неправильный, и центрирование сбивается. Это код, который я использую для получения прямоугольника диалогового окна:

[DllImport("User32.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern Boolean GetWindowRect([In] IntPtr handle, [Out] out RECT rectangle);

[DllImport("Dwmapi.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)]
private static extern UInt32 DwmGetWindowAttribute([In] IntPtr handle, [In] DWMWINDOW_ATTRIBUTE attribute, [In, Out] IntPtr attributeValue, [In] UInt32 attributeSize);

[DllImport("Dwmapi.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)]
private static extern UInt32 DwmIsCompositionEnabled([Out, MarshalAs(UnmanagedType.Bool)] out Boolean enabled);

internal static Boolean AeroThemeEnabled
{
    get
    {
        Boolean result = false;

        if (Environment.OSVersion.Version.Major >= 6)
            DwmIsCompositionEnabled(out result);

        return result;
    }
}

internal static Boolean GetRectangle(IntPtr windowHandle, out Rectangle rectangle)
{
    RECT nativeRectangle;

    if (AeroThemeEnabled)
    {
        IntPtr attributeValue = IntPtr.Zero;
        UInt32 size = GetSize(typeof(RECT));

        try
        {
            attributeValue = Marshal.AllocCoTaskMem((Int32)size);

            UInt32 result = DwmGetWindowAttribute(windowHandle, DWMWINDOW_ATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, attributeValue, size);

            if (result == 0)
                nativeRectangle = Marshal.PtrToStructure<RECT>(attributeValue);
            else if (!GetWindowRect(windowHandle, out nativeRectangle))
            {
                rectangle = Rectangle.Empty;
                return false;
            }
        }
        finally
        {
            if (attributeValue != IntPtr.Zero)
                Marshal.FreeCoTaskMem(attributeValue);
        }
    }
    else if (!GetWindowRect(windowHandle, out nativeRectangle))
    {
        rectangle = Rectangle.Empty;
        return false;
    }

    Int32 x = nativeRectangle.Left;
    Int32 y = nativeRectangle.Top;
    Int32 width = nativeRectangle.Right - x;
    Int32 height = nativeRectangle.Bottom - y;

    rectangle = new Rectangle(x, y, width, height);

    return true;
}

В моей рабочей среде (Win7 с VS2013 и Framework 4.6.2), как я уже говорил ранее, метод GetRectangle возвращает неправильный прямоугольник Width и Height, когда диалоговое окно, которое я должен центрировать, наследуется от CommonDialog. Во время моих тестов я обнаружил, что и GetWindowRect, и DwmGetWindowAttribute возвращают одинаковые значения, поэтому основной обходной путь, который я реализовал, совершенно бесполезен. Я также обнаружил, что полученный прямоугольник всегда на 40 пикселей или около того шире реального (я измерил его, сделав снимок экрана).

Теперь, это серьезно сводит меня с ума. Единственные решения, которые я могу понять, это:

  1. Определите, принадлежит ли обработчик окна CommonDialog, и в этом случае вручную исправьте размер прямоугольника. Я не знаю как, так как GetClassName возвращает #32770 как для MessageBox, так и для CommonDialog. Я также сомневаюсь, что GetWindowLong может предоставить полезную информацию, которая позволит мне распознать тип диалогового окна.

  2. Вручную измените размер CommonDialog, как только он появится, чтобы я мог узнать его реальный размер.

Любое другое предложение более чем приветствуется.


person Tommaso Belluzzo    schedule 01.12.2016    source источник
comment
GetClientRect также возвращает неправильное значение, когда приходит время измерять CommonDialog. Например... Ширина GetWindowRect равна 664, ширина GetClientRect равна 648... но истинные значения должны быть соответственно 625 и 609.   -  person Tommaso Belluzzo    schedule 01.12.2016
comment
Небольшое обновление: мой подход (в основном такой: stackoverflow.com/a/3498791/796085) работает для каждого диалоговое окно, кроме OpenFileDialog и SaveFileDialog. Это связано с тем, что класс CommonDialog выполняет метод с именем MoveToScreenCenter при обработке WM_INITDIALOG. Таким образом, если вы добавите флаг SWP_SHOWWINDOW к SetWindowPos, вы сможете ясно увидеть идеально отцентрированное диалоговое окно, которое внезапно перемещается в центр экрана.   -  person Tommaso Belluzzo    schedule 02.12.2016