It got lost in win32u conversion. In most cases, we may perform the conversion in user32, but trying to do that in inter-process would be weird.
From: Jacek Caban jacek@codeweavers.com
Based on user32 WINPROC_CallProcWtoA.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53744 --- dlls/win32u/font.c | 82 ++++++++++ dlls/win32u/message.c | 282 ++++++++++++++++++++++++++++++++++- dlls/win32u/ntgdi_private.h | 1 - dlls/win32u/win32u_private.h | 3 + 4 files changed, 366 insertions(+), 2 deletions(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 9c5da5bbdc9..832ffc0bed0 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -3619,6 +3619,60 @@ CPTABLEINFO *get_cptable( WORD cp ) return &tables[i]; }
+/* Based on NlsValidateLocale */ +const NLS_LOCALE_DATA *get_locale_data( LCID lcid ) +{ + static const NLS_LOCALE_HEADER *locale_table; + static const NLS_LOCALE_LCID_INDEX *lcids_index; + int min = 0, max; + + if (!locale_table) + { + LARGE_INTEGER size; + void *addr; + LCID lcid; + NTSTATUS status; + static struct + { + UINT ctypes; + UINT unknown1; + UINT unknown2; + UINT unknown3; + UINT locales; + UINT charmaps; + UINT geoids; + UINT scripts; + } *header; + + status = NtInitializeNlsFiles( &addr, &lcid, &size ); + if (status) + { + ERR( "Failed to load nls file\n" ); + return NULL; + } + + if (InterlockedCompareExchangePointer( (void **)&header, addr, NULL )) + NtUnmapViewOfSection( GetCurrentProcess(), addr ); + + locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales); + lcids_index = (const NLS_LOCALE_LCID_INDEX *)((char *)locale_table + locale_table->lcids_offset); + } + + max = locale_table->nb_lcids - 1; + while (min <= max) + { + int pos = (min + max) / 2; + if (lcid < lcids_index[pos].id) max = pos - 1; + else if (lcid > lcids_index[pos].id) min = pos + 1; + else + { + ULONG offset = locale_table->locales_offset + pos * locale_table->locale_size; + return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset); + } + } + return NULL; +} + DWORD win32u_wctomb( CPTABLEINFO *info, char *dst, DWORD dstlen, const WCHAR *src, DWORD srclen ) { DWORD ret; @@ -3665,6 +3719,34 @@ DWORD win32u_mbtowc( CPTABLEINFO *info, WCHAR *dst, DWORD dstlen, const char *sr return ret / sizeof(WCHAR); }
+DWORD win32u_mbtowc_size( CPTABLEINFO *info, const char *src, DWORD srclen ) +{ + DWORD ret; + + if (info->CodePage == CP_UTF8) + { + RtlUTF8ToUnicodeN( NULL, 0, &ret, src, srclen ); + ret /= sizeof(WCHAR); + } + else if (info->DBCSCodePage) + { + for (ret = 0; srclen; srclen--, src++, ret++) + { + if (info->DBCSOffsets[(unsigned char)*src] && srclen > 1) + { + src++; + srclen--; + } + } + } + else + { + ret = srclen; + } + + return ret; +} + static BOOL wc_to_index( UINT cp, WCHAR wc, unsigned char *dst, BOOL allow_default ) { const CPTABLEINFO *info; diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index d1eeacba39e..f439b7da23b 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -29,6 +29,7 @@ #define WIN32_NO_STATUS #include "win32u_private.h" #include "ntuser_private.h" +#include "winnls.h" #include "hidusage.h" #include "dbt.h" #include "dde.h" @@ -2521,6 +2522,17 @@ static LRESULT send_inter_thread_message( const struct send_message_info *info, return retrieve_reply( info, reply_size, res_ptr ); }
+static LRESULT send_inter_thread_callback( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, + LRESULT *result, void *arg ) +{ + struct send_message_info *info = arg; + info->hwnd = hwnd; + info->msg = msg; + info->wparam = wp; + info->lparam = lp; + return send_inter_thread_message( info, result ); +} + /*********************************************************************** * send_internal_message_timeout * @@ -2835,6 +2847,268 @@ static BOOL process_packed_message( struct send_message_info *info, LRESULT *res return TRUE; }
+static inline void *get_buffer( void *static_buffer, size_t size, size_t need ) +{ + if (size >= need) return static_buffer; + return malloc( need ); +} + +static inline void free_buffer( void *static_buffer, void *buffer ) +{ + if (buffer != static_buffer) free( buffer ); +} + +typedef LRESULT (*winproc_callback_t)( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, + LRESULT *result, void *arg ); + +/********************************************************************** + * test_lb_for_string + * + * Return TRUE if the lparam is a string + */ +static inline BOOL test_lb_for_string( HWND hwnd, UINT msg ) +{ + DWORD style = get_window_long( hwnd, GWL_STYLE ); + if (msg <= CB_MSGMAX) + return (!(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) || (style & CBS_HASSTRINGS)); + else + return (!(style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) || (style & LBS_HASSTRINGS)); + +} + +static CPTABLEINFO *get_input_codepage( void ) +{ + const NLS_LOCALE_DATA *locale; + HKL hkl = NtUserGetKeyboardLayout( 0 ); + CPTABLEINFO *ret = NULL; + + locale = get_locale_data( LOWORD(hkl) ); + if (locale && locale->idefaultansicodepage != CP_UTF8) + ret = get_cptable( locale->idefaultansicodepage ); + return ret ? ret : &ansi_cp; +} + +/*********************************************************************** + * map_wparam_AtoW + * + * Convert the wparam of an ASCII message to Unicode. + */ +static BOOL map_wparam_AtoW( UINT message, WPARAM *wparam, enum wm_char_mapping mapping ) +{ + char ch[2]; + WCHAR wch[2]; + + wch[0] = wch[1] = 0; + switch(message) + { + case WM_CHARTOITEM: + case EM_SETPASSWORDCHAR: + case WM_DEADCHAR: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + case WM_MENUCHAR: + ch[0] = LOBYTE(*wparam); + ch[1] = HIBYTE(*wparam); + win32u_mbtowc( get_input_codepage(), wch, 2, ch, 2 ); + *wparam = MAKEWPARAM(wch[0], wch[1]); + break; + + case WM_IME_CHAR: + ch[0] = HIBYTE(*wparam); + ch[1] = LOBYTE(*wparam); + if (ch[0]) win32u_mbtowc( get_input_codepage(), wch, 2, ch, 2 ); + else win32u_mbtowc( get_input_codepage(), wch, 1, ch + 1, 1 ); + *wparam = MAKEWPARAM(wch[0], HIWORD(*wparam)); + break; + } + return TRUE; +} + +/********************************************************************** + * call_messageAtoW + * + * Call a window procedure, translating args from Ansi to Unicode. + */ +LRESULT call_messageAtoW( winproc_callback_t callback, HWND hwnd, UINT msg, WPARAM wparam, + LPARAM lparam, LRESULT *result, void *arg, enum wm_char_mapping mapping ) +{ + LRESULT ret = 0; + + TRACE( "(hwnd=%p,msg=%s,wp=%#lx,lp=%#lx)\n", hwnd, debugstr_msg_name( msg, hwnd ), + (long)wparam, (long)lparam ); + + switch(msg) + { + case WM_MDICREATE: + { + WCHAR *ptr, buffer[512]; + DWORD title_lenA = 0, title_lenW = 0, class_lenA = 0, class_lenW = 0; + MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lparam; + MDICREATESTRUCTW csW; + + memcpy( &csW, csA, sizeof(csW) ); + + if (!IS_INTRESOURCE(csA->szTitle)) + { + title_lenA = strlen(csA->szTitle) + 1; + title_lenW = win32u_mbtowc_size( &ansi_cp, csA->szTitle, title_lenA ); + } + if (!IS_INTRESOURCE(csA->szClass)) + { + class_lenA = strlen(csA->szClass) + 1; + class_lenW = win32u_mbtowc_size( &ansi_cp, csA->szClass, class_lenA ); + } + + if (!(ptr = get_buffer( buffer, sizeof(buffer), (title_lenW + class_lenW) * sizeof(WCHAR) ))) + break; + + if (title_lenW) + { + csW.szTitle = ptr; + win32u_mbtowc( &ansi_cp, ptr, title_lenW, csA->szTitle, title_lenA ); + } + if (class_lenW) + { + csW.szClass = ptr + title_lenW; + win32u_mbtowc( &ansi_cp, ptr + title_lenW, class_lenW, csA->szClass, class_lenA ); + } + ret = callback( hwnd, msg, wparam, (LPARAM)&csW, result, arg ); + free_buffer( buffer, ptr ); + } + break; + + case WM_GETTEXT: + case WM_ASKCBFORMATNAME: + { + WCHAR *ptr, buffer[512]; + LPSTR str = (LPSTR)lparam; + DWORD len = wparam * sizeof(WCHAR); + + if (!(ptr = get_buffer( buffer, sizeof(buffer), len ))) break; + ret = callback( hwnd, msg, wparam, (LPARAM)ptr, result, arg ); + if (wparam) + { + len = 0; + len = *result ? win32u_wctomb( &ansi_cp, str, wparam - 1, ptr, *result ) : 0; + str[len] = 0; + *result = len; + } + free_buffer( buffer, ptr ); + } + break; + + case LB_ADDSTRING: + case LB_INSERTSTRING: + case LB_FINDSTRING: + case LB_FINDSTRINGEXACT: + case LB_SELECTSTRING: + case CB_ADDSTRING: + case CB_INSERTSTRING: + case CB_FINDSTRING: + case CB_FINDSTRINGEXACT: + case CB_SELECTSTRING: + if (!lparam || !test_lb_for_string( hwnd, msg )) + { + ret = callback( hwnd, msg, wparam, lparam, result, arg ); + break; + } + /* fall through */ + case WM_SETTEXT: + case WM_WININICHANGE: + case WM_DEVMODECHANGE: + case CB_DIR: + case LB_DIR: + case LB_ADDFILE: + case EM_REPLACESEL: + if (!lparam) + { + ret = callback( hwnd, msg, wparam, lparam, result, arg ); + } + else + { + WCHAR *ptr, buffer[512]; + LPCSTR strA = (LPCSTR)lparam; + DWORD lenW, lenA = strlen(strA) + 1; + + lenW = win32u_mbtowc_size( &ansi_cp, strA, lenA ); + if ((ptr = get_buffer( buffer, sizeof(buffer), lenW * sizeof(WCHAR) ))) + { + win32u_mbtowc( &ansi_cp, ptr, lenW, strA, lenA ); + ret = callback( hwnd, msg, wparam, (LPARAM)ptr, result, arg ); + free_buffer( buffer, ptr ); + } + } + break; + + case EM_GETLINE: + { + WCHAR *ptr, buffer[512]; + WORD len = *(WORD *)lparam; + + if (!(ptr = get_buffer( buffer, sizeof(buffer), len * sizeof(WCHAR) ))) break; + *((WORD *)ptr) = len; /* store the length */ + ret = callback( hwnd, msg, wparam, (LPARAM)ptr, result, arg ); + if (*result) + { + DWORD reslen; + reslen = win32u_wctomb( &ansi_cp, (char *)lparam, len, ptr, *result ); + if (reslen < len) ((LPSTR)lparam)[reslen] = 0; + *result = reslen; + } + free_buffer( buffer, ptr ); + } + break; + + case WM_GETDLGCODE: + if (lparam) + { + MSG newmsg = *(MSG *)lparam; + if (map_wparam_AtoW( newmsg.message, &newmsg.wParam, WMCHAR_MAP_NOMAPPING )) + ret = callback( hwnd, msg, wparam, (LPARAM)&newmsg, result, arg ); + } + else ret = callback( hwnd, msg, wparam, lparam, result, arg ); + break; + + case WM_CHARTOITEM: + case WM_MENUCHAR: + case WM_DEADCHAR: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + case EM_SETPASSWORDCHAR: + case WM_IME_CHAR: + if (map_wparam_AtoW( msg, &wparam, mapping )) + ret = callback( hwnd, msg, wparam, lparam, result, arg ); + break; + + case WM_GETTEXTLENGTH: + case CB_GETLBTEXTLEN: + case LB_GETTEXTLEN: + ret = callback( hwnd, msg, wparam, lparam, result, arg ); + if (*result >= 0) + { + WCHAR *ptr, buffer[512]; + LRESULT res; + DWORD len = *result + 1; + /* Determine respective GETTEXT message */ + UINT msg_get_text = msg == WM_GETTEXTLENGTH ? WM_GETTEXT : + (msg == CB_GETLBTEXTLEN ? CB_GETLBTEXT : LB_GETTEXT); + /* wparam differs between the messages */ + WPARAM wp = msg == WM_GETTEXTLENGTH ? len : wparam; + + if (!(ptr = get_buffer( buffer, sizeof(buffer), len * sizeof(WCHAR) ))) break; + + res = send_message( hwnd, msg_get_text, wp, (LPARAM)ptr ); + *result = win32u_wctomb_size( &ansi_cp, ptr, res ); + free_buffer( buffer, ptr ); + } + break; + + default: + ret = callback( hwnd, msg, wparam, lparam, result, arg ); + break; + } + return ret; +}
/*********************************************************************** * process_message @@ -2871,7 +3145,13 @@ static BOOL process_message( struct send_message_info *info, DWORD_PTR *res_ptr, { if (dest_pid != GetCurrentProcessId() && (info->type == MSG_ASCII || info->type == MSG_UNICODE)) info->type = MSG_OTHER_PROCESS; - ret = send_inter_thread_message( info, &result ); + + /* MSG_ASCII can be sent unconverted except for WM_CHAR; everything else needs to be Unicode */ + if (ansi && (info->type != MSG_ASCII || info->msg == WM_CHAR)) + ret = call_messageAtoW( send_inter_thread_callback, info->hwnd, info->msg, + info->wparam, info->lparam, &result, info, info->wm_char ); + else + ret = send_inter_thread_message( info, &result ); } else if (info->type != MSG_OTHER_PROCESS) { diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index 4190ced2f65..be3e4d8cd56 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -339,7 +339,6 @@ extern int add_gdi_face( const WCHAR *family_name, const WCHAR *second_name, DWORD ntmflags, DWORD version, DWORD flags, const struct bitmap_font_size *size ) DECLSPEC_HIDDEN; extern UINT font_init(void) DECLSPEC_HIDDEN; -extern CPTABLEINFO *get_cptable( WORD cp ) DECLSPEC_HIDDEN; extern const struct font_backend_funcs *init_freetype_lib(void) DECLSPEC_HIDDEN;
/* opentype.c */ diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 8e294b7924a..dfa83daba91 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -453,11 +453,14 @@ static inline WCHAR win32u_towupper( WCHAR ch )
extern CPTABLEINFO ansi_cp DECLSPEC_HIDDEN;
+CPTABLEINFO *get_cptable( WORD cp ) DECLSPEC_HIDDEN; +const NLS_LOCALE_DATA *get_locale_data( LCID lcid ) DECLSPEC_HIDDEN; DWORD win32u_mbtowc( CPTABLEINFO *info, WCHAR *dst, DWORD dstlen, const char *src, DWORD srclen ) DECLSPEC_HIDDEN; DWORD win32u_wctomb( CPTABLEINFO *info, char *dst, DWORD dstlen, const WCHAR *src, DWORD srclen ) DECLSPEC_HIDDEN; DWORD win32u_wctomb_size( CPTABLEINFO *info, const WCHAR *src, DWORD srclen ) DECLSPEC_HIDDEN; +DWORD win32u_mbtowc_size( CPTABLEINFO *info, const char *src, DWORD srclen ) DECLSPEC_HIDDEN;
static inline WCHAR *win32u_wcsdup( const WCHAR *str ) {
From: Jacek Caban jacek@codeweavers.com
--- dlls/win32u/tests/win32u.c | 139 +++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index bcfafbf13b3..87419f6fadb 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -694,11 +694,149 @@ static void test_NtUserDisplayConfigGetDeviceInfo(void) ok(status == STATUS_UNSUCCESSFUL || status == STATUS_NOT_SUPPORTED, "got %#lx.\n", status); }
+static LRESULT WINAPI test_ipc_message_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + switch (msg) + { + case WM_SETTEXT: + case CB_FINDSTRING: + ok( !wcscmp( (const WCHAR *)lparam, L"test" ), + "lparam = %s\n", wine_dbgstr_w( (const WCHAR *)lparam )); + return 6; + + case WM_GETTEXT: + case EM_GETLINE: + case CB_GETLBTEXT: + ok( wparam == 100, "wparam = %Iu\n", wparam ); + wcscpy( (void *)lparam, L"Test" ); + return 4; + + case WM_GETTEXTLENGTH: + return 99; + + case WM_MDICREATE: + { + MDICREATESTRUCTW *mdi = (MDICREATESTRUCTW *)lparam; + ok( !wcscmp( mdi->szClass, L"TestClass" ), "szClass = %s\n", wine_dbgstr_w( mdi->szClass )); + ok( !wcscmp( mdi->szTitle, L"TestTitle" ), "szTitle = %s\n", wine_dbgstr_w( mdi->szTitle )); + return 0xdeadbeef; + } + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static void test_inter_process_messages( const char *argv0 ) +{ + WNDCLASSW cls = { 0 }; + char path[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA startup; + MSG msg; + HWND hwnd; + BOOL ret; + + cls.lpfnWndProc = test_ipc_message_proc; + cls.lpszClassName = L"TestIPCClass"; + RegisterClassW( &cls ); + + hwnd = CreateWindowExW( 0, L"TestIPCClass", NULL, WS_POPUP | CBS_HASSTRINGS, 0,0,0,0,0,0,0, NULL ); + + memset( &startup, 0, sizeof(startup) ); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + sprintf( path, "%s win32u ipcmsg %Ix", argv0, (INT_PTR)hwnd ); + ret = CreateProcessA( NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &pi ); + ok( ret, "CreateProcess '%s' failed err %lu.\n", path, GetLastError() ); + + do + { + GetMessageW( &msg, NULL, 0, 0 ); + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } while (msg.message != WM_USER); + + wait_child_process( pi.hProcess ); + + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + + DestroyWindow( hwnd ); + UnregisterClassW( L"TestIPCClass", NULL ); +} + +static void test_inter_process_child( HWND hwnd ) +{ + MDICREATESTRUCTA mdi; + WCHAR bufW[100]; + char buf[100]; + int res; + + res = SendMessageA( hwnd, WM_SETTEXT, 0, (LPARAM)"test" ); + ok( res == 6, "WM_SETTEXT returned %d\n", res ); + + res = NtUserMessageCall( hwnd, WM_SETTEXT, 0, (LPARAM)"test", NULL, NtUserSendMessage, TRUE ); + ok( res == 6, "res = %d\n", res ); + + res = NtUserMessageCall( hwnd, CB_FINDSTRING, 0, (LPARAM)"test", NULL, NtUserSendMessage, TRUE ); + ok( res == 6, "res = %d\n", res ); + + memset( buf, 0xcc, sizeof(buf) ); + res = NtUserMessageCall( hwnd, WM_GETTEXT, sizeof(buf), (LPARAM)buf, NULL, NtUserSendMessage, TRUE ); + ok( res == 4, "res = %d\n", res ); + ok( !strcmp( buf, "Test" ), "buf = %s\n", buf ); + + memset( bufW, 0xcc, sizeof(bufW) ); + res = NtUserMessageCall( hwnd, WM_GETTEXT, ARRAYSIZE(bufW), (LPARAM)bufW, NULL, NtUserSendMessage, FALSE ); + ok( res == 4, "res = %d\n", res ); + ok( !wcscmp( bufW, L"Test" ), "buf = %s\n", buf ); + + memset( buf, 0xcc, sizeof(buf) ); + res = NtUserMessageCall( hwnd, CB_GETLBTEXT, 100, (LPARAM)buf, NULL, NtUserSendMessage, TRUE ); + todo_wine + ok( res == 1, "res = %d\n", res ); + ok( buf[0] == 'T', "buf[0] = %c\n", buf[0] ); + todo_wine + ok( buf[1] == (char)0xcc, "buf[1] = %x\n", buf[1] ); + + memset( buf, 0xcc, sizeof(buf) ); + *(DWORD *)buf = ARRAYSIZE(buf); + res = NtUserMessageCall( hwnd, EM_GETLINE, sizeof(buf), (LPARAM)buf, NULL, NtUserSendMessage, TRUE ); + ok( res == 4, "res = %d\n", res ); + ok( !strcmp( buf, "Test" ), "buf = %s\n", buf ); + + res = NtUserMessageCall( hwnd, WM_GETTEXTLENGTH, 0, 0, NULL, NtUserSendMessage, TRUE ); + ok( res == 4, "res = %d\n", res ); + + mdi.szClass = "TestClass"; + mdi.szTitle = "TestTitle"; + mdi.hOwner = 0; + mdi.x = mdi.y = 10; + mdi.cx = mdi.cy = 100; + mdi.style = 0; + mdi.lParam = 0xdeadbeef; + res = NtUserMessageCall( hwnd, WM_MDICREATE, 0, (LPARAM)&mdi, NULL, NtUserSendMessage, TRUE ); + ok( res == 0xdeadbeef, "res = %d\n", res ); + + PostMessageA( hwnd, WM_USER, 0, 0 ); +} + START_TEST(win32u) { + char **argv; + int argc; + /* native win32u.dll fails if user32 is not loaded, so make sure it's fully initialized */ GetDesktopWindow();
+ argc = winetest_get_mainargs( &argv ); + if (argc > 3 && !strcmp( argv[2], "ipcmsg" )) + { + test_inter_process_child( LongToHandle( strtol( argv[3], NULL, 16 ))); + return; + } + test_NtUserEnumDisplayDevices(); test_window_props(); test_class(); @@ -709,6 +847,7 @@ START_TEST(win32u) test_menu(); test_message_filter(); test_timer(); + test_inter_process_messages( argv[0] );
test_NtUserCloseWindowStation(); test_NtUserDisplayConfigGetDeviceInfo();
This merge request was approved by Huw Davies.