diff options
author | Gerald Combs <gerald@wireshark.org> | 2018-05-09 11:46:56 -0700 |
---|---|---|
committer | Gerald Combs <gerald@wireshark.org> | 2018-05-09 20:02:34 +0000 |
commit | 23e1cde5e7489bf662fd9e8bb2555085b9845871 (patch) | |
tree | 75a868f5b4acc746efdeb719cf5998120910c408 | |
parent | e76ca2d3cb005d9276c953a14e63523a21cdb8dc (diff) |
Windows: Make sure native dialogs handle HiDPI.
Enable per-monitor v2 DPI awareness before displaying native file
dialogs so that they will render correctly on HiDPI displays.
Add some notes about DPI awareness in our manifest and in
wireshark-qt.cpp.
Remove win32_get_ofnsize while we're here.
Change-Id: Ic553fdeea0c05020c3a7ff06f648692cb814b3eb
Reviewed-on: https://code.wireshark.org/review/27435
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Gerald Combs <gerald@wireshark.org>
-rw-r--r-- | image/wireshark.exe.manifest.in | 13 | ||||
-rw-r--r-- | ui/win32/file_dlg_win32.c | 179 | ||||
-rw-r--r-- | ui/win32/file_dlg_win32.h | 21 | ||||
-rw-r--r-- | wireshark-qt.cpp | 2 |
4 files changed, 135 insertions, 80 deletions
diff --git a/image/wireshark.exe.manifest.in b/image/wireshark.exe.manifest.in index b59921b941..805c2d9603 100644 --- a/image/wireshark.exe.manifest.in +++ b/image/wireshark.exe.manifest.in @@ -46,4 +46,17 @@ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> </application> </compatibility> + <!-- + MSDN recommends setting our DPI awareness to PerMonitorV2 instead + of PerMonitor. Unfortunately that causes layout issues with Qt + 5.6 and 5.9. For now enable PerMonitor DPI awareness by enabling + Qt::AA_EnableHighDpiScaling in wireshark-qt.cpp. + --> + <!-- + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> + </windowsSettings> + </application> + --> </assembly> diff --git a/ui/win32/file_dlg_win32.c b/ui/win32/file_dlg_win32.c index c05f259b3c..97f16a430a 100644 --- a/ui/win32/file_dlg_win32.c +++ b/ui/win32/file_dlg_win32.c @@ -93,6 +93,56 @@ static void range_handle_wm_command(HWND dlg_hwnd, HWND ctrl, WPARAM w_param, pa static TCHAR *build_file_open_type_list(void); static TCHAR *build_file_save_type_list(GArray *savable_file_types); +#ifdef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +typedef DPI_AWARENESS_CONTEXT (WINAPI *GetThreadDpiAwarenessContextProc)(void); +typedef DPI_AWARENESS_CONTEXT (WINAPI *SetThreadDpiAwarenessContextProc)(DPI_AWARENESS_CONTEXT); + +static GetThreadDpiAwarenessContextProc GetThreadDpiAwarenessContextP; +static SetThreadDpiAwarenessContextProc SetThreadDpiAwarenessContextP; +static gboolean got_proc_addresses = FALSE; + +static gboolean get_proc_addresses(void) { + if (got_proc_addresses) return TRUE; + + HMODULE u32_module = LoadLibrary(_T("User32.dll")); + if (!u32_module) { + got_proc_addresses = FALSE; + return FALSE; + } + gboolean got_all = TRUE; + GetThreadDpiAwarenessContextP = (GetThreadDpiAwarenessContextProc) GetProcAddress(u32_module, "GetThreadDpiAwarenessContext"); + if (!GetThreadDpiAwarenessContextP) got_all = FALSE; + SetThreadDpiAwarenessContextP = (SetThreadDpiAwarenessContextProc) GetProcAddress(u32_module, "SetThreadDpiAwarenessContext"); + if (!SetThreadDpiAwarenessContextP) got_all = FALSE; + + got_proc_addresses = got_all; + return got_all; +} + +// Enabling DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 causes issues +// when dragging our open file dialog between differently-DPIed +// displays. It might be time to break down and switch to common +// item dialogs. +HANDLE set_thread_per_monitor_v2_awareness(void) { + if (! get_proc_addresses()) return 0; +#if 0 + WCHAR info[100]; + StringCchPrintf(info, 100, + L"GetThrDpiAwarenessCtx: %d", + GetThreadDpiAwarenessContextP()); + MessageBox(NULL, info, _T("DPI info"), MB_OK); +#endif + return (HANDLE) SetThreadDpiAwarenessContextP(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); +} + +void revert_thread_per_monitor_v2_awareness(HANDLE context) { + SetThreadDpiAwarenessContextP((DPI_AWARENESS_CONTEXT) context); +} +#else // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +HANDLE set_thread_per_monitor_v2_awareness(void) { return 0; } +void revert_thread_per_monitor_v2_awareness(HANDLE context _U_) { } +#endif // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 + static int g_filetype; static gboolean g_compressed; static packet_range_t *g_range; @@ -114,65 +164,6 @@ static HWND g_sf_hwnd = NULL; static char *g_dfilter_str = NULL; static unsigned int g_format_type = WTAP_TYPE_AUTO; -static int -win32_get_ofnsize() -{ - gboolean bVerGE5 = FALSE; - int ofnsize; - /* Remarks on OPENFILENAME_SIZE_VERSION_400: - * - * MSDN states that OPENFILENAME_SIZE_VERSION_400 should be used with - * WINVER and _WIN32_WINNT >= 0x0500. - * Unfortunately all these are compiler constants, while the underlying is a - * problem based is a length check of the runtime version used. - * - * Instead of using OPENFILENAME_SIZE_VERSION_400, just malloc - * the OPENFILENAME size plus 12 bytes. - * These 12 bytes are the difference between the two versions of this struct. - * - * Interestingly this fixes a bug, so the places bar e.g. "My Documents" - * is displayed - which wasn't the case with the former implementation. - * - * XXX - It's unclear if this length+12 works on all supported platforms, - * NT4 is the question here. However, even if it fails, we must calculate - * the length based on the runtime, not the compiler version anyway ... - */ - /* This assumption does not work when compiling with MSVC2008EE as - * the open dialog window does not appear. - * Instead detect Windows version at runtime and choose size accordingly */ -#if (_MSC_VER >= 1500) - /* - * On VS2103, GetVersionEx is deprecated. Microsoft recommend to - * use VerifyVersionInfo instead - */ -#if (_MSC_VER >= 1800) - OSVERSIONINFOEX osvi; - DWORDLONG dwlConditionMask = 0; - int op = VER_GREATER_EQUAL; - /* Initialize the OSVERSIONINFOEX structure. */ - SecureZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - osvi.dwMajorVersion = 5; - /* Initialize the condition mask. */ - VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op); - /* Perform the test. */ - bVerGE5=VerifyVersionInfo( - &osvi, - VER_MAJORVERSION, - dwlConditionMask); -#else - OSVERSIONINFO osvi; - SecureZeroMemory(&osvi, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osvi); - bVerGE5 = (osvi.dwMajorVersion >= 5); -#endif /* _MSC_VER >= 1800 */ - ofnsize = (bVerGE5)?sizeof(OPENFILENAME):OPENFILENAME_SIZE_VERSION_400; -#else - ofnsize = sizeof(OPENFILENAME)+12; -#endif /* _MSC_VER >= 1500 */ - return ofnsize; -} /* * According to http://msdn.microsoft.com/en-us/library/bb776913.aspx * we should use IFileOpenDialog and IFileSaveDialog on Windows Vista @@ -184,7 +175,7 @@ win32_open_file (HWND h_wnd, GString *file_name, unsigned int *type, GString *di OPENFILENAME *ofn; TCHAR file_name16[MAX_PATH] = _T(""); int ofnsize; - gboolean gofn_ok; + BOOL gofn_ok; if (!file_name || !display_filter) return FALSE; @@ -199,7 +190,7 @@ win32_open_file (HWND h_wnd, GString *file_name, unsigned int *type, GString *di g_free(g_dfilter_str); g_dfilter_str = NULL; } - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -226,7 +217,9 @@ win32_open_file (HWND h_wnd, GString *file_name, unsigned int *type, GString *di ofn->lpfnHook = open_file_hook_proc; ofn->lpTemplateName = _T("WIRESHARK_OPENFILENAME_TEMPLATE"); + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); gofn_ok = GetOpenFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); if (gofn_ok) { g_string_printf(file_name, "%s", utf_16to8(file_name16)); @@ -339,7 +332,7 @@ win32_save_as_file(HWND h_wnd, capture_file *cf, GString *file_name, int *file_t OPENFILENAME *ofn; TCHAR file_name16[MAX_PATH] = _T(""); int ofnsize; - gboolean gsfn_ok; + BOOL gsfn_ok; if (!file_name || !file_type || !compressed) return FALSE; @@ -361,7 +354,7 @@ win32_save_as_file(HWND h_wnd, capture_file *cf, GString *file_name, int *file_t return FALSE; /* shouldn't happen - the "Save As..." item should be disabled if we can't save the file */ g_compressed = FALSE; - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -385,7 +378,9 @@ win32_save_as_file(HWND h_wnd, capture_file *cf, GString *file_name, int *file_t ofn->lpfnHook = save_as_file_hook_proc; ofn->lpTemplateName = _T("WIRESHARK_SAVEASFILENAME_TEMPLATE"); + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); gsfn_ok = GetSaveFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); if (gsfn_ok) { g_string_printf(file_name, "%s", utf_16to8(file_name16)); @@ -416,7 +411,7 @@ gboolean win32_save_as_statstree(HWND h_wnd, GString *file_name, int *file_type) OPENFILENAME *ofn; TCHAR file_name16[MAX_PATH] = _T(""); int ofnsize; - gboolean gsfn_ok; + BOOL gsfn_ok; #if (_MSC_VER >= 1500) OSVERSIONINFO osvi; #endif @@ -463,7 +458,9 @@ gboolean win32_save_as_statstree(HWND h_wnd, GString *file_name, int *file_type) ofn->lpfnHook = save_as_statstree_hook_proc; ofn->lpTemplateName = _T("WIRESHARK_SAVEASSTATSTREENAME_TEMPLATE"); + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); gsfn_ok = GetSaveFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); if (gsfn_ok) { g_string_printf(file_name, "%s", utf_16to8(file_name16)); @@ -487,7 +484,7 @@ win32_export_specified_packets_file(HWND h_wnd, capture_file *cf, OPENFILENAME *ofn; TCHAR file_name16[MAX_PATH] = _T(""); int ofnsize; - gboolean gsfn_ok; + BOOL gsfn_ok; if (!file_name || !file_type || !compressed || !range) return FALSE; @@ -505,7 +502,7 @@ win32_export_specified_packets_file(HWND h_wnd, capture_file *cf, g_cf = cf; g_compressed = FALSE; - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -529,7 +526,9 @@ win32_export_specified_packets_file(HWND h_wnd, capture_file *cf, ofn->lpfnHook = export_specified_packets_file_hook_proc; ofn->lpTemplateName = _T("WIRESHARK_EXPORT_SPECIFIED_PACKETS_FILENAME_TEMPLATE"); + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); gsfn_ok = GetSaveFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); if (gsfn_ok) { g_string_printf(file_name, "%s", utf_16to8(file_name16)); @@ -563,7 +562,7 @@ win32_merge_file (HWND h_wnd, GString *file_name, GString *display_filter, int * OPENFILENAME *ofn; TCHAR file_name16[MAX_PATH] = _T(""); int ofnsize; - gboolean gofn_ok; + BOOL gofn_ok; if (!file_name || !display_filter || !merge_type) return FALSE; @@ -579,7 +578,7 @@ win32_merge_file (HWND h_wnd, GString *file_name, GString *display_filter, int * g_dfilter_str = NULL; } - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -606,7 +605,9 @@ win32_merge_file (HWND h_wnd, GString *file_name, GString *display_filter, int * ofn->lpfnHook = merge_file_hook_proc; ofn->lpTemplateName = _T("WIRESHARK_MERGEFILENAME_TEMPLATE"); + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); gofn_ok = GetOpenFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); if (gofn_ok) { g_string_printf(file_name, "%s", utf_16to8(file_name16)); @@ -644,7 +645,7 @@ win32_export_file(HWND h_wnd, capture_file *cf, export_type_e export_type) { g_cf = cf; - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -680,7 +681,11 @@ win32_export_file(HWND h_wnd, capture_file *cf, export_type_e export_type) { print_args.print_formfeed = FALSE; print_args.stream = NULL; - if (GetSaveFileName(ofn)) { + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); + BOOL gsfn_ok = GetSaveFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); + + if (gsfn_ok) { print_args.file = utf_16to8(file_name); switch (ofn->nFilterIndex) { case export_type_text: /* Text */ @@ -756,7 +761,7 @@ win32_export_raw_file(HWND h_wnd, capture_file *cf) { return; } - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -785,7 +790,11 @@ win32_export_raw_file(HWND h_wnd, capture_file *cf) { * grab the info from cf->finfo_selected. Which is more "correct"? */ - if (GetSaveFileName(ofn)) { + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); + BOOL gsfn_ok = GetSaveFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); + + if (gsfn_ok) { g_free( (void *) ofn); file_name8 = utf_16to8(file_name); data_p = tvb_get_ptr(cf->finfo_selected->ds_tvb, 0, -1) + @@ -831,7 +840,7 @@ win32_export_sslkeys_file(HWND h_wnd) { return; } - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -855,7 +864,11 @@ win32_export_sslkeys_file(HWND h_wnd) { ofn->lpfnHook = export_sslkeys_file_hook_proc; ofn->lpTemplateName = _T("WIRESHARK_EXPORTSSLKEYSFILENAME_TEMPLATE"); - if (GetSaveFileName(ofn)) { + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); + BOOL gsfn_ok = GetSaveFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); + + if (gsfn_ok) { g_free( (void *) ofn); file_name8 = utf_16to8(file_name); keylist = ssl_export_sessions(); @@ -898,7 +911,7 @@ win32_export_color_file(HWND h_wnd, capture_file *cf, gpointer filter_list) { int ofnsize; gchar *err_msg = NULL; - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -924,7 +937,11 @@ win32_export_color_file(HWND h_wnd, capture_file *cf, gpointer filter_list) { g_filetype = cf->cd_t; /* XXX - Support marked filters */ - if (GetSaveFileName(ofn)) { + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); + BOOL gsfn_ok = GetSaveFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); + + if (gsfn_ok) { g_free( (void *) ofn); if (!color_filters_export(utf_16to8(file_name), filter_list, FALSE /* all filters */, &err_msg)) { @@ -949,7 +966,7 @@ win32_import_color_file(HWND h_wnd, gpointer color_filters) { int ofnsize; gchar *err_msg = NULL; - ofnsize = win32_get_ofnsize(); + ofnsize = sizeof(OPENFILENAME); ofn = g_malloc0(ofnsize); ofn->lStructSize = ofnsize; @@ -973,7 +990,11 @@ win32_import_color_file(HWND h_wnd, gpointer color_filters) { ofn->lpTemplateName = NULL; /* XXX - Support export limited to selected filters */ - if (GetOpenFileName(ofn)) { + HANDLE save_da_ctx = set_thread_per_monitor_v2_awareness(); + BOOL gofn_ok = GetOpenFileName(ofn); + revert_thread_per_monitor_v2_awareness(save_da_ctx); + + if (gofn_ok) { g_free( (void *) ofn); if (!color_filters_import(utf_16to8(file_name), color_filters, &err_msg, color_filter_add_cb)) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); @@ -1324,7 +1345,6 @@ filter_tb_syntax_check(HWND hwnd, TCHAR *filter_text) { g_free(strval); } - static UINT_PTR CALLBACK open_file_hook_proc(HWND of_hwnd, UINT msg, WPARAM w_param, LPARAM l_param) { HWND cur_ctrl, parent; @@ -1873,7 +1893,6 @@ export_specified_packets_file_hook_proc(HWND sf_hwnd, UINT msg, WPARAM w_param, return 0; } - #define STATIC_LABEL_CHARS 100 /* For each range static control, fill in its value and enable/disable it. */ static void diff --git a/ui/win32/file_dlg_win32.h b/ui/win32/file_dlg_win32.h index 10ced0e4be..671c1b4af5 100644 --- a/ui/win32/file_dlg_win32.h +++ b/ui/win32/file_dlg_win32.h @@ -20,6 +20,27 @@ extern "C" { #endif /* __cplusplus */ +/** + * @brief set_thread_per_monitor_v2_awareness + * + * Qt <= 5.9 supports setting old (Windows 8.1) per-monitor DPI awareness + * via Qt:AA_EnableHighDpiScaling. We do this in wireshark-qt.cpp. In order + * for native dialogs to be rendered correctly we need to to set per-monitor + * *v2* awareness prior to creating the dialog, which we can do here. + * Qt doesn't render correctly when per-monitor v2 awareness is enabled, so + * we need to revert our thread context when we're done. + * + * @return The current thread DPI awareness context, which should + * be passed to revert_thread_per_monitor_v2_awareness. + */ +HANDLE set_thread_per_monitor_v2_awareness(void); + +/** + * @brief revert_thread_per_monitor_v2_awareness + * @param context + */ +void revert_thread_per_monitor_v2_awareness(HANDLE context); + /** Open the "Open" dialog box. * * @param h_wnd HWND of the parent window. diff --git a/wireshark-qt.cpp b/wireshark-qt.cpp index 7b8d48ebad..4b595b0ebc 100644 --- a/wireshark-qt.cpp +++ b/wireshark-qt.cpp @@ -514,6 +514,8 @@ int main(int argc, char *qt_argv[]) // one case on X11 and we don't yet support Android. // We do the equivalent on macOS by setting NSHighResolutionCapable // in Info.plist. + // Note that this enables Windows 8.1-style Per-monitor DPI + // awareness but not Windows 10-style Per-monitor v2 awareness. // http://doc.qt.io/qt-5/scalability.html // http://doc.qt.io/qt-5/highdpi.html // https://bugreports.qt.io/browse/QTBUG-53022 - The device pixel ratio is pretty much bogus on Windows. |