Don't know if this helps but MFC has an issue drawing a frame rect around a toolbar depending on the monitors involved and I obtained the following code that showed one way to fix it. But, we don't build MFC and we found setting per monitor aware allows an MFC app with a toolbar to work just fine. In any case, here is some code that simulates a resizing rectangle that worked on my desktop with 3 monitors including the 4k set to 200%. The magic is in VirtualToPhysical. I'd upload the project but don't see an easy way to do so. It is a single source file though.
// DrawToScreenSample.cpp : Defines the entry point for the application. //
#include "stdafx.h"
#include <windowsx.h> #include "DrawToScreenSample.h"
#define MAX_LOADSTRING 100
// Global Variables: HINSTANCE hInst; // current instance WCHAR szTitle[MAX_LOADSTRING]; // The title bar text WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
const WCHAR dummyMoveWindowClass[] = L"DummyMoveWindow"; using SetThreadDpiAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*)(_In_ DPI_AWARENESS_CONTEXT dpiContext); SetThreadDpiAwarenessContextFunc g_setThreadDpiAwarenessContext;
HWND g_windowToTrack; bool m_movingWindow; RECT g_lastRect; POINT g_moveStartPoint; POINT g_moveEndPoint; bool g_moveWindow; HWND g_dummyWindowToMove;
// Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); ATOM RegisterDummyMoveWindowClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void DrawToScreen(HWND selectedWindow); RECT VirtualToPhysical(const RECT &virtualRect);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize global strings LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_DRAWTOSCREENSAMPLE, szWindowClass, MAX_LOADSTRING);
if (!MyRegisterClass(hInstance)) { return FALSE; }
// Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; }
HMODULE user32Dll = GetModuleHandle(L"User32.dll"); if (!user32Dll) { return FALSE; }
g_setThreadDpiAwarenessContext = reinterpret_cast<SetThreadDpiAwarenessContextFunc>(GetProcAddress(user32Dll, "SetThreadDpiAwarenessContext")); if (!g_setThreadDpiAwarenessContext && GetLastError() != ERROR_PROC_NOT_FOUND) { return FALSE; }
if (g_setThreadDpiAwarenessContext && !RegisterDummyMoveWindowClass(hInstance)) { return FALSE; }
//g_setThreadDpiAwarenessContext = nullptr; HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DRAWTOSCREENSAMPLE));
MSG msg;
// Main message loop: while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
return static_cast<int>(msg.wParam); }
// // FUNCTION: MyRegisterClass() // // PURPOSE: Registers the window class. // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DRAWTOSCREENSAMPLE)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_DRAWTOSCREENSAMPLE); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex); }
ATOM RegisterDummyMoveWindowClass(HINSTANCE hInstance) { WNDCLASSEXW wcex = {}; wcex.hInstance = hInstance; wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpfnWndProc = []( _In_ HWND hWnd, _In_ UINT message, _In_ WPARAM wParam, _In_ LPARAM lParam) ->LRESULT { switch (message) { case WM_DPICHANGED: { RECT* const prcNewWindow = reinterpret_cast<RECT*>(lParam); SetWindowPos(hWnd, nullptr, prcNewWindow->left, prcNewWindow->top, prcNewWindow->right - prcNewWindow->left, prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); break; } }
return DefWindowProc(hWnd, message, wParam, lParam); };
wcex.lpszClassName = dummyMoveWindowClass; wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
return RegisterClassExW(&wcex); }
// // FUNCTION: InitInstance(HINSTANCE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) { return FALSE; }
ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
return TRUE; }
// // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_LBUTTONDOWN: SetCapture(hWnd); return DefWindowProc(hWnd, message, wParam, lParam); case WM_MOUSEMOVE: { DPI_AWARENESS_CONTEXT oldAwareness = nullptr; if (g_setThreadDpiAwarenessContext) { oldAwareness = g_setThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); }
if (GetCapture() == hWnd) { DWORD postion = GetMessagePos(); POINT pt; pt.x = GET_X_LPARAM(postion); pt.y = GET_Y_LPARAM(postion);
if (!m_movingWindow) { HWND window = WindowFromPoint(pt); HWND topLevelWindow = GetAncestor(window, GA_ROOT); if ((g_windowToTrack || topLevelWindow) && (g_windowToTrack != topLevelWindow)) { DrawToScreen(topLevelWindow); g_windowToTrack = topLevelWindow; }
// if the user wants to move the top-level window if (topLevelWindow && (wParam & MK_CONTROL)) { m_movingWindow = true; g_moveStartPoint = pt; g_moveWindow = false;
if (g_setThreadDpiAwarenessContext) { RECT physicalRect; if (GetWindowRect(g_windowToTrack, &physicalRect)) { g_dummyWindowToMove = CreateWindow(dummyMoveWindowClass, nullptr, WS_POPUP/* | WS_VISIBLE*/, physicalRect.left, physicalRect.top, physicalRect.right - physicalRect.left, physicalRect.bottom - physicalRect.top, nullptr, nullptr, hInst, nullptr); } } } } else { if (!(wParam & MK_CONTROL)) { ReleaseCapture(); } else { DrawToScreen(nullptr); } } }
if (g_setThreadDpiAwarenessContext) { g_setThreadDpiAwarenessContext(oldAwareness); } } return DefWindowProc(hWnd, message, wParam, lParam); case WM_LBUTTONUP: { if (m_movingWindow) { if (wParam & MK_CONTROL) { DPI_AWARENESS_CONTEXT oldAwareness = nullptr; if (g_setThreadDpiAwarenessContext) { oldAwareness = g_setThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); }
DrawToScreen(nullptr);
if (g_setThreadDpiAwarenessContext) { g_setThreadDpiAwarenessContext(oldAwareness); }
DWORD postion = GetMessagePos(); g_moveEndPoint.x = GET_X_LPARAM(postion); g_moveEndPoint.y = GET_Y_LPARAM(postion); g_moveWindow = true; } } ReleaseCapture(); } return DefWindowProc(hWnd, message, wParam, lParam); case WM_CAPTURECHANGED: if (m_movingWindow) { m_movingWindow = false; DrawToScreen(nullptr);
if (g_moveWindow) { RECT virtualRect;
if (g_dummyWindowToMove) { if (GetWindowRect(g_dummyWindowToMove, &virtualRect)) { SetWindowPos(g_windowToTrack, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE); } } else { if (GetWindowRect(g_windowToTrack, &virtualRect)) { OffsetRect(&virtualRect, (g_moveEndPoint.x - g_moveStartPoint.x), (g_moveEndPoint.y - g_moveStartPoint.y)); SetWindowPos(g_windowToTrack, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE); } } }
if (g_dummyWindowToMove) { DestroyWindow(g_dummyWindowToMove); g_dummyWindowToMove = nullptr; }
g_windowToTrack = nullptr; } else if (g_windowToTrack) { DrawToScreen(nullptr); g_windowToTrack = nullptr; } return DefWindowProc(hWnd, message, wParam, lParam); case WM_COMMAND: { int wmId = LOWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: Add any drawing code that uses hdc here... EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
void DrawToScreen(HWND selectedWindow) { HDC dc = GetDC(nullptr); if (dc) { if (SetROP2(dc, R2_XORPEN)) { WORD grayPattern[8]; for (int i = 0; i < 8; i++) { grayPattern = WORD(0x5555 << (i & 1)); }
HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); if (grayBitmap) { LOGBRUSH logBrush = {}; logBrush.lbStyle = BS_PATTERN; logBrush.lbHatch = reinterpret_cast<LONG_PTR>(grayBitmap); HPEN pen = ExtCreatePen(PS_GEOMETRIC, 0, &logBrush, 0, nullptr); if (pen) { HGDIOBJ oldPen = SelectObject(dc, pen); if (oldPen) { HGDIOBJ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); if (oldBrush) { if (g_windowToTrack) { Rectangle(dc, g_lastRect.left, g_lastRect.top, g_lastRect.right, g_lastRect.bottom); }
RECT calculatedPhysicalRect = {};
if (m_movingWindow) { DWORD postion = GetMessagePos(); POINT pt; pt.x = GET_X_LPARAM(postion); pt.y = GET_Y_LPARAM(postion);
RECT virtualRect; if (GetWindowRect(g_windowToTrack, &virtualRect)) { OffsetRect(&virtualRect, (pt.x - g_moveStartPoint.x), (pt.y - g_moveStartPoint.y)); if (g_dummyWindowToMove) { if (SetWindowPos(g_dummyWindowToMove, nullptr, virtualRect.left, virtualRect.top, virtualRect.right - virtualRect.left, virtualRect.bottom - virtualRect.top, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE)) { if (!GetWindowRect(g_dummyWindowToMove, &calculatedPhysicalRect)) { SetRectEmpty(&calculatedPhysicalRect); } } } else { calculatedPhysicalRect = VirtualToPhysical(virtualRect); } } } else if (selectedWindow) { if (g_setThreadDpiAwarenessContext) { if (!GetWindowRect(selectedWindow, &calculatedPhysicalRect)) { SetRectEmpty(&calculatedPhysicalRect); } } else { RECT virtualRect; if (GetWindowRect(selectedWindow, &virtualRect)) { calculatedPhysicalRect = VirtualToPhysical(virtualRect); } } }
if (!IsRectEmpty(&calculatedPhysicalRect)) { if (Rectangle(dc, calculatedPhysicalRect.left, calculatedPhysicalRect.top, calculatedPhysicalRect.right, calculatedPhysicalRect.bottom)) { g_lastRect = calculatedPhysicalRect; } }
SelectObject(dc, oldBrush); } SelectObject(dc, oldPen); } DeleteObject(pen); } DeleteObject(grayBitmap); } } ReleaseDC(nullptr, dc); } }
RECT VirtualToPhysical(const RECT &virtualRect) { RECT calculatedPhysicalRect = {};
POINT centerPoint; centerPoint.x = virtualRect.left + (virtualRect.right - virtualRect.left) / 2; centerPoint.y = virtualRect.top + (virtualRect.bottom - virtualRect.top) / 2; HMONITOR monitor = MonitorFromPoint(centerPoint, MONITOR_DEFAULTTONEAREST); if (monitor) { MONITORINFOEX monitorInfo = {}; monitorInfo.cbSize = sizeof(MONITORINFOEX); if (GetMonitorInfo(monitor, &monitorInfo)) { HDC monitorDC = CreateDC(nullptr, monitorInfo.szDevice, nullptr, nullptr); if (monitorDC) { int screenWidth = GetDeviceCaps(monitorDC, DESKTOPHORZRES); int virtualizedScreenWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
calculatedPhysicalRect.left = (virtualRect.left - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.left; calculatedPhysicalRect.top = (virtualRect.top - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.top; calculatedPhysicalRect.right = (virtualRect.right - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.left; calculatedPhysicalRect.bottom = (virtualRect.bottom - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.top;
DeleteDC(monitorDC); } } }
return calculatedPhysicalRect; }
//void DrawMoving() //{ // HDC dc = GetDC(nullptr); // // SetROP2(dc, R2_XORPEN); // // WORD grayPattern[8]; // for (int i = 0; i < 8; i++) // { // grayPattern = (WORD)(0x5555 << (i & 1)); // } // // HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); // // LOGBRUSH logBrush = {}; // logBrush.lbStyle = BS_PATTERN; // logBrush.lbHatch = (LONG_PTR)grayBitmap; // HPEN pen = ExtCreatePen(PS_GEOMETRIC, 0, &logBrush, 0, nullptr); // // SelectObject(dc, pen); // SelectObject(dc, GetStockObject(NULL_BRUSH)); // // if (g_windowToTrack) // { // Rectangle(dc, g_lastRect.left, g_lastRect.top, g_lastRect.right, g_lastRect.bottom); // } // // if (m_movingWindow) // // { // DWORD postion = GetMessagePos(); // // POINT pt; // // pt.x = GET_X_LPARAM(postion); // // pt.y = GET_Y_LPARAM(postion); // // // RECT virtualRect; // GetWindowRect(g_windowToTrack, &virtualRect); // OffsetRect(&virtualRect, (pt.x - g_moveStartPoint.x), (pt.y - g_moveStartPoint.y)); // // // RECT calculatedPhysicalRect = {}; // // if (g_dummyWindowToMove) // // { // SetWindowPos(g_dummyWindowToMove, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE); // // // DPI_AWARENESS_CONTEXT oldAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); // GetWindowRect(g_dummyWindowToMove, &calculatedPhysicalRect); // // SetThreadDpiAwarenessContext(oldAwareness); // } // else // { // POINT centerPoint; // centerPoint.x = virtualRect.left + (virtualRect.right - virtualRect.left) / 2; // centerPoint.y = virtualRect.top + (virtualRect.bottom - virtualRect.top) / 2; // HMONITOR monitor = MonitorFromPoint(centerPoint, MONITOR_DEFAULTTONEAREST); // MONITORINFOEX monitorInfo = {}; // monitorInfo.cbSize = sizeof(MONITORINFOEX); // GetMonitorInfo(monitor, &monitorInfo); // // HDC monitorDC = CreateDC(nullptr, monitorInfo.szDevice, nullptr, nullptr); // int screenWidth = GetDeviceCaps(monitorDC, DESKTOPHORZRES); // int virtualizedScreenWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; // // calculatedPhysicalRect.left = ((virtualRect.left - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.left; // calculatedPhysicalRect.top = ((virtualRect.top - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.top; // calculatedPhysicalRect.right = ((virtualRect.right - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.left; // calculatedPhysicalRect.bottom = ((virtualRect.bottom - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.top; // } // // Rectangle(dc, calculatedPhysicalRect.left, calculatedPhysicalRect.top, calculatedPhysicalRect.right, calculatedPhysicalRect.bottom); // // g_lastRect = calculatedPhysicalRect; // } // // DeleteObject(pen); // DeleteObject(grayBitmap); // // ReleaseDC(nullptr, dc); //}
// Message handler for about box. INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE;
case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }
|