web-dev-qa-db-ja.com

GetWindowRectは、「非表示」の境界線を含むサイズを返します

画面上のウィンドウをグリッドスタイルで配置するアプリを開発しています。これをWindows 10で実行すると、ウィンドウ間に大きなギャップがあります。さらなる調査の結果、GetWindowRectは目に見えない境界線を含む予期しない値を返していることがわかりましたが、境界線が見える実際の値を返すことはできません。

1) このスレッド はこれが仕様によるものであり、winver = 6とリンクすることで「修正」できることを示唆しています。私の環境ではこれは許可されていませんが、影響なしでPE MajorOperatingSystemVersionおよびMajorSubsystemVersionを6に変更しようとしました

2)同じスレッドは、DwmGetWindowAttributeDWMWA_EXTENDED_FRAME_BOUNDSとともに使用してDWMから実際の座標を取得することも推奨していますが、これは機能しますが、ウィンドウ座標を取得するすべての場所を変更することを意味します。また、値を設定することもできません。ウィンドウサイズを設定できるように、プロセスを逆にする必要があります。

3) この質問 は、プロセスにおけるDPI認識の欠如を示唆しています。マニフェストにDPI認識フラグを設定することも、SetProcessDpiAwarenessを呼び出すことも、結果にはなりませんでした。

4)気まぐれに、Windows Vista、7、8、8.1、および10の互換性フラグも追加しようとしましたが、Windowsテーマは変更なしで表示されます。

Screenshot of a "fullscreen" window with gaps all round このウィンドウは0x0、1280x1024に移動され、おそらく画面全体に表示されます。座標を照会すると、同じ値が返されます。ただし、ウィンドウの実際の幅は14ピクセル狭くなっています。これは、古いバージョンのWindowsの境界を考慮するためです。

実際のウィンドウ座標で作業するようにWindowsを説得するにはどうすればよいですか?

24
Deanna

Windows 10の左、右、下には目に見えない薄い境界線があり、サイズ変更のためにマウスをつかむために使用されます。境界は次のようになります。7,0,7,7(左、上、右、下)

SetWindowPosを呼び出して、ウィンドウをこの座標に配置すると、次のようになります。
0, 0, 1280, 1024

ウィンドウはそれらの正確な座標を選択し、GetWindowRectは同じ座標を返します。しかし視覚的には、ウィンドウは次のように見えます。
7, 0, 1273, 1017

ウィンドウをだまして、代わりにここに移動するように指示できます。
-7, 0, 1287, 1031

そのために、Windows 10の境界線の太さを取得します。

RECT rect, frame;
GetWindowRect(hwnd, &rect);
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));

//rect should be `0, 0, 1280, 1024`
//frame should be `7, 0, 1273, 1017`

RECT border;
border.left = frame.left - rect.left;
border.top = frame.top - rect.top;
border.right = rect.right - frame.right;
border.bottom = rect.bottom - frame.bottom;

//border should be `7, 0, 7, 7`

次に、長方形を次のようにオフセットします。

rect.left -= border.left;
rect.top -= border.top;
rect.right += border.left + border.right;
rect.bottom += border.top + border.bottom;

//new rect should be `-7, 0, 1287, 1031`

より簡単な解決策がない限り!

24

どうすれば実際のウィンドウ座標で作業できるようにWindowsを説得できますか?

あなたはすでに実際の座標で作業しています。 Windows10は単に境界線を目から隠すことを選択しました。しかし、それでも彼らはまだそこにいます。ウィンドウの端を通過すると、カーソルがサイズ変更カーソルに変わります。つまり、実際にはまだウィンドウ上にあります。

Windowsが伝えていることと目を一致させたい場合は、Aero Liteテーマを使用して、これらの境界線を表示して、再び見えるようにすることができます。

http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/

2
mikew

AdjustWindowRectEx (またはWindows 10以降 AdjustWindowRectExForDpi )が役立つ場合があります。これらの関数は、クライアントの長方形をウィンドウサイズに変換します。

境界線を重ねたくないと思うので、これはおそらく完全な解決策ではありません-しかし、それは解決策の一部であり、この質問に出くわした他の人々にとって役立つかもしれません。

これが私のコードベースの簡単なスニペットです。これらを使用してウィンドウサイズを設定し、目的のクライアントサイズを取得しました。エラー処理マクロはご容赦ください。

DWORD window_style = (DWORD)GetWindowLong(global_context->window, GWL_STYLE);
CHECK_CODE(window_style);
CHECK(window_style != WS_OVERLAPPED); // Required by AdjustWindowRectEx

DWORD window_style_ex = (DWORD)GetWindowLong(global_context->window, GWL_EXSTYLE);
CHECK_CODE(window_style_ex);

// XXX: Use DPI aware version?
RECT requested_size = {};
requested_size.right = width;
requested_size.bottom = height;
AdjustWindowRectEx(
    &requested_size,
    window_style,
    false, // XXX: Why always false here?
    window_style_ex
);

UINT set_window_pos_flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
CHECK_CODE(SetWindowPos(
    global_context->window,
    nullptr,
    0,
    0,
    requested_size.right - requested_size.left,
    requested_size.bottom - requested_size.top,
    set_window_pos_flags
));

上記の使用例には、まだ2つのあいまいさが存在します。

  • 私のウィンドウにはメニューがありますが、メニューパラメータにfalseを渡さなければならないか、間違ったサイズを取得します。これがなぜなのかがわかれば、この回答を説明で更新します!
  • WindowsがDPI対応をどのように処理するかについてはまだ読んでいないので、DPI非対応の機能と関数のどちらを使用するかわからない
0
Mason