[PATCH v2 0/4] MR11123: Draft: win32u: Move window procedure to the shared memory.
Not extremely useful as reading other processes window proc actually returns ERROR_ACCESS_DENIED, but this continues unifying the window info logic and simplifies the code. -- v2: win32u: Move window procedure to the shared memory. win32u: Simplify winproc handle to index conversions. server: Keep track of class window proc ansi nature. server: Move class local flag to the shared memory. https://gitlab.winehq.org/wine/wine/-/merge_requests/11123
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/win32u/class.c | 19 +++++++++++-------- server/class.c | 16 ++++++++-------- server/protocol.def | 3 +-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index adcbadcd119..06c9dcc4600 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -46,7 +46,6 @@ SYSTEM_BASIC_INFORMATION system_info; typedef struct tagCLASS { struct list entry; /* Entry in class list */ - BOOL local; /* Local class? */ struct dce *dce; /* Opaque pointer to class DCE */ HICON icon_internal; /* internal small icon, derived from hIcon */ const shared_object_t *shared; /* class object in session shared memory */ @@ -538,6 +537,12 @@ static BOOL class_name_matches( CLASS *class, UNICODE_STRING *name ) return name->Length == len && !wcsnicmp( class_name, name->Buffer, len / sizeof(WCHAR) ); } +static BOOL is_local_class( CLASS *class ) +{ + /* class local flag is safe to read without shared object locking as it is constant */ + return class->shared->shm.class.local; +} + static UINT_PTR get_class_instance( CLASS *class ) { struct object_lock lock = OBJECT_LOCK_INIT; @@ -563,7 +568,7 @@ static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name ) UINT_PTR class_instance = get_class_instance( class ); if (!class_name_matches( class, name )) continue; is_win16 = !(class_instance >> 16); - if (!instance || !class->local || class_instance == instance || + if (!instance || !is_local_class( class ) || class_instance == instance || (!is_win16 && ((class_instance & ~0xffff) == (instance & ~0xffff)))) { TRACE( "%s %lx -> %p\n", debugstr_us(name), instance, class ); @@ -648,8 +653,6 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam if (!(class = calloc( 1, sizeof(*class) ))) return 0; - class->local = !fnid && !(wc->style & CS_GLOBALCLASS); - /* Other non-null values must be set by caller */ icon_internal = wc->hIconSm ? 0 : create_small_icon( wc->hIcon ); @@ -670,7 +673,6 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam .menu_name = wine_server_client_ptr( menu_name ), }; wine_server_add_data( req, &info, sizeof(info) ); - req->local = class->local; req->client_ptr = wine_server_client_ptr( class ); req->atom = wine_server_add_atom( req, name ); req->fnid = fnid; @@ -696,15 +698,16 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam goto failed; } - if (class->local) list_add_head( &class_list, &class->entry ); - else list_add_tail( &class_list, &class->entry ); - TRACE( "name=%s->%s atom=%04x wndproc=%p hinst=%p bg=%p style=%08x clsExt=%d winExt=%d class=%p\n", debugstr_w(wc->lpszClassName), debugstr_us(name), atom, wc->lpfnWndProc, instance, wc->hbrBackground, wc->style, wc->cbClsExtra, wc->cbWndExtra, class ); class->icon_internal = icon_internal; class->shared = shared; + + if (is_local_class( class )) list_add_head( &class_list, &class->entry ); + else list_add_tail( &class_list, &class->entry ); + release_class_ptr( class ); return atom; diff --git a/server/class.c b/server/class.c index a3e1b85bc80..28e263b0395 100644 --- a/server/class.c +++ b/server/class.c @@ -44,7 +44,6 @@ struct window_class struct winstation *winstation; /* winstation the class was created on */ struct process *process; /* process owning the class */ int count; /* reference count */ - int local; /* local class? */ atom_t atom; /* class atom for versioned class */ unsigned int fnid; /* builtin control FNID, or 0 */ client_ptr_t client_ptr; /* pointer to class in client address space */ @@ -61,7 +60,6 @@ static struct window_class *create_class( struct process *process, int local, in class->process = (struct process *)grab_object( process ); class->count = 0; - class->local = local; if (!(class->shared = alloc_shared_object( offsetof(class_shm_t, extra[cls_extra]) ))) goto failed; @@ -110,7 +108,7 @@ static struct window_class *find_class( struct process *process, atom_t atom, mo const class_shm_t *shared = class->shared; if (class->atom != atom) continue; is_win16 = !(shared->info.instance >> 16); - if (!instance || !class->local || shared->info.instance == instance || + if (!instance || !shared->local || shared->info.instance == instance || (!is_win16 && ((shared->info.instance & ~0xffff) == (instance & ~0xffff)))) return class; } return NULL; @@ -136,7 +134,7 @@ void release_class( struct window_class *class ) int is_desktop_class( struct window_class *class ) { - return (class->shared->info.atom == DESKTOP_ATOM && !class->local); + return class->shared->info.atom == DESKTOP_ATOM && !class->shared->local; } int is_message_class( struct window_class *class ) @@ -144,8 +142,7 @@ int is_message_class( struct window_class *class ) static const WCHAR messageW[] = {'M','e','s','s','a','g','e'}; static const struct unicode_str name = { messageW, sizeof(messageW) }; struct atom_table *table = get_user_atom_table(); - - return (!class->local && class->shared->info.atom == find_atom( table, &name )); + return !class->shared->local && class->shared->info.atom == find_atom( table, &name ); } int get_class_style( struct window_class *class ) @@ -199,6 +196,7 @@ DECL_HANDLER(create_class) atom_t atom = req->atom; struct class_info info; WCHAR buffer[16]; + bool local; if (name.len < sizeof(info)) return set_error( STATUS_INVALID_PARAMETER ); memcpy( &info, name.str, sizeof(info) ); @@ -227,8 +225,9 @@ DECL_HANDLER(create_class) info.atom = grab_atom( table, atom ); } + local = !req->fnid && !(info.style & CS_GLOBALCLASS); class = find_class( current->process, atom, info.instance ); - if (class && !class->local == !req->local) + if (class && !class->shared->local == !local) { set_win32_error( ERROR_CLASS_ALREADY_EXISTS ); release_atom( table, atom ); @@ -244,7 +243,7 @@ DECL_HANDLER(create_class) return; } - if (!(class = create_class( current->process, req->local, info.cls_extra ))) + if (!(class = create_class( current->process, local, info.cls_extra ))) { release_atom( table, atom ); release_atom( table, info.atom ); @@ -259,6 +258,7 @@ DECL_HANDLER(create_class) memcpy( (void *)shared->name, name.str, name.len ); shared->name_offset = name_offset; shared->name_len = name.len; + shared->local = local; memcpy( (void *)&shared->info, &info, sizeof(info) ); memset( (void *)shared->extra, 0, info.cls_extra ); } diff --git a/server/protocol.def b/server/protocol.def index e8fe6f1db58..3adb403e9c1 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1058,7 +1058,7 @@ typedef volatile struct data_size_t name_offset; /* offset in WCHAR of the unversioned class name, constant */ data_size_t name_len; /* len in bytes of the class name, constant */ WCHAR name[MAX_ATOM_LEN]; /* class name, constant */ - unsigned short __pad; + unsigned short local; /* class is local, constant */ struct class_info info; /* class info (GCLP_*) */ char extra[]; /* extra bytes storage */ } class_shm_t; @@ -3296,7 +3296,6 @@ enum caret_state /* Create a window class */ @REQ(create_class) - int local; /* is it a local class? */ atom_t atom; /* class atom */ unsigned int fnid; /* FNID for builtin classes, or 0 */ client_ptr_t client_ptr; /* pointer to class in client address space */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11123
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/win32u/class.c | 10 ++++++---- server/class.c | 4 ++++ server/protocol.def | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index 06c9dcc4600..19663e6dce9 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -676,6 +676,7 @@ ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *nam req->client_ptr = wine_server_client_ptr( class ); req->atom = wine_server_add_atom( req, name ); req->fnid = fnid; + req->ansi = ansi; req->name_offset = version->Length / sizeof(WCHAR); ret = !wine_server_call_err( req ); locator = reply->locator; @@ -925,7 +926,7 @@ INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name ) } /* Set class info with the wine server. */ -static BOOL server_set_class_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size, ULONG_PTR *oldval ) +static BOOL server_set_class_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size, ULONG_PTR *oldval, BOOL ansi ) { BOOL ret; @@ -935,6 +936,7 @@ static BOOL server_set_class_info( HWND hwnd, INT offset, LONG_PTR newval, UINT req->offset = offset; req->size = size; req->new_info = newval; + req->ansi = ansi; ret = !wine_server_call_err( req ); *oldval = reply->old_info; } @@ -959,18 +961,18 @@ static ULONG_PTR set_class_long_size( HWND hwnd, INT offset, LONG_PTR newval, UI case GCLP_HICONSM: case GCLP_HMODULE: case GCLP_MENUNAME: - server_set_class_info( hwnd, offset, newval, size, &retval ); + server_set_class_info( hwnd, offset, newval, size, &retval, ansi ); break; case GCLP_WNDPROC: newval = (ULONG_PTR)alloc_winproc( (WNDPROC)newval, ansi ); - if (!server_set_class_info( hwnd, offset, newval, size, &retval )) break; + if (!server_set_class_info( hwnd, offset, newval, size, &retval, ansi )) break; retval = (ULONG_PTR)get_winproc( (WNDPROC)retval, ansi ); break; case GCL_CBCLSEXTRA: /* cannot change this one */ RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); break; default: - if (offset >= 0) server_set_class_info( hwnd, offset, newval, size, &retval ); + if (offset >= 0) server_set_class_info( hwnd, offset, newval, size, &retval, ansi ); else RtlSetLastWin32Error( ERROR_INVALID_INDEX ); break; } diff --git a/server/class.c b/server/class.c index 28e263b0395..8a2f56b4473 100644 --- a/server/class.c +++ b/server/class.c @@ -47,6 +47,7 @@ struct window_class atom_t atom; /* class atom for versioned class */ unsigned int fnid; /* builtin control FNID, or 0 */ client_ptr_t client_ptr; /* pointer to class in client address space */ + bool ansi; /* class wndproc is ansi */ class_shm_t *shared; /* class in session shared memory */ }; @@ -60,6 +61,7 @@ static struct window_class *create_class( struct process *process, int local, in class->process = (struct process *)grab_object( process ); class->count = 0; + class->ansi = false; if (!(class->shared = alloc_shared_object( offsetof(class_shm_t, extra[cls_extra]) ))) goto failed; @@ -252,6 +254,7 @@ DECL_HANDLER(create_class) class->atom = atom; class->fnid = req->fnid; class->client_ptr = req->client_ptr; + class->ansi = !!req->ansi; SHARED_WRITE_BEGIN( class->shared, class_shm_t ) { @@ -330,6 +333,7 @@ DECL_HANDLER(set_class_info) case GCLP_WNDPROC: reply->old_info = shared->info.wndproc; shared->info.wndproc = req->new_info; + class->ansi = !!req->ansi; break; case GCLP_HCURSOR: reply->old_info = shared->info.cursor; diff --git a/server/protocol.def b/server/protocol.def index 3adb403e9c1..9a105d2536c 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3298,6 +3298,7 @@ enum caret_state @REQ(create_class) atom_t atom; /* class atom */ unsigned int fnid; /* FNID for builtin classes, or 0 */ + unsigned int ansi; /* class wndproc is ansi */ client_ptr_t client_ptr; /* pointer to class in client address space */ data_size_t name_offset; /* base class name offset for specified atom */ VARARG(info,class_info); /* class info */ @@ -3326,6 +3327,7 @@ enum caret_state int offset; /* offset of the info */ data_size_t size; /* size of the info value to write */ lparam_t new_info; /* new class info value */ + unsigned int ansi; /* new class info is ansi */ @REPLY lparam_t old_info; /* previous class info value */ @END -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11123
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/user.exe16/message.c | 15 +++++++-------- dlls/win32u/class.c | 19 ++++++------------- dlls/win32u/ntuser_private.h | 3 +-- dlls/win32u/window.c | 2 +- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/dlls/user.exe16/message.c b/dlls/user.exe16/message.c index 584eba0c396..a8531f2f9a7 100644 --- a/dlls/user.exe16/message.c +++ b/dlls/user.exe16/message.c @@ -112,9 +112,9 @@ typedef struct } WINPROC_THUNK; #pragma pack(pop) -#define WINPROC_HANDLE (~0u >> 16) -#define MAX_WINPROCS32 4096 -#define MAX_WINPROCS16 1024 +#define MAKE_WNDPROC16(index) ((WNDPROC)(UINT_PTR)(UINT)MAKELONG(index, 0xffff)) +#define MAX_WINPROCS32 4096 +#define MAX_WINPROCS16 1024 static WNDPROC16 winproc16_array[MAX_WINPROCS16]; static unsigned int winproc16_used; @@ -140,7 +140,7 @@ static int winproc_to_index( WNDPROC16 handle ) else { index = LOWORD(handle); - if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) return -1; + if (handle != (WNDPROC16)MAKE_WNDPROC16(index)) return -1; /* check array limits */ if (index >= winproc16_used + MAX_WINPROCS32) return -1; } @@ -191,8 +191,7 @@ WNDPROC WINPROC_AllocProc16( WNDPROC16 func ) if (!func) return NULL; /* check if the function is already a win proc */ - if ((index = winproc_to_index( func )) != -1) - return (WNDPROC)(ULONG_PTR)(index | (WINPROC_HANDLE << 16)); + if ((index = winproc_to_index( func )) != -1) return MAKE_WNDPROC16(index); /* then check if we already have a winproc for that function */ for (index = 0; index < winproc16_used; index++) @@ -206,7 +205,7 @@ WNDPROC WINPROC_AllocProc16( WNDPROC16 func ) winproc16_array[winproc16_used++] = func; done: - ret = (WNDPROC)(ULONG_PTR)((index + MAX_WINPROCS32) | (WINPROC_HANDLE << 16)); + ret = MAKE_WNDPROC16(index + MAX_WINPROCS32); TRACE( "returning %p for %p/16-bit (%d/%d used)\n", ret, func, winproc16_used, MAX_WINPROCS16 ); return ret; @@ -221,7 +220,7 @@ WNDPROC16 WINPROC_GetProc16( WNDPROC proc, BOOL unicode ) { WNDPROC winproc = wow_handlers32.alloc_winproc( proc, unicode ); - if ((ULONG_PTR)winproc >> 16 != WINPROC_HANDLE) return (WNDPROC16)winproc; + if (winproc != MAKE_WNDPROC16(LOWORD(winproc))) return (WNDPROC16)winproc; return alloc_win16_thunk( winproc ); } diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index 19663e6dce9..1d474a1867b 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -202,18 +202,12 @@ static WINDOWPROC *find_winproc( WNDPROC func, BOOL ansi ) static WINDOWPROC *get_winproc_ptr( WNDPROC handle ) { UINT index = LOWORD(handle); - if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) return NULL; + if (handle != MAKE_WNDPROC(index)) return NULL; if (index >= MAX_WINPROCS) return WINPROC_PROC16; if (index >= winproc_used) return NULL; return &winproc_array[index]; } -/* create a handle for a given window proc */ -static inline WNDPROC proc_to_handle( WINDOWPROC *proc ) -{ - return (WNDPROC)(ULONG_PTR)((proc - winproc_array) | (WINPROC_HANDLE << 16)); -} - /* allocate and initialize a new winproc */ static inline WINDOWPROC *alloc_winproc_ptr( WNDPROC func, BOOL ansi ) { @@ -233,13 +227,12 @@ static inline WINDOWPROC *alloc_winproc_ptr( WNDPROC func, BOOL ansi ) proc = &winproc_array[winproc_used++]; if (ansi) proc->procA = func; else proc->procW = func; - TRACE_(win)( "allocated %p for %c %p (%d/%d used)\n", - proc_to_handle(proc), ansi ? 'A' : 'W', func, - winproc_used, MAX_WINPROCS ); + TRACE_(win)( "allocated %p for %c %p (%d/%d used)\n", MAKE_WNDPROC(proc - winproc_array), + ansi ? 'A' : 'W', func, winproc_used, MAX_WINPROCS ); } else WARN_(win)( "too many winprocs, cannot allocate one for %p\n", func ); } - else TRACE_(win)( "reusing %p for %p\n", proc_to_handle(proc), func ); + else TRACE_(win)( "reusing %p for %p\n", MAKE_WNDPROC(proc - winproc_array), func ); pthread_mutex_unlock( &winproc_lock ); return proc; @@ -260,7 +253,7 @@ WNDPROC alloc_winproc( WNDPROC func, BOOL ansi ) if (!(proc = alloc_winproc_ptr( func, ansi ))) return func; if (proc == WINPROC_PROC16) return func; - return proc_to_handle( proc ); + return MAKE_WNDPROC(proc - winproc_array); } /* Get a window procedure pointer that can be passed to the Windows program. */ @@ -1112,7 +1105,7 @@ static void register_builtin( enum ntuser_client_procs proc ) .style = descr->style, .cbWndExtra = descr->extra, .hbrBackground = descr->brush, - .lpfnWndProc = BUILTIN_WINPROC( proc ), + .lpfnWndProc = MAKE_WNDPROC( proc ), }; if (descr->atom) return; /* already registered */ diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 0c7f4802351..e066ef4b5d6 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -152,8 +152,7 @@ struct scroll_info BOOL painted; /* Whether the scroll bar is painted by DefWinProc() */ }; -#define WINPROC_HANDLE (~0u >> 16) -#define BUILTIN_WINPROC(index) ((WNDPROC)(ULONG_PTR)((index) | (WINPROC_HANDLE << 16))) +#define MAKE_WNDPROC(index) ((WNDPROC)(UINT_PTR)(UINT)MAKELONG(index, 0xffff)) #define MAX_ATOM_LEN 255 diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 7afd444a027..70204bf4adb 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -1248,7 +1248,7 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests * that the hack is in GetWindowLongPtr[AW], not in winprocs. */ - if (win->winproc == BUILTIN_WINPROC(NTUSER_WNDPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE))) + if (win->winproc == MAKE_WNDPROC(NTUSER_WNDPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE))) retval = (ULONG_PTR)win->winproc; else retval = (ULONG_PTR)get_winproc( win->winproc, ansi ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11123
From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/win32u/class.c | 27 ------- dlls/win32u/message.c | 6 +- dlls/win32u/ntuser_private.h | 6 +- dlls/win32u/window.c | 135 +++++++++++++++-------------------- server/class.c | 13 ++++ server/protocol.def | 8 ++- server/user.h | 1 + server/window.c | 23 +++--- 8 files changed, 93 insertions(+), 126 deletions(-) diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index 1d474a1867b..24f7e64b46b 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -274,17 +274,6 @@ WNDPROC get_winproc( WNDPROC proc, BOOL ansi ) } } -/* Return the window procedure type, or the default value if not a winproc handle. */ -BOOL is_winproc_unicode( WNDPROC proc, BOOL def_val ) -{ - WINDOWPROC *ptr = get_winproc_ptr( proc ); - - if (!ptr) return def_val; - if (ptr == WINPROC_PROC16) return FALSE; /* 16-bit is always A */ - if (ptr->procA && ptr->procW) return def_val; /* can be both */ - return ptr->procW != NULL; -} - void get_winproc_params( struct win_proc_params *params, BOOL fixup_ansi_dst ) { WINDOWPROC *proc = get_winproc_ptr( params->func ); @@ -572,22 +561,6 @@ static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name ) return NULL; } -/*********************************************************************** - * get_class_winproc - */ -WNDPROC get_class_winproc( CLASS *class ) -{ - struct object_lock lock = OBJECT_LOCK_INIT; - const class_shm_t *class_shm; - WNDPROC wndproc = NULL; - NTSTATUS status; - - while ((status = get_shared_class( class, &lock, &class_shm )) == STATUS_PENDING) - wndproc = wine_server_get_ptr( class_shm->info.wndproc ); - if (status) return 0; - return wndproc; -} - /*********************************************************************** * get_class_dce */ diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 1428ff586f4..b6cdd3efa0e 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -434,7 +434,7 @@ static BOOL init_win_proc_params( struct win_proc_params *params, HWND hwnd, UIN static BOOL init_window_call_params( struct win_proc_params *params, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL ansi, enum wm_char_mapping mapping ) { - BOOL is_dialog; + BOOL is_dialog, is_ansi; WND *win; user_check_not_lock(); @@ -442,8 +442,8 @@ static BOOL init_window_call_params( struct win_proc_params *params, HWND hwnd, if (!is_current_thread_window( hwnd )) return FALSE; if (!(win = get_win_ptr( hwnd ))) return FALSE; if (win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE; - params->func = win->winproc; - params->ansi_dst = !(win->flags & WIN_ISUNICODE); + params->func = get_window_wndproc_handle( hwnd, &is_ansi ); + params->ansi_dst = is_ansi; is_dialog = win->dlgInfo != NULL; release_win_ptr( win ); diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index e066ef4b5d6..978700a37aa 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -47,7 +47,6 @@ typedef struct tagWND HWND owner; /* Window owner */ struct tagCLASS *class; /* Window class */ struct dce *dce; /* DCE pointer */ - WNDPROC winproc; /* Window procedure */ struct window_rects rects; /* window rects in window DPI, relative to the parent client area */ RECT normal_rect; /* Normal window rect saved when maximized/minimized */ RECT present_rect; /* present rect for exclusive fullscreen mode */ @@ -79,7 +78,6 @@ typedef struct tagWND #define WIN_NEED_SIZE 0x0002 /* Internal WM_SIZE is needed */ #define WIN_NCACTIVATED 0x0004 /* last WM_NCACTIVATE was positive */ #define WIN_ISMDICLIENT 0x0008 /* Window is an MDIClient */ -#define WIN_ISUNICODE 0x0010 /* Window is Unicode */ #define WIN_NEEDS_SHOW_OWNEDPOPUP 0x0020 /* WM_SHOWWINDOW:SC_SHOW must be sent in the next ShowOwnedPopup call */ #define WIN_CHILDREN_MOVED 0x0040 /* children may have moved, ignore stored positions */ #define WIN_HAS_IME_WIN 0x0080 /* the window has been registered with imm32 */ @@ -152,6 +150,7 @@ struct scroll_info BOOL painted; /* Whether the scroll bar is painted by DefWinProc() */ }; +/* also defined in server/class.c */ #define MAKE_WNDPROC(index) ((WNDPROC)(UINT_PTR)(UINT)MAKELONG(index, 0xffff)) #define MAX_ATOM_LEN 255 @@ -172,9 +171,7 @@ extern void spy_exit_message( INT flag, HWND hwnd, UINT msg, /* class.c */ extern HINSTANCE user32_module; WNDPROC alloc_winproc( WNDPROC func, BOOL ansi ); -BOOL is_winproc_unicode( WNDPROC proc, BOOL def_val ); DWORD get_class_long( HWND hwnd, INT offset, BOOL ansi ); -WNDPROC get_class_winproc( struct tagCLASS *class ); ULONG_PTR get_class_long_ptr( HWND hwnd, INT offset, BOOL ansi ); WORD get_class_word( HWND hwnd, INT offset ); DLGPROC get_dialog_proc( DLGPROC proc, BOOL ansi ); @@ -235,5 +232,6 @@ struct obj_locator get_window_class_locator( HWND hwnd ); WND *get_win_ptr( HWND hwnd ); BOOL is_child( HWND parent, HWND child ); BOOL is_window( HWND hwnd ); +extern WNDPROC get_window_wndproc_handle( HWND hwnd, BOOL *ansi ); #endif /* __WINE_NTUSER_PRIVATE_H */ diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 70204bf4adb..c3a32dde6cf 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -185,6 +185,25 @@ struct obj_locator get_window_class_locator( HWND hwnd ) return locator; } +/*********************************************************************** + * get_window_wndproc_handle + */ +WNDPROC get_window_wndproc_handle( HWND hwnd, BOOL *ansi ) +{ + struct object_lock lock = OBJECT_LOCK_INIT; + const window_shm_t *window_shm = NULL; + WNDPROC wndproc = NULL; + NTSTATUS status; + + while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING) + { + wndproc = wine_server_get_ptr( window_shm->info.wndproc ); + *ansi = window_shm->ansi; + } + if (status) return 0; + return wndproc; +} + /*********************************************************************** * get_user_handle_ptr */ @@ -980,28 +999,9 @@ BOOL is_window_drawable( HWND hwnd, BOOL icon ) /* see IsWindowUnicode */ BOOL is_window_unicode( HWND hwnd ) { - WND *win; - BOOL ret = FALSE; - - if (!(win = get_win_ptr(hwnd))) return FALSE; - - if (win == WND_DESKTOP) return TRUE; - - if (win != WND_OTHER_PROCESS) - { - ret = (win->flags & WIN_ISUNICODE) != 0; - release_win_ptr( win ); - } - else - { - SERVER_START_REQ( get_window_info ) - { - req->handle = wine_server_user_handle( hwnd ); - if (!wine_server_call_err( req )) ret = reply->is_unicode; - } - SERVER_END_REQ; - } - return ret; + BOOL ansi = FALSE; + if (!get_window_wndproc_handle( hwnd, &ansi )) return FALSE; + return !ansi; } /***************************************************************** @@ -1113,14 +1113,30 @@ static HWND get_last_active_popup( HWND hwnd ) return retval; } -static LONG_PTR get_window_long_shm( HWND hwnd, UINT offset, UINT size, BOOL internal ) +static WNDPROC get_window_proc( WNDPROC wndproc, BOOL wndproc_ansi, BOOL ansi ) +{ + /* This looks like a hack only for the edit control (see tests). This makes these controls + * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests + * that the hack is in GetWindowLongPtr[AW], not in winprocs. + */ + if (wndproc == MAKE_WNDPROC(NTUSER_WNDPROC_EDIT) && wndproc_ansi != ansi) return wndproc; + return get_winproc( wndproc, ansi ); +} + +static LONG_PTR get_window_long_shm( HWND hwnd, UINT offset, UINT size, BOOL ansi, BOOL internal ) { struct object_lock lock = OBJECT_LOCK_INIT; const window_shm_t *window_shm = NULL; - BOOL valid = TRUE; + BOOL valid = TRUE, window_ansi = FALSE; LONG_PTR ret = 0; NTSTATUS status; + if (offset == GWLP_WNDPROC && !is_current_process_window( hwnd )) + { + RtlSetLastWin32Error( ERROR_ACCESS_DENIED ); + return 0; + } + while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING) { switch (offset) @@ -1128,12 +1144,14 @@ static LONG_PTR get_window_long_shm( HWND hwnd, UINT offset, UINT size, BOOL int case GWLP_ID: ret = window_shm->info.id; break; case GWLP_HINSTANCE: ret = window_shm->info.instance; break; case GWLP_USERDATA: memcpy( &ret, (void *)&window_shm->info.user_data, size ); break; + case GWLP_WNDPROC: ret = window_shm->info.wndproc; break; default: valid = size <= window_shm->extra_size && offset <= window_shm->extra_size - size && (internal || offset >= window_shm->private_size); if (valid) memcpy( &ret, (char *)window_shm->extra + offset, size ); break; } + window_ansi = window_shm->ansi; } if (status) { @@ -1147,6 +1165,7 @@ static LONG_PTR get_window_long_shm( HWND hwnd, UINT offset, UINT size, BOOL int return 0; } + if (offset == GWLP_WNDPROC) return (ULONG_PTR)get_window_proc( (WNDPROC)ret, window_ansi, ansi ); return ret; } @@ -1184,7 +1203,8 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans case GWLP_ID: case GWLP_HINSTANCE: case GWLP_USERDATA: - return get_window_long_shm( hwnd, offset, size, internal ); + case GWLP_WNDPROC: + return get_window_long_shm( hwnd, offset, size, ansi, internal ); case GWLP_HWNDPARENT: { HWND parent = NtUserGetAncestor( hwnd, GA_PARENT ); @@ -1211,9 +1231,6 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans return retval; case GWL_EXSTYLE: return 0; - case GWLP_WNDPROC: - RtlSetLastWin32Error( ERROR_ACCESS_DENIED ); - return 0; } RtlSetLastWin32Error( ERROR_INVALID_INDEX ); return 0; @@ -1221,11 +1238,6 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans if (win == WND_OTHER_PROCESS) { - if (offset == GWLP_WNDPROC) - { - RtlSetLastWin32Error( ERROR_ACCESS_DENIED ); - return 0; - } SERVER_START_REQ( get_window_info ) { req->handle = wine_server_user_handle( hwnd ); @@ -1243,20 +1255,6 @@ static LONG_PTR get_window_long_size( HWND hwnd, INT offset, UINT size, BOOL ans { case GWL_STYLE: retval = win->dwStyle; break; case GWL_EXSTYLE: retval = win->dwExStyle; break; - case GWLP_WNDPROC: - /* This looks like a hack only for the edit control (see tests). This makes these controls - * more tolerant to A/W mismatches. The lack of W->A->W conversion for such a mismatch suggests - * that the hack is in GetWindowLongPtr[AW], not in winprocs. - */ - if (win->winproc == MAKE_WNDPROC(NTUSER_WNDPROC_EDIT) && (!!ansi != !(win->flags & WIN_ISUNICODE))) - retval = (ULONG_PTR)win->winproc; - else - retval = (ULONG_PTR)get_winproc( win->winproc, ansi ); - break; - default: - WARN("Unknown offset %d\n", offset ); - RtlSetLastWin32Error( ERROR_INVALID_INDEX ); - break; } release_win_ptr( win ); return retval; @@ -1291,7 +1289,8 @@ static WORD get_window_word( HWND hwnd, INT offset ) } /* Set window info with the wine server. */ -static BOOL server_set_window_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size, LONG_PTR *oldval, BOOL internal ) +static BOOL server_set_window_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size, + LONG_PTR *oldval, BOOL *ansi, BOOL internal ) { BOOL ret; @@ -1301,9 +1300,11 @@ static BOOL server_set_window_info( HWND hwnd, INT offset, LONG_PTR newval, UINT req->offset = offset; req->size = size; req->new_info = newval; + req->new_ansi = *ansi; req->internal = internal; ret = !wine_server_call_err( req ); *oldval = reply->old_info; + *ansi = reply->old_ansi; } SERVER_END_REQ; return ret; @@ -1311,7 +1312,7 @@ static BOOL server_set_window_info( HWND hwnd, INT offset, LONG_PTR newval, UINT UINT set_window_style_bits( HWND hwnd, UINT set_bits, UINT clear_bits ) { - BOOL ok, made_visible = FALSE; + BOOL ok, ansi = FALSE, made_visible = FALSE; STYLESTRUCT style; LONG_PTR oldval; WND *win = get_win_ptr( hwnd ); @@ -1330,7 +1331,7 @@ UINT set_window_style_bits( HWND hwnd, UINT set_bits, UINT clear_bits ) release_win_ptr( win ); return style.styleNew; } - if ((ok = server_set_window_info( hwnd, GWL_STYLE, style.styleNew, 0, &oldval, FALSE ))) + if ((ok = server_set_window_info( hwnd, GWL_STYLE, style.styleNew, 0, &oldval, &ansi, FALSE ))) { style.styleOld = oldval; win->dwStyle = style.styleNew; @@ -1403,7 +1404,7 @@ static HWND set_window_owner( HWND hwnd, HWND owner ) static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size, LONG_PTR newval, BOOL ansi, BOOL internal ) { - BOOL ok, made_visible = FALSE, layered = FALSE; + BOOL ok, made_visible = FALSE, layered = FALSE, old_ansi = ansi; LONG_PTR retval = 0, oldval; STYLESTRUCT style; WND *win; @@ -1429,11 +1430,6 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size, } if (win == WND_OTHER_PROCESS) { - if (offset == GWLP_WNDPROC) - { - RtlSetLastWin32Error( ERROR_ACCESS_DENIED ); - return 0; - } if (offset > 32767 || offset < -32767) { RtlSetLastWin32Error( ERROR_INVALID_INDEX ); @@ -1479,27 +1475,11 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size, return (ULONG_PTR)NtUserSetParent( hwnd, (HWND)newval ); } case GWLP_WNDPROC: - { - WNDPROC proc; - UINT old_flags = win->flags; - retval = get_window_long_ptr( hwnd, offset, ansi ); - proc = alloc_winproc( (WNDPROC)newval, ansi ); - if (proc) win->winproc = proc; - if (is_winproc_unicode( proc, !ansi )) win->flags |= WIN_ISUNICODE; - else win->flags &= ~WIN_ISUNICODE; - if (!((old_flags ^ win->flags) & WIN_ISUNICODE)) - { - release_win_ptr( win ); - return retval; - } - /* update is_unicode flag on the server side */ + newval = (ULONG_PTR)alloc_winproc( (WNDPROC)newval, ansi ); break; } - } - - if (offset == GWLP_WNDPROC) newval = !!(win->flags & WIN_ISUNICODE); - if ((ok = server_set_window_info( hwnd, offset, newval, size, &oldval, internal ))) + if ((ok = server_set_window_info( hwnd, offset, newval, size, &oldval, &old_ansi, internal ))) { switch (offset) { @@ -1512,8 +1492,6 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size, win->dwExStyle = newval; retval = oldval; break; - case GWLP_WNDPROC: - break; default: retval = oldval; break; @@ -1539,6 +1517,7 @@ static LONG_PTR set_window_long_internal( HWND hwnd, INT offset, UINT size, send_message( hwnd, WM_STYLECHANGED, offset, (LPARAM)&style ); } + if (offset == GWLP_WNDPROC) return (ULONG_PTR)get_window_proc( (WNDPROC)retval, old_ansi, ansi ); return retval; } @@ -5511,6 +5490,7 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name, req->dpi_context = dpi_context; req->style = style; req->ex_style = ex_style; + req->ansi = ansi; req->atom = wine_server_add_atom( req, name ); if (!wine_server_call_err( req )) { @@ -5564,9 +5544,7 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name, win->parent = full_parent; win->owner = full_owner; win->class = class; - win->winproc = get_class_winproc( class ); set_user_handle_ptr( handle, win ); - if (is_winproc_unicode( win->winproc, !ansi )) win->flags |= WIN_ISUNICODE; return win; } @@ -5805,7 +5783,6 @@ HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name, req->handle = wine_server_user_handle( hwnd ); req->style = win->dwStyle; req->ex_style = win->dwExStyle; - req->is_unicode = (win->flags & WIN_ISUNICODE) != 0; wine_server_call( req ); } SERVER_END_REQ; diff --git a/server/class.c b/server/class.c index 8a2f56b4473..daaa344f00a 100644 --- a/server/class.c +++ b/server/class.c @@ -38,6 +38,9 @@ #include "winuser.h" #include "winternl.h" +/* also defined in win32u/ntuser_private.h */ +#define MAKE_WNDPROC(index) ((UINT_PTR)(UINT)MAKELONG(index, 0xffff)) + struct window_class { struct list entry; /* entry in process list */ @@ -172,6 +175,16 @@ unsigned int get_class_fnid( struct window_class *class, data_size_t *extra_size return class->fnid; } +client_ptr_t get_class_wndproc( struct window_class *class, bool *is_ansi ) +{ + client_ptr_t wndproc = class->shared->info.wndproc; + UINT index = LOWORD(wndproc); + + /* builtin wndproc can be both ansi or unicode, otherwise use the class wndproc kind */ + if (wndproc != MAKE_WNDPROC(index) || index >= NTUSER_NB_PROCS) *is_ansi = !!class->ansi; + return wndproc; +} + client_ptr_t get_class_client_ptr( struct window_class *class ) { return class->client_ptr; diff --git a/server/protocol.def b/server/protocol.def index 9a105d2536c..7b467618f7a 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1068,6 +1068,7 @@ struct window_info lparam_t id; /* window id */ mod_handle_t instance; /* creator instance */ lparam_t user_data; /* user-specific data */ + client_ptr_t wndproc; /* window proc */ }; typedef volatile struct @@ -1075,6 +1076,8 @@ typedef volatile struct struct obj_locator class; /* object locator for the window class shared object */ unsigned int dpi_context; /* DPI awareness context */ unsigned int fnid; /* builtin class FNID, or 0 */ + unsigned int ansi; /* window wndproc is ansi */ + int __pad; data_size_t private_size; /* length of private extra bytes range */ data_size_t extra_size; /* size of the extra info */ struct window_info info; /* window info (GWLP_*) */ @@ -2637,6 +2640,7 @@ enum message_type unsigned int dpi_context; /* thread DPI context */ unsigned int style; /* window style */ unsigned int ex_style; /* window extended style */ + unsigned int ansi; /* window created as ansi */ VARARG(class,unicode_str); /* class name */ @REPLY user_handle_t handle; /* created window */ @@ -2678,7 +2682,6 @@ enum message_type data_size_t size; /* size of the info value to read */ @REPLY user_handle_t last_active; /* last active popup */ - int is_unicode; /* ANSI or unicode */ lparam_t info; /* current window info value */ @END @@ -2688,7 +2691,6 @@ enum message_type user_handle_t handle; /* handle to the window */ unsigned int style; /* window style */ unsigned int ex_style; /* window extended style */ - short int is_unicode; /* ANSI or unicode */ @REPLY @END @@ -2699,9 +2701,11 @@ enum message_type int offset; /* offset of the info */ data_size_t size; /* size of the info value to write */ lparam_t new_info; /* new window info value */ + unsigned int new_ansi; /* new window info is ansi */ unsigned int internal; /* set internal control info */ @REPLY lparam_t old_info; /* previous window info value */ + unsigned int old_ansi; /* previous window info is ansi */ @END diff --git a/server/user.h b/server/user.h index f527db3e57c..1d16e2aab95 100644 --- a/server/user.h +++ b/server/user.h @@ -195,6 +195,7 @@ extern int is_message_class( struct window_class *class ); extern int get_class_style( struct window_class *class ); extern atom_t get_class_atom( struct window_class *class ); extern unsigned int get_class_fnid( struct window_class *class, data_size_t *extra_size, data_size_t *private_size ); +extern client_ptr_t get_class_wndproc( struct window_class *class, bool *ansi ); extern client_ptr_t get_class_client_ptr( struct window_class *class ); /* windows station functions */ diff --git a/server/window.c b/server/window.c index 9665af1069f..6bff48e7a22 100644 --- a/server/window.c +++ b/server/window.c @@ -74,7 +74,6 @@ struct window struct region *update_region; /* update region (relative to window rect) */ unsigned int style; /* window style */ unsigned int ex_style; /* window extended style */ - unsigned int is_unicode : 1; /* ANSI or unicode */ unsigned int is_linked : 1; /* is it linked into the parent z-order list? */ unsigned int is_layered : 1; /* has layered info been set? */ unsigned int is_orphan : 1; /* is window orphaned */ @@ -605,7 +604,7 @@ void post_desktop_message( struct desktop *desktop, unsigned int message, /* create a new window structure (note: the window is not linked in the window tree) */ static struct window *create_window( struct window *parent, struct window *owner, atom_t atom, - mod_handle_t class_instance ) + mod_handle_t class_instance, bool ansi ) { data_size_t extra_size, private_size; struct window *win = NULL; @@ -655,7 +654,6 @@ static struct window *create_window( struct window *parent, struct window *owner win->update_region = NULL; win->style = 0; win->ex_style = 0; - win->is_unicode = 1; win->is_linked = 0; win->is_layered = 0; win->is_orphan = 0; @@ -682,6 +680,8 @@ static struct window *create_window( struct window *parent, struct window *owner shared->extra_size = extra_size; memset( (void *)&shared->info, 0, sizeof(shared->info) ); memset( (void *)shared->extra, 0, extra_size ); + shared->info.wndproc = get_class_wndproc( win->class, &ansi ); + shared->ansi = ansi; } SHARED_WRITE_END; @@ -2235,7 +2235,7 @@ DECL_HANDLER(create_window) if (!atom) atom = find_atom( table, &cls_name ); - if (!(win = create_window( parent, owner, atom, req->class_instance ))) return; + if (!(win = create_window( parent, owner, atom, req->class_instance, !!req->ansi ))) return; if (parent && !is_desktop_window( parent )) dpi_context = parent->shared->dpi_context; @@ -2333,7 +2333,7 @@ DECL_HANDLER(get_desktop_window) if (!desktop->top_window && req->force) /* create it */ { - if ((desktop->top_window = create_window( NULL, NULL, DESKTOP_ATOM, 0 ))) + if ((desktop->top_window = create_window( NULL, NULL, DESKTOP_ATOM, 0, false ))) { detach_window_thread( desktop->top_window ); desktop->top_window->style = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; @@ -2346,7 +2346,7 @@ DECL_HANDLER(get_desktop_window) static const struct unicode_str name = { messageW, sizeof(messageW) }; struct atom_table *table = get_user_atom_table(); atom_t atom = add_atom( table, &name ); - if (atom && (desktop->msg_window = create_window( NULL, NULL, atom, 0 ))) + if (atom && (desktop->msg_window = create_window( NULL, NULL, atom, 0, false ))) { detach_window_thread( desktop->msg_window ); desktop->msg_window->style = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; @@ -2396,14 +2396,12 @@ DECL_HANDLER(get_window_info) if (!(win = get_window( req->handle ))) return; reply->last_active = win->handle; - reply->is_unicode = win->is_unicode; if (get_user_object( win->last_active, NTUSER_OBJ_WINDOW )) reply->last_active = win->last_active; switch (req->offset) { case GWL_STYLE: reply->info = win->style; break; case GWL_EXSTYLE: reply->info = win->ex_style; break; - case GWLP_WNDPROC: reply->info = win->is_unicode; break; default: if (req->size) set_win32_error( ERROR_INVALID_INDEX ); break; @@ -2419,7 +2417,6 @@ DECL_HANDLER(init_window_info) if (!(win = get_window( req->handle ))) return; win->style = req->style; win->ex_style = req->ex_style; - win->is_unicode = req->is_unicode; /* changing window style triggers a non-client paint */ win->paint_flags |= PAINT_NONCLIENT; @@ -2430,6 +2427,7 @@ DECL_HANDLER(init_window_info) DECL_HANDLER(set_window_info) { struct window *win; + bool ansi; if (!(win = get_window( req->handle ))) return; if (is_desktop_window( win ) && win->thread != current) @@ -2462,8 +2460,11 @@ DECL_HANDLER(set_window_info) shared->info.instance = req->new_info; break; case GWLP_WNDPROC: - reply->old_info = win->is_unicode; - win->is_unicode = req->new_info; + reply->old_info = shared->info.wndproc; + reply->old_ansi = shared->ansi; + if (req->new_info) shared->info.wndproc = req->new_info; + else shared->info.wndproc = get_class_wndproc( win->class, &ansi ); + shared->ansi = req->new_ansi; /* class ansi is actually ignored */ break; case GWLP_USERDATA: reply->old_info = shared->info.user_data; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11123
v2: Fix some struct padding, initialize desktop/message window proc as well in create_window. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11123#note_142826
participants (2)
-
Rémi Bernon -
Rémi Bernon (@rbernon)