wine/dlls/win32u/class.c
2025-08-27 12:22:09 +02:00

1197 lines
37 KiB
C

/*
* Window classes functions
*
* Copyright 1993, 1996, 2003 Alexandre Julliard
* Copyright 1995 Martin von Loewis
* Copyright 1998 Juergen Schmied (jsch)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include <pthread.h>
#include <assert.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "win32u_private.h"
#include "ntuser_private.h"
#include "wine/server.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(class);
WINE_DECLARE_DEBUG_CHANNEL(win);
SYSTEM_BASIC_INFORMATION system_info;
#define MAX_ATOM_LEN 255
#define IS_INTATOM(x) (((ULONG_PTR)(x) >> 16) == 0)
#define MAX_WINPROCS 4096
#define WINPROC_PROC16 ((void *)1) /* placeholder for 16-bit window procs */
typedef struct tagCLASS
{
struct list entry; /* Entry in class list */
UINT style; /* Class style */
BOOL local; /* Local class? */
WNDPROC winproc; /* Window procedure */
INT cbClsExtra; /* Class extra bytes */
INT cbWndExtra; /* Window extra bytes */
struct dce *dce; /* Opaque pointer to class DCE */
UINT_PTR instance; /* Module that created the task */
HICON hIcon; /* Default icon */
HICON hIconSm; /* Default small icon */
HICON hIconSmIntern; /* Internal small icon, derived from hIcon */
HCURSOR hCursor; /* Default cursor */
HBRUSH hbrBackground; /* Default background */
ATOM atom; /* name of the class */
WCHAR name[MAX_ATOM_LEN + 1];
WCHAR *basename; /* Base name for redirected classes, pointer within 'name'. */
struct client_menu_name menu_name; /* Default menu name */
} CLASS;
/* Built-in class descriptor */
struct builtin_class_descr
{
const char *name; /* class name */
UINT style; /* class style */
INT extra; /* window extra bytes */
ULONG_PTR cursor; /* cursor id */
HBRUSH brush; /* brush or system color */
enum ntuser_client_procs proc;
};
typedef struct tagWINDOWPROC
{
WNDPROC procA; /* ANSI window proc */
WNDPROC procW; /* Unicode window proc */
} WINDOWPROC;
static WINDOWPROC winproc_array[MAX_WINPROCS];
static UINT winproc_used = NTUSER_NB_PROCS;
static pthread_mutex_t winproc_lock = PTHREAD_MUTEX_INITIALIZER;
static struct list class_list = LIST_INIT( class_list );
HINSTANCE user32_module = 0;
/* find an existing winproc for a given function and type */
/* FIXME: probably should do something more clever than a linear search */
static WINDOWPROC *find_winproc( WNDPROC func, BOOL ansi )
{
unsigned int i;
for (i = 0; i < NTUSER_NB_PROCS; i++)
{
/* match either proc, some apps confuse A and W */
if (winproc_array[i].procA != func && winproc_array[i].procW != func) continue;
return &winproc_array[i];
}
for ( ; i < winproc_used; i++)
{
if (ansi && winproc_array[i].procA != func) continue;
if (!ansi && winproc_array[i].procW != func) continue;
return &winproc_array[i];
}
return NULL;
}
/* return the window proc for a given handle, or NULL for an invalid handle,
* or WINPROC_PROC16 for a handle to a 16-bit proc. */
static WINDOWPROC *get_winproc_ptr( WNDPROC handle )
{
UINT index = LOWORD(handle);
if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) 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 )
{
WINDOWPROC *proc;
/* check if the function is already a win proc */
if (!func) return NULL;
if ((proc = get_winproc_ptr( func ))) return proc;
pthread_mutex_lock( &winproc_lock );
/* check if we already have a winproc for that function */
if (!(proc = find_winproc( func, ansi )))
{
if (winproc_used < MAX_WINPROCS)
{
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 );
}
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 );
pthread_mutex_unlock( &winproc_lock );
return proc;
}
/**********************************************************************
* alloc_winproc
*
* Allocate a window procedure for a window or class.
*
* Note that allocated winprocs are never freed; the idea is that even if an app creates a
* lot of windows, it will usually only have a limited number of window procedures, so the
* array won't grow too large, and this way we avoid the need to track allocations per window.
*/
WNDPROC alloc_winproc( WNDPROC func, BOOL ansi )
{
WINDOWPROC *proc;
if (!(proc = alloc_winproc_ptr( func, ansi ))) return func;
if (proc == WINPROC_PROC16) return func;
return proc_to_handle( proc );
}
/* Get a window procedure pointer that can be passed to the Windows program. */
WNDPROC get_winproc( WNDPROC proc, BOOL ansi )
{
WINDOWPROC *ptr = get_winproc_ptr( proc );
if (!ptr || ptr == WINPROC_PROC16) return proc;
if (ansi)
{
if (ptr->procA) return ptr->procA;
return proc;
}
else
{
if (ptr->procW) return ptr->procW;
return proc;
}
}
/* 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 );
if (!proc)
{
params->procW = params->procA = NULL;
}
else if (proc == WINPROC_PROC16)
{
params->procW = params->procA = WINPROC_PROC16;
}
else
{
params->procA = proc->procA;
params->procW = proc->procW;
if (fixup_ansi_dst)
{
if (params->ansi)
{
if (params->procA) params->ansi_dst = TRUE;
else if (params->procW) params->ansi_dst = FALSE;
}
else
{
if (params->procW) params->ansi_dst = FALSE;
else if (params->procA) params->ansi_dst = TRUE;
}
}
}
if (!params->procA) params->procA = params->func;
if (!params->procW) params->procW = params->func;
}
DLGPROC get_dialog_proc( DLGPROC ret, BOOL ansi )
{
WINDOWPROC *proc;
if (!(proc = get_winproc_ptr( (WNDPROC)ret ))) return ret;
if (proc == WINPROC_PROC16) return WINPROC_PROC16;
return (DLGPROC)(ansi ? proc->procA : proc->procW);
}
static void init_user(void)
{
NtQuerySystemInformation( SystemBasicInformation, &system_info, sizeof(system_info), NULL );
shared_session_init();
gdi_init();
sysparams_init();
winstation_init();
register_desktop_class();
}
/***********************************************************************
* NtUserInitializeClientPfnArrays (win32u.@)
*/
NTSTATUS WINAPI NtUserInitializeClientPfnArrays( const ntuser_client_func_ptr *client_procsA,
const ntuser_client_func_ptr *client_procsW,
const ntuser_client_func_ptr *client_workers,
HINSTANCE user_module )
{
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
UINT i;
for (i = 0; i < NTUSER_NB_PROCS; i++)
{
winproc_array[i].procA = client_procsA[i][0];
winproc_array[i].procW = client_procsW[i][0];
}
user32_module = user_module;
pthread_once( &init_once, init_user );
return STATUS_SUCCESS;
}
/***********************************************************************
* get_int_atom_value
*/
static ATOM get_int_atom_value( UNICODE_STRING *name )
{
const WCHAR *ptr = name->Buffer;
const WCHAR *end = ptr + name->Length / sizeof(WCHAR);
UINT ret = 0;
if (IS_INTRESOURCE(ptr)) return LOWORD(ptr);
if (*ptr++ != '#') return 0;
while (ptr < end)
{
if (*ptr < '0' || *ptr > '9') return 0;
ret = ret * 10 + *ptr++ - '0';
if (ret >= MAXINTATOM) return 0;
}
return ret;
}
atom_t wine_server_add_atom( void *req, UNICODE_STRING *str )
{
atom_t atom;
if (!(atom = get_int_atom_value( str ))) wine_server_add_data( req, str->Buffer, str->Length );
return atom;
}
BOOL is_desktop_class( UNICODE_STRING *name )
{
static const WCHAR desktopW[] = {'#','3','2','7','6','9'};
return name->Length == sizeof(desktopW) && !wcsnicmp( name->Buffer, desktopW, ARRAY_SIZE(desktopW) );
}
BOOL is_message_class( UNICODE_STRING *name )
{
static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
return name->Length == sizeof(messageW) && !wcsnicmp( name->Buffer, messageW, ARRAY_SIZE(messageW) );
}
static unsigned int is_integral_atom( const WCHAR *atomstr, ULONG len, RTL_ATOM *ret_atom )
{
RTL_ATOM atom;
if ((ULONG_PTR)atomstr >> 16)
{
const WCHAR* ptr = atomstr;
if (!len) return STATUS_OBJECT_NAME_INVALID;
if (*ptr++ == '#')
{
atom = 0;
while (ptr < atomstr + len && *ptr >= '0' && *ptr <= '9')
{
atom = atom * 10 + *ptr++ - '0';
}
if (ptr > atomstr + 1 && ptr == atomstr + len) goto done;
}
if (len > MAX_ATOM_LEN) return STATUS_INVALID_PARAMETER;
return STATUS_MORE_ENTRIES;
}
else if ((atom = LOWORD( atomstr )) >= MAXINTATOM) return STATUS_INVALID_PARAMETER;
done:
if (atom >= MAXINTATOM) atom = 0;
if (!(*ret_atom = atom)) return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;
}
static ULONG integral_atom_name( WCHAR *buffer, ULONG len, RTL_ATOM atom )
{
char tmp[16];
int ret = snprintf( tmp, sizeof(tmp), "#%u", atom );
len /= sizeof(WCHAR);
if (len)
{
if (len <= ret) ret = len - 1;
ascii_to_unicode( buffer, tmp, ret );
buffer[ret] = 0;
}
return ret * sizeof(WCHAR);
}
/***********************************************************************
* get_class_ptr
*/
static CLASS *get_class_ptr( HWND hwnd, BOOL write_access )
{
WND *ptr = get_win_ptr( hwnd );
if (ptr)
{
if (ptr != WND_OTHER_PROCESS && ptr != WND_DESKTOP) return ptr->class;
if (!write_access) return OBJ_OTHER_PROCESS;
/* modifying classes in other processes is not allowed */
if (ptr == WND_DESKTOP || is_window( hwnd ))
{
RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
return NULL;
}
}
RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
return NULL;
}
/***********************************************************************
* release_class_ptr
*/
static void release_class_ptr( CLASS *ptr )
{
user_unlock();
}
static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name )
{
ULONG_PTR instance = (UINT_PTR)module;
CLASS *class;
int is_win16;
user_lock();
LIST_FOR_EACH_ENTRY( class, &class_list, CLASS, entry )
{
if (wcsnicmp( class->name, name->Buffer, name->Length / sizeof(WCHAR) ) ||
class->name[name->Length / sizeof(WCHAR)]) continue;
is_win16 = !(class->instance >> 16);
if (!instance || !class->local || class->instance == instance ||
(!is_win16 && ((class->instance & ~0xffff) == (instance & ~0xffff))))
{
TRACE( "%s %lx -> %p\n", debugstr_us(name), instance, class );
return class;
}
}
user_unlock();
return NULL;
}
/***********************************************************************
* get_class_winproc
*/
WNDPROC get_class_winproc( CLASS *class )
{
return class->winproc;
}
/***********************************************************************
* get_class_dce
*/
struct dce *get_class_dce( CLASS *class )
{
return class->dce;
}
/***********************************************************************
* set_class_dce
*/
struct dce *set_class_dce( CLASS *class, struct dce *dce )
{
if (class->dce) return class->dce; /* already set, don't change it */
class->dce = dce;
return dce;
}
/***********************************************************************
* NtUserRegisterClassExWOW (win32u.@)
*/
ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *name, UNICODE_STRING *version,
struct client_menu_name *client_menu_name, DWORD fnid,
DWORD flags, DWORD *wow )
{
const BOOL is_builtin = fnid, ansi = flags;
HINSTANCE instance;
HICON sm_icon = 0;
CLASS *class;
ATOM atom;
BOOL ret;
/* create the desktop window to trigger builtin class registration */
if (!is_builtin) get_desktop_window();
if (wc->cbSize != sizeof(*wc) || wc->cbClsExtra < 0 || wc->cbWndExtra < 0 ||
(!is_builtin && wc->hInstance == user32_module)) /* we can't register a class for user32 */
{
RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
return 0;
}
if (!(instance = wc->hInstance)) instance = NtCurrentTeb()->Peb->ImageBaseAddress;
TRACE( "name=%s hinst=%p style=0x%x clExtr=0x%x winExtr=0x%x\n",
debugstr_us(name), instance, wc->style, wc->cbClsExtra, wc->cbWndExtra );
/* Fix the extra bytes value */
if (wc->cbClsExtra > 40) /* Extra bytes are limited to 40 in Win32 */
WARN( "Class extra bytes %d is > 40\n", wc->cbClsExtra);
if (wc->cbWndExtra > 40) /* Extra bytes are limited to 40 in Win32 */
WARN("Win extra bytes %d is > 40\n", wc->cbWndExtra );
if (!(class = calloc( 1, sizeof(CLASS) + wc->cbClsExtra ))) return 0;
memcpy( class->name, name->Buffer, name->Length );
class->name[name->Length / sizeof(WCHAR)] = 0;
class->basename = class->name + version->Length / sizeof(WCHAR);
class->style = wc->style;
class->local = !is_builtin && !(wc->style & CS_GLOBALCLASS);
class->cbWndExtra = wc->cbWndExtra;
class->cbClsExtra = wc->cbClsExtra;
class->instance = (UINT_PTR)instance;
SERVER_START_REQ( create_class )
{
req->local = class->local;
req->style = class->style;
req->instance = class->instance;
req->extra = class->cbClsExtra;
req->win_extra = class->cbWndExtra;
req->client_ptr = wine_server_client_ptr( class );
req->atom = wine_server_add_atom( req, name );
req->name_offset = version->Length / sizeof(WCHAR);
ret = !wine_server_call_err( req );
atom = reply->atom;
}
SERVER_END_REQ;
if (!ret)
{
free( class );
return 0;
}
/* Other non-null values must be set by caller */
if (wc->hIcon && !wc->hIconSm)
sm_icon = CopyImage( wc->hIcon, IMAGE_ICON,
get_system_metrics( SM_CXSMICON ),
get_system_metrics( SM_CYSMICON ),
LR_COPYFROMRESOURCE );
user_lock();
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->hIcon = wc->hIcon;
class->hIconSm = wc->hIconSm;
class->hIconSmIntern = sm_icon;
class->hCursor = wc->hCursor;
class->hbrBackground = wc->hbrBackground;
class->atom = atom;
class->winproc = alloc_winproc( wc->lpfnWndProc, ansi );
if (client_menu_name) class->menu_name = *client_menu_name;
release_class_ptr( class );
return atom;
}
/***********************************************************************
* NtUserUnregisterClass (win32u.@)
*/
BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
struct client_menu_name *client_menu_name )
{
CLASS *class = NULL;
/* create the desktop window to trigger builtin class registration */
get_desktop_window();
SERVER_START_REQ( destroy_class )
{
req->instance = wine_server_client_ptr( instance );
req->atom = wine_server_add_atom( req, name );
if (!wine_server_call_err( req )) class = wine_server_get_ptr( reply->client_ptr );
}
SERVER_END_REQ;
if (!class) return FALSE;
TRACE( "%p\n", class );
user_lock();
if (class->dce) free_dce( class->dce, 0 );
list_remove( &class->entry );
if (class->hbrBackground > (HBRUSH)(COLOR_GRADIENTINACTIVECAPTION + 1))
NtGdiDeleteObjectApp( class->hbrBackground );
*client_menu_name = class->menu_name;
NtUserDestroyCursor( class->hIconSmIntern, 0 );
free( class );
user_unlock();
return TRUE;
}
/***********************************************************************
* NtUserGetClassInfo (win32u.@)
*/
ATOM WINAPI NtUserGetClassInfoEx( HINSTANCE instance, UNICODE_STRING *name, WNDCLASSEXW *wc,
struct client_menu_name *menu_name, BOOL ansi )
{
CLASS *class;
ATOM atom;
/* create the desktop window to trigger builtin class registration */
if (!is_desktop_class( name ) && !is_message_class( name )) get_desktop_window();
if (!(class = find_class( instance, name ))) return 0;
if (wc)
{
wc->style = class->style;
wc->lpfnWndProc = get_winproc( class->winproc, ansi );
wc->cbClsExtra = class->cbClsExtra;
wc->cbWndExtra = class->cbWndExtra;
wc->hInstance = (instance == user32_module) ? 0 : instance;
wc->hIcon = class->hIcon;
wc->hIconSm = class->hIconSm ? class->hIconSm : class->hIconSmIntern;
wc->hCursor = class->hCursor;
wc->hbrBackground = class->hbrBackground;
wc->lpszMenuName = ansi ? (const WCHAR *)class->menu_name.nameA : class->menu_name.nameW;
wc->lpszClassName = name->Buffer;
}
if (menu_name) *menu_name = class->menu_name;
atom = class->atom;
release_class_ptr( class );
return atom;
}
/***********************************************************************
* NtUserGetAtomName (win32u.@)
*/
ULONG WINAPI NtUserGetAtomName( ATOM atom, UNICODE_STRING *name )
{
WCHAR buffer[MAX_ATOM_LEN];
UINT size = 0;
if (atom < MAXINTATOM)
{
if (!atom)
{
set_ntstatus( STATUS_INVALID_PARAMETER );
return 0;
}
size = integral_atom_name( buffer, sizeof(buffer), atom );
}
else
{
SERVER_START_REQ( get_user_atom_name )
{
req->atom = atom;
wine_server_set_reply( req, buffer, sizeof(buffer) );
if (!wine_server_call_err( req ))
{
size = wine_server_reply_size( reply );
buffer[size / sizeof(WCHAR)] = 0;
}
}
SERVER_END_REQ;
if (!size) return 0;
}
if (name->MaximumLength < sizeof(WCHAR))
{
RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
size = min( size, name->MaximumLength - sizeof(WCHAR) );
if (size) memcpy( name->Buffer, buffer, size );
name->Buffer[size / sizeof(WCHAR)] = 0;
return size / sizeof(WCHAR);
}
/***********************************************************************
* NtUserRegisterWindowMessage (win32u.@)
*/
ATOM WINAPI NtUserRegisterWindowMessage( UNICODE_STRING *name )
{
unsigned int status;
RTL_ATOM atom = 0;
TRACE( "%s\n", debugstr_us(name) );
if (!name)
{
RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
return 0;
}
status = is_integral_atom( name->Buffer, name->Length / sizeof(WCHAR), &atom );
if (status == STATUS_MORE_ENTRIES)
{
SERVER_START_REQ( add_user_atom )
{
wine_server_add_data( req, name->Buffer, name->Length );
status = wine_server_call( req );
atom = reply->atom;
}
SERVER_END_REQ;
}
TRACE( "%s -> %x\n", debugstr_us(name), status == STATUS_SUCCESS ? atom : 0 );
set_ntstatus( status );
return atom;
}
/***********************************************************************
* NtUserGetClassName (win32u.@)
*/
INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name )
{
CLASS *class;
int ret;
TRACE( "%p %x %p\n", hwnd, real, name );
if (name->MaximumLength <= sizeof(WCHAR))
{
RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
if (!(class = get_class_ptr( hwnd, FALSE ))) return 0;
if (class == OBJ_OTHER_PROCESS)
{
ATOM atom = 0;
SERVER_START_REQ( get_class_info )
{
req->window = wine_server_user_handle( hwnd );
req->offset = GCW_ATOM;
req->size = sizeof(atom);
wine_server_call_err( req );
atom = reply->info;
}
SERVER_END_REQ;
return NtUserGetAtomName( atom, name );
}
ret = min( name->MaximumLength / sizeof(WCHAR) - 1, lstrlenW(class->basename) );
if (ret) memcpy( name->Buffer, class->basename, ret * sizeof(WCHAR) );
name->Buffer[ret] = 0;
release_class_ptr( class );
return ret;
}
/* Set class info with the wine server. */
static BOOL set_server_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size )
{
BOOL ret;
SERVER_START_REQ( set_class_info )
{
req->window = wine_server_user_handle( hwnd );
req->offset = offset;
req->size = size;
req->new_info = newval;
ret = !wine_server_call_err( req );
}
SERVER_END_REQ;
return ret;
}
static ULONG_PTR set_class_long_size( HWND hwnd, INT offset, LONG_PTR newval, UINT size, BOOL ansi )
{
ULONG_PTR retval = 0;
HICON small_icon = 0;
CLASS *class;
if (!(class = get_class_ptr( hwnd, TRUE ))) return 0;
if (offset >= 0)
{
if (set_server_info( hwnd, offset, newval, size ))
{
void *ptr = (char *)(class + 1) + offset;
memcpy( &retval, ptr, size );
memcpy( ptr, &newval, size );
}
}
else switch(offset)
{
case GCLP_MENUNAME:
{
struct client_menu_name *menu_name = (void *)newval;
struct client_menu_name prev = class->menu_name;
class->menu_name = *menu_name;
*menu_name = prev;
retval = 0; /* Old value is now meaningless anyway */
break;
}
case GCLP_WNDPROC:
retval = (ULONG_PTR)get_winproc( class->winproc, ansi );
class->winproc = alloc_winproc( (WNDPROC)newval, ansi );
break;
case GCLP_HBRBACKGROUND:
retval = (ULONG_PTR)class->hbrBackground;
class->hbrBackground = (HBRUSH)newval;
break;
case GCLP_HCURSOR:
retval = (ULONG_PTR)class->hCursor;
class->hCursor = (HCURSOR)newval;
break;
case GCLP_HICON:
retval = (ULONG_PTR)class->hIcon;
if (retval == newval) break;
if (newval && !class->hIconSm)
{
release_class_ptr( class );
small_icon = CopyImage( (HICON)newval, IMAGE_ICON,
get_system_metrics( SM_CXSMICON ),
get_system_metrics( SM_CYSMICON ),
LR_COPYFROMRESOURCE );
if (!(class = get_class_ptr( hwnd, TRUE )))
{
NtUserDestroyCursor( small_icon, 0 );
return 0;
}
if (retval != HandleToUlong( class->hIcon ) || class->hIconSm)
{
/* someone beat us, restart */
release_class_ptr( class );
NtUserDestroyCursor( small_icon, 0 );
return set_class_long_size( hwnd, offset, newval, size, ansi );
}
}
if (class->hIconSmIntern) NtUserDestroyCursor( class->hIconSmIntern, 0 );
class->hIcon = (HICON)newval;
class->hIconSmIntern = small_icon;
break;
case GCLP_HICONSM:
retval = (ULONG_PTR)class->hIconSm;
if (retval == newval) break;
if (retval && !newval && class->hIcon)
{
HICON icon = class->hIcon;
release_class_ptr( class );
small_icon = CopyImage( icon, IMAGE_ICON,
get_system_metrics( SM_CXSMICON ),
get_system_metrics( SM_CYSMICON ),
LR_COPYFROMRESOURCE );
if (!(class = get_class_ptr( hwnd, TRUE )))
{
NtUserDestroyCursor( small_icon, 0 );
return 0;
}
if (class->hIcon != icon || !class->hIconSm)
{
/* someone beat us, restart */
release_class_ptr( class );
NtUserDestroyCursor( small_icon, 0 );
return set_class_long_size( hwnd, offset, newval, size, ansi );
}
}
if (class->hIconSmIntern) NtUserDestroyCursor( class->hIconSmIntern, 0 );
class->hIconSm = (HICON)newval;
class->hIconSmIntern = small_icon;
break;
case GCL_STYLE:
if (!set_server_info( hwnd, offset, newval, size )) break;
retval = class->style;
class->style = newval;
break;
case GCL_CBWNDEXTRA:
if (!set_server_info( hwnd, offset, newval, size )) break;
retval = class->cbWndExtra;
class->cbWndExtra = newval;
break;
case GCLP_HMODULE:
if (!set_server_info( hwnd, offset, newval, size )) break;
retval = class->instance;
class->instance = newval;
break;
case GCL_CBCLSEXTRA: /* cannot change this one */
RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
break;
default:
RtlSetLastWin32Error( ERROR_INVALID_INDEX );
break;
}
release_class_ptr( class );
return retval;
}
/***********************************************************************
* NtUserSetClassLong (win32u.@)
*/
DWORD WINAPI NtUserSetClassLong( HWND hwnd, INT offset, LONG newval, BOOL ansi )
{
return set_class_long_size( hwnd, offset, newval, sizeof(LONG), ansi );
}
/***********************************************************************
* NtUserSetClassLongPtr (win32u.@)
*/
ULONG_PTR WINAPI NtUserSetClassLongPtr( HWND hwnd, INT offset, LONG_PTR newval, BOOL ansi )
{
return set_class_long_size( hwnd, offset, newval, sizeof(LONG_PTR), ansi );
}
/***********************************************************************
* NtUserSetClassWord (win32u.@)
*/
WORD WINAPI NtUserSetClassWord( HWND hwnd, INT offset, WORD newval )
{
return set_class_long_size( hwnd, offset, newval, sizeof(WORD), TRUE );
}
static ULONG_PTR get_class_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
{
CLASS *class;
ULONG_PTR retvalue = 0;
if (!(class = get_class_ptr( hwnd, FALSE ))) return 0;
if (class == OBJ_OTHER_PROCESS)
{
SERVER_START_REQ( get_class_info )
{
req->window = wine_server_user_handle( hwnd );
req->offset = offset;
req->size = size;
if (!wine_server_call_err( req ))
{
switch (offset)
{
case GCLP_HBRBACKGROUND:
case GCLP_HCURSOR:
case GCLP_HICON:
case GCLP_HICONSM:
case GCLP_WNDPROC:
case GCLP_MENUNAME:
FIXME( "offset %d not supported on other process window %p\n", offset, hwnd );
break;
default:
retvalue = reply->info;
break;
}
}
}
SERVER_END_REQ;
return retvalue;
}
if (offset >= 0)
{
if (offset <= class->cbClsExtra - size)
{
if (size == sizeof(DWORD))
{
DWORD retdword;
memcpy( &retdword, (char *)(class + 1) + offset, sizeof(DWORD) );
retvalue = retdword;
}
else
memcpy( &retvalue, (char *)(class + 1) + offset, sizeof(ULONG_PTR) );
}
else
RtlSetLastWin32Error( ERROR_INVALID_INDEX );
release_class_ptr( class );
return retvalue;
}
switch(offset)
{
case GCLP_HBRBACKGROUND:
retvalue = (ULONG_PTR)class->hbrBackground;
break;
case GCLP_HCURSOR:
retvalue = (ULONG_PTR)class->hCursor;
break;
case GCLP_HICON:
retvalue = (ULONG_PTR)class->hIcon;
break;
case GCLP_HICONSM:
retvalue = (ULONG_PTR)(class->hIconSm ? class->hIconSm : class->hIconSmIntern);
break;
case GCL_STYLE:
retvalue = class->style;
break;
case GCL_CBWNDEXTRA:
retvalue = class->cbWndExtra;
break;
case GCL_CBCLSEXTRA:
retvalue = class->cbClsExtra;
break;
case GCLP_HMODULE:
retvalue = class->instance;
break;
case GCLP_WNDPROC:
retvalue = (ULONG_PTR)get_winproc( class->winproc, ansi );
break;
case GCLP_MENUNAME:
retvalue = ansi ? (ULONG_PTR)class->menu_name.nameA : (ULONG_PTR)class->menu_name.nameW;
break;
case GCW_ATOM:
retvalue = class->atom;
break;
default:
RtlSetLastWin32Error( ERROR_INVALID_INDEX );
break;
}
release_class_ptr( class );
return retvalue;
}
DWORD get_class_long( HWND hwnd, INT offset, BOOL ansi )
{
return get_class_long_size( hwnd, offset, sizeof(DWORD), ansi );
}
ULONG_PTR get_class_long_ptr( HWND hwnd, INT offset, BOOL ansi )
{
return get_class_long_size( hwnd, offset, sizeof(ULONG_PTR), ansi );
}
WORD get_class_word( HWND hwnd, INT offset )
{
return get_class_long_size( hwnd, offset, sizeof(WORD), TRUE );
}
BOOL needs_ime_window( HWND hwnd )
{
static const WCHAR imeW[] = {'I','M','E',0};
CLASS *class;
BOOL ret;
if (!(class = get_class_ptr( hwnd, FALSE ))) return FALSE;
ret = !(class->style & CS_IME) && wcscmp( imeW, class->name );
release_class_ptr( class );
return ret;
}
static const struct builtin_class_descr desktop_builtin_class =
{
.name = "#32769", /* DESKTOP_CLASS_ATOM */
.style = CS_DBLCLKS,
.proc = NTUSER_WNDPROC_DESKTOP,
.brush = (HBRUSH)(COLOR_BACKGROUND + 1),
};
static const struct builtin_class_descr message_builtin_class =
{
.name = "Message",
.proc = NTUSER_WNDPROC_MESSAGE,
};
static const struct builtin_class_descr builtin_classes[] =
{
/* button */
{
.name = "Button",
.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
.proc = NTUSER_WNDPROC_BUTTON,
.extra = sizeof(UINT) + 2 * sizeof(HANDLE),
.cursor = IDC_ARROW,
},
/* combo */
{
.name = "ComboBox",
.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
.proc = NTUSER_WNDPROC_COMBO,
.extra = sizeof(void *),
.cursor = IDC_ARROW,
},
/* combolbox */
{
.name = "ComboLBox",
.style = CS_DBLCLKS | CS_SAVEBITS,
.proc = NTUSER_WNDPROC_COMBOLBOX,
.extra = sizeof(void *),
.cursor = IDC_ARROW,
},
/* dialog */
{
.name = "#32770", /* DIALOG_CLASS_ATOM */
.style = CS_SAVEBITS | CS_DBLCLKS,
.proc = NTUSER_WNDPROC_DIALOG,
.extra = DLGWINDOWEXTRA,
.cursor = IDC_ARROW,
},
/* icon title */
{
.name = "#32772", /* ICONTITLE_CLASS_ATOM */
.proc = NTUSER_WNDPROC_ICONTITLE,
.cursor = IDC_ARROW,
},
/* IME */
{
.name = "IME",
.proc = NTUSER_WNDPROC_IME,
.extra = 2 * sizeof(LONG_PTR),
.cursor = IDC_ARROW,
},
/* listbox */
{
.name = "ListBox",
.style = CS_DBLCLKS,
.proc = NTUSER_WNDPROC_LISTBOX,
.extra = sizeof(void *),
.cursor = IDC_ARROW,
},
/* menu */
{
.name = "#32768", /* POPUPMENU_CLASS_ATOM */
.style = CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,
.proc = NTUSER_WNDPROC_MENU,
.extra = sizeof(HMENU),
.cursor = IDC_ARROW,
.brush = (HBRUSH)(COLOR_MENU + 1),
},
/* MDIClient */
{
.name = "MDIClient",
.proc = NTUSER_WNDPROC_MDICLIENT,
.extra = 2 * sizeof(void *),
.cursor = IDC_ARROW,
.brush = (HBRUSH)(COLOR_APPWORKSPACE + 1),
},
/* scrollbar */
{
.name = "ScrollBar",
.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
.proc = NTUSER_WNDPROC_SCROLLBAR,
.extra = sizeof(struct scroll_bar_win_data),
.cursor = IDC_ARROW,
},
/* static */
{
.name = "Static",
.style = CS_DBLCLKS | CS_PARENTDC,
.proc = NTUSER_WNDPROC_STATIC,
.extra = 2 * sizeof(HANDLE),
.cursor = IDC_ARROW,
},
};
/***********************************************************************
* register_builtin
*
* Register a builtin control class.
* This allows having both ANSI and Unicode winprocs for the same class.
*/
static void register_builtin( const struct builtin_class_descr *descr )
{
UNICODE_STRING name, version = { .Length = 0 };
struct client_menu_name menu_name = { 0 };
WCHAR nameW[64];
WNDCLASSEXW class = {
.cbSize = sizeof(class),
.hInstance = user32_module,
.style = descr->style,
.cbWndExtra = descr->extra,
.hbrBackground = descr->brush,
.lpfnWndProc = BUILTIN_WINPROC( descr->proc ),
};
if (descr->cursor)
class.hCursor = LoadImageW( 0, (const WCHAR *)descr->cursor, IMAGE_CURSOR,
0, 0, LR_SHARED | LR_DEFAULTSIZE );
asciiz_to_unicode( nameW, descr->name );
RtlInitUnicodeString( &name, nameW );
if (!NtUserRegisterClassExWOW( &class, &name, &version, &menu_name, 1, 0, NULL ) && class.hCursor)
NtUserDestroyCursor( class.hCursor, 0 );
}
static void register_builtins(void)
{
ULONG ret_len, i;
void *ret_ptr;
/* 64-bit Windows use sizeof(UINT64) for all processes, while 32-bit Windows use 6 for extra
* bytes size. Civilization II depends on the size being 6, so we use that even in wow64. */
const struct builtin_class_descr edit_class =
{
.name = "Edit",
.style = CS_DBLCLKS | CS_PARENTDC,
.proc = NTUSER_WNDPROC_EDIT,
.extra = sizeof(void *) == 4 || NtCurrentTeb()->WowTebOffset ? 6 : sizeof(UINT64),
.cursor = IDC_IBEAM,
};
for (i = 0; i < ARRAYSIZE(builtin_classes); i++) register_builtin( &builtin_classes[i] );
register_builtin( &edit_class );
KeUserModeCallback( NtUserInitBuiltinClasses, NULL, 0, &ret_ptr, &ret_len );
}
/***********************************************************************
* register_builtin_classes
*/
void register_builtin_classes(void)
{
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
pthread_once( &init_once, register_builtins );
}
/***********************************************************************
* register_desktop_class
*/
void register_desktop_class(void)
{
register_builtin( &desktop_builtin_class );
register_builtin( &message_builtin_class );
}