Skip to content

fix Win32/Win64 garbled "Chinese" window titles in UNICODE builds. Fix #27645 #27646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 4.x
Choose a base branch
from

Conversation

GerHobbelt
Copy link

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request


fix Win32/Win64 garbled "Chinese" window titles in UNICODE builds. See also see also https://stackoverflow.com/questions/33358546/chinese-characters-in-title-bar for an equivalent problem. This fix does away with all the usual ANSI API hacks as that DOES NOT fly as soon as you have a user-defined Windows message handler callback, which calls DefWindowProc et al.

This fix introduces a generic approach which works for all ANSI and UNICODE builds, where an assistant class is presented to do the heavy lifting back & forth between UTF8/char*/CHAR and UCS2/UTF16/WCHAR for anyone who needs it. The design intent is for this class to be used as a temporary instance, presenting a pointer to its internal storage for the duration of an API call, where & when needed.

The supplied T() macro is merely a small service to make the code more readable. (Old MFC hacks will gnash their dentures, I'm sure :-) )

The rest of the implementation uses the default Win32 API calls as offered by Microsoft system header files (no A or W suffix), plus the advised TEXT() macro to ensure any string constant is properly tagged for the compiler to produce the correct content and type (char/wchar_t -> LPCSTR/LPCWSTR).

NEVER use CHAR/LPSTR/LPCSTR/... for ANSI/UNICODE build portable code; use TCHAR/LPTSTR/LPCTSTR/... instead and have your support code ready to deal with TCHAR being either char or UCS2/UTF16 wchar_t. This is a corollary of Microsoft's advice to not mix ANSI and UNICODE API calls in the same run-time (that's EXE + DLLs all). You may get away with an ANSI patch (I often do!) when you're not using UI/GUI API calls, but once you need those (as OpenCV does here to open a window pane to show an image), the game is up. A sensible rule-of-thumb test is: does your Win32 API usage possibly involve a Windows Message Loop under the hood? --> if not answerable by an empathetic NO, then refrain from using any ANSI calls.

This also fixes a very minor issue about a very cute while(0) hack that doesn't fly as soon as you compile with the warning level dialed up beyond vanilla W3 (we build this stuff with ALL warnings turned on -- the MSVC simile of -Wpedantic: see the code comment there.


From the code:

Win32UIClass: assistant class to ease compiling this in both classic ANSI mode and modern UNICODE builds.

the moral of the story for this entire source file: ditch the Win32 A (ANSI) API calls' hack, and use what they (MS) hand you as-is (W or A, depending on your compile settings) and transform any strings to the type that's needed: ANSI/UTF8 or UCS2/UTF16.

root cause: see also https://stackoverflow.com/questions/33358546/chinese-characters-in-title-bar which was someone else with what turned out to be the same exhibit (this code also has DefWindowProc calls in the window handlers making the whole thing b0rk if you're not extra-special careful in coding this sort of thing.

Bottom line: as soon as you're using DefWindowProc in any way, stick with what Microsoft
system header files give you as the 'default' Win32 API and deal! TCHAR, TEXT(), etc.

  • Win32UIString class and our personal T() macro for the non-static strings.

Re: do ... while (0);:
while(0,0) instead of while(0) to avoid MSVC compiler warning C4127: "conditional expression is constant" ... but now gives us this instead:
warning C4548 : expression before comma has no effect; expected expression with side-effect

--> no way around using #pragma warning(disable) then.

…e also see also https://stackoverflow.com/questions/33358546/chinese-characters-in-title-bar for an equivalent problem. This fix does away with all the usual ANSI API hacks as that DOES NOT fly as soon as you have a user-defined Windows message handler callback, which calls DefWindowProc et al.

This fix introduces a generic approach which works for all ANSI and UNICODE builds, where an assistant class is presented to do the heavy lifting back & forth between UTF8/`char*`/`CHAR` and UCS2/UTF16/`WCHAR` for anyone who needs it. The design intent is for this class to be used as a temporary instance, presenting a pointer to its internal storage for the duration of an API call, where & when needed.

The supplied `T()` macro is merely a small service to make the code more readable. (Old MFC hacks will gnash their dentures, I'm sure :-) )

The rest of the implementation uses the default Win32 API calls as offered by Microsoft system header files (no A or W suffix), plus the advised `TEXT()` macro to ensure any **string constant** is properly tagged for the compiler to produce the correct content and *type* (char/wchar_t -> LPCSTR/LPCWSTR).

NEVER use CHAR/LPSTR/LPCSTR/... for ANSI/UNICODE build portable code; use TCHAR/LPTSTR/LPCTSTR/... instead and have your support code ready to deal with TCHAR being either `char` or UCS2/UTF16 `wchar_t`. This is a corollary of Microsoft's advice to not mix ANSI and UNICODE API calls in the same run-time (that's EXE + DLLs all).
You may get away with an ANSI patch (I often do!) when you're *not* using UI/GUI API calls, but once you need those (as OpenCV does here to open a window pane to show an image), the game is up.
A sensible rule-of-thumb test is: does your Win32 API usage *possibly involve a Windows Message Loop under the hood?* --> if not answerable by an empathetic NO, then refrain from using any ANSI calls.

This also fixes a very minor issue about a very cute `while(0)` hack that doesn't fly as soon as you compile with the warning level dialed up beyond vanilla W3 (we build this stuff with ALL warnings turned on -- the MSVC simile of `-Wpedantic`: see the code comment there.

----------------------------------

From the code:

Win32UIClass: assistant class to ease compiling this in both classic ANSI mode and modern UNICODE builds.

the moral of the story for this entire source file: ditch the Win32 A (ANSI) API calls' hack, and
use what they (MS) hand you as-is (W or A, depending on your compile settings) and transform any
strings to the type that's needed: ANSI/UTF8 or UCS2/UTF16.

root cause: see also https://stackoverflow.com/questions/33358546/chinese-characters-in-title-bar
which was someone else with what turned out to be the same exhibit (this code also has
DefWindowProc calls in the window handlers making the whole thing b0rk if you're not extra-special
careful in coding this sort of thing.

Bottom line: as soon as you're using DefWindowProc in *any* way, stick with what Microsoft
system header files give you as the 'default' Win32 API and deal!    TCHAR, TEXT(), etc.
+ Win32UIString class and our personal T() macro for the non-static strings.

Re: `do ... while (0);`:
while(0,0) instead of while(0) to avoid MSVC compiler warning C4127: "conditional expression is constant"
... but now gives us this instead:
warning C4548 : expression before comma has no effect; expected expression with side-effect

--> no way around using `#pragma warning(disable)` then.

# Conflicts:
#	modules/highgui/src/window_w32.cpp
@GerHobbelt
Copy link
Author

GerHobbelt commented Aug 8, 2025

I see one of your CI builds fails for this, due to me using std::variant, which is C++17.

Do you want this backported to C++11 or older?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants