ws2_32: Return a valid value for WSAIoctl SIO_IDEAL_SEND_BACKLOG_QUERY.
[wine.git] / dlls / win32u / class.c
blob28eabcc1f514ccaeaa8dc0148b352ec4bb62049a
1 /*
2 * Window classes functions
4 * Copyright 1993, 1996, 2003 Alexandre Julliard
5 * Copyright 1995 Martin von Loewis
6 * Copyright 1998 Juergen Schmied (jsch)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #if 0
24 #pragma makedep unix
25 #endif
27 #include <pthread.h>
28 #include <assert.h>
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #include "win32u_private.h"
32 #include "ntuser_private.h"
33 #include "wine/server.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(class);
37 WINE_DECLARE_DEBUG_CHANNEL(win);
39 #define MAX_WINPROCS 4096
40 #define WINPROC_PROC16 ((void *)1) /* placeholder for 16-bit window procs */
42 typedef struct tagCLASS
44 struct list entry; /* Entry in class list */
45 UINT style; /* Class style */
46 BOOL local; /* Local class? */
47 WNDPROC winproc; /* Window procedure */
48 INT cbClsExtra; /* Class extra bytes */
49 INT cbWndExtra; /* Window extra bytes */
50 struct dce *dce; /* Opaque pointer to class DCE */
51 UINT_PTR instance; /* Module that created the task */
52 HICON hIcon; /* Default icon */
53 HICON hIconSm; /* Default small icon */
54 HICON hIconSmIntern; /* Internal small icon, derived from hIcon */
55 HCURSOR hCursor; /* Default cursor */
56 HBRUSH hbrBackground; /* Default background */
57 ATOM atomName; /* Name of the class */
58 WCHAR name[MAX_ATOM_LEN + 1];
59 WCHAR *basename; /* Base name for redirected classes, pointer within 'name'. */
60 struct client_menu_name menu_name; /* Default menu name */
61 } CLASS;
63 /* Built-in class descriptor */
64 struct builtin_class_descr
66 const char *name; /* class name */
67 UINT style; /* class style */
68 INT extra; /* window extra bytes */
69 ULONG_PTR cursor; /* cursor id */
70 HBRUSH brush; /* brush or system color */
71 enum builtin_winprocs proc;
74 typedef struct tagWINDOWPROC
76 WNDPROC procA; /* ANSI window proc */
77 WNDPROC procW; /* Unicode window proc */
78 } WINDOWPROC;
80 static WINDOWPROC winproc_array[MAX_WINPROCS];
81 static UINT winproc_used = NB_BUILTIN_WINPROCS;
82 static pthread_mutex_t winproc_lock = PTHREAD_MUTEX_INITIALIZER;
84 static struct list class_list = LIST_INIT( class_list );
86 HINSTANCE user32_module = 0;
88 /* find an existing winproc for a given function and type */
89 /* FIXME: probably should do something more clever than a linear search */
90 static WINDOWPROC *find_winproc( WNDPROC func, BOOL ansi )
92 unsigned int i;
94 for (i = 0; i < NB_BUILTIN_AW_WINPROCS; i++)
96 /* match either proc, some apps confuse A and W */
97 if (winproc_array[i].procA != func && winproc_array[i].procW != func) continue;
98 return &winproc_array[i];
100 for (i = NB_BUILTIN_AW_WINPROCS; i < winproc_used; i++)
102 if (ansi && winproc_array[i].procA != func) continue;
103 if (!ansi && winproc_array[i].procW != func) continue;
104 return &winproc_array[i];
106 return NULL;
109 /* return the window proc for a given handle, or NULL for an invalid handle,
110 * or WINPROC_PROC16 for a handle to a 16-bit proc. */
111 WINDOWPROC *get_winproc_ptr( WNDPROC handle )
113 UINT index = LOWORD(handle);
114 if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) return NULL;
115 if (index >= MAX_WINPROCS) return WINPROC_PROC16;
116 if (index >= winproc_used) return NULL;
117 return &winproc_array[index];
120 /* create a handle for a given window proc */
121 static inline WNDPROC proc_to_handle( WINDOWPROC *proc )
123 return (WNDPROC)(ULONG_PTR)((proc - winproc_array) | (WINPROC_HANDLE << 16));
126 /* allocate and initialize a new winproc */
127 static inline WINDOWPROC *alloc_winproc_ptr( WNDPROC func, BOOL ansi )
129 WINDOWPROC *proc;
131 /* check if the function is already a win proc */
132 if (!func) return NULL;
133 if ((proc = get_winproc_ptr( func ))) return proc;
135 pthread_mutex_lock( &winproc_lock );
137 /* check if we already have a winproc for that function */
138 if (!(proc = find_winproc( func, ansi )))
140 if (winproc_used < MAX_WINPROCS)
142 proc = &winproc_array[winproc_used++];
143 if (ansi) proc->procA = func;
144 else proc->procW = func;
145 TRACE_(win)( "allocated %p for %c %p (%d/%d used)\n",
146 proc_to_handle(proc), ansi ? 'A' : 'W', func,
147 winproc_used, MAX_WINPROCS );
149 else WARN_(win)( "too many winprocs, cannot allocate one for %p\n", func );
151 else TRACE_(win)( "reusing %p for %p\n", proc_to_handle(proc), func );
153 pthread_mutex_unlock( &winproc_lock );
154 return proc;
157 /**********************************************************************
158 * alloc_winproc
160 * Allocate a window procedure for a window or class.
162 * Note that allocated winprocs are never freed; the idea is that even if an app creates a
163 * lot of windows, it will usually only have a limited number of window procedures, so the
164 * array won't grow too large, and this way we avoid the need to track allocations per window.
166 WNDPROC alloc_winproc( WNDPROC func, BOOL ansi )
168 WINDOWPROC *proc;
170 if (!(proc = alloc_winproc_ptr( func, ansi ))) return func;
171 if (proc == WINPROC_PROC16) return func;
172 return proc_to_handle( proc );
175 /* Get a window procedure pointer that can be passed to the Windows program. */
176 WNDPROC get_winproc( WNDPROC proc, BOOL ansi )
178 WINDOWPROC *ptr = get_winproc_ptr( proc );
180 if (!ptr || ptr == WINPROC_PROC16) return proc;
181 if (ansi)
183 if (ptr->procA) return ptr->procA;
184 return proc;
186 else
188 if (ptr->procW) return ptr->procW;
189 return proc;
193 /* Return the window procedure type, or the default value if not a winproc handle. */
194 BOOL is_winproc_unicode( WNDPROC proc, BOOL def_val )
196 WINDOWPROC *ptr = get_winproc_ptr( proc );
198 if (!ptr) return def_val;
199 if (ptr == WINPROC_PROC16) return FALSE; /* 16-bit is always A */
200 if (ptr->procA && ptr->procW) return def_val; /* can be both */
201 return ptr->procW != NULL;
204 void get_winproc_params( struct win_proc_params *params, BOOL fixup_ansi_dst )
206 WINDOWPROC *proc = get_winproc_ptr( params->func );
208 if (!proc)
210 params->procW = params->procA = NULL;
212 else if (proc == WINPROC_PROC16)
214 params->procW = params->procA = WINPROC_PROC16;
216 else
218 params->procA = proc->procA;
219 params->procW = proc->procW;
221 if (fixup_ansi_dst)
223 if (params->ansi)
225 if (params->procA) params->ansi_dst = TRUE;
226 else if (params->procW) params->ansi_dst = FALSE;
228 else
230 if (params->procW) params->ansi_dst = FALSE;
231 else if (params->procA) params->ansi_dst = TRUE;
236 if (!params->procA) params->procA = params->func;
237 if (!params->procW) params->procW = params->func;
240 DLGPROC get_dialog_proc( DLGPROC ret, BOOL ansi )
242 WINDOWPROC *proc;
244 if (!(proc = get_winproc_ptr( ret ))) return ret;
245 if (proc == WINPROC_PROC16) return WINPROC_PROC16;
246 return ansi ? proc->procA : proc->procW;
249 static void init_user(void)
251 gdi_init();
252 winstation_init();
253 sysparams_init();
254 register_desktop_class();
257 /***********************************************************************
258 * NtUserInitializeClientPfnArrays (win32u.@)
260 NTSTATUS WINAPI NtUserInitializeClientPfnArrays( const struct user_client_procs *client_procsA,
261 const struct user_client_procs *client_procsW,
262 const void *client_workers, HINSTANCE user_module )
264 static pthread_once_t init_once = PTHREAD_ONCE_INIT;
266 winproc_array[WINPROC_BUTTON].procA = client_procsA->pButtonWndProc;
267 winproc_array[WINPROC_BUTTON].procW = client_procsW->pButtonWndProc;
268 winproc_array[WINPROC_COMBO].procA = client_procsA->pComboWndProc;
269 winproc_array[WINPROC_COMBO].procW = client_procsW->pComboWndProc;
270 winproc_array[WINPROC_DEFWND].procA = client_procsA->pDefWindowProc;
271 winproc_array[WINPROC_DEFWND].procW = client_procsW->pDefWindowProc;
272 winproc_array[WINPROC_DIALOG].procA = client_procsA->pDefDlgProc;
273 winproc_array[WINPROC_DIALOG].procW = client_procsW->pDefDlgProc;
274 winproc_array[WINPROC_EDIT].procA = client_procsA->pEditWndProc;
275 winproc_array[WINPROC_EDIT].procW = client_procsW->pEditWndProc;
276 winproc_array[WINPROC_LISTBOX].procA = client_procsA->pListBoxWndProc;
277 winproc_array[WINPROC_LISTBOX].procW = client_procsW->pListBoxWndProc;
278 winproc_array[WINPROC_MDICLIENT].procA = client_procsA->pMDIClientWndProc;
279 winproc_array[WINPROC_MDICLIENT].procW = client_procsW->pMDIClientWndProc;
280 winproc_array[WINPROC_SCROLLBAR].procA = client_procsA->pScrollBarWndProc;
281 winproc_array[WINPROC_SCROLLBAR].procW = client_procsW->pScrollBarWndProc;
282 winproc_array[WINPROC_STATIC].procA = client_procsA->pStaticWndProc;
283 winproc_array[WINPROC_STATIC].procW = client_procsW->pStaticWndProc;
284 winproc_array[WINPROC_IME].procA = client_procsA->pImeWndProc;
285 winproc_array[WINPROC_IME].procW = client_procsW->pImeWndProc;
286 winproc_array[WINPROC_DESKTOP].procA = client_procsA->pDesktopWndProc;
287 winproc_array[WINPROC_DESKTOP].procW = client_procsW->pDesktopWndProc;
288 winproc_array[WINPROC_ICONTITLE].procA = client_procsA->pIconTitleWndProc;
289 winproc_array[WINPROC_ICONTITLE].procW = client_procsW->pIconTitleWndProc;
290 winproc_array[WINPROC_MENU].procA = client_procsA->pPopupMenuWndProc;
291 winproc_array[WINPROC_MENU].procW = client_procsW->pPopupMenuWndProc;
292 winproc_array[WINPROC_MESSAGE].procA = client_procsA->pMessageWndProc;
293 winproc_array[WINPROC_MESSAGE].procW = client_procsW->pMessageWndProc;
295 user32_module = user_module;
297 pthread_once( &init_once, init_user );
298 return STATUS_SUCCESS;
301 /***********************************************************************
302 * get_int_atom_value
304 ATOM get_int_atom_value( UNICODE_STRING *name )
306 const WCHAR *ptr = name->Buffer;
307 const WCHAR *end = ptr + name->Length / sizeof(WCHAR);
308 UINT ret = 0;
310 if (IS_INTRESOURCE(ptr)) return LOWORD(ptr);
312 if (*ptr++ != '#') return 0;
313 while (ptr < end)
315 if (*ptr < '0' || *ptr > '9') return 0;
316 ret = ret * 10 + *ptr++ - '0';
317 if (ret > 0xffff) return 0;
319 return ret;
322 /***********************************************************************
323 * get_class_ptr
325 static CLASS *get_class_ptr( HWND hwnd, BOOL write_access )
327 WND *ptr = get_win_ptr( hwnd );
329 if (ptr)
331 if (ptr != WND_OTHER_PROCESS && ptr != WND_DESKTOP) return ptr->class;
332 if (!write_access) return OBJ_OTHER_PROCESS;
334 /* modifying classes in other processes is not allowed */
335 if (ptr == WND_DESKTOP || is_window( hwnd ))
337 RtlSetLastWin32Error( ERROR_ACCESS_DENIED );
338 return NULL;
341 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
342 return NULL;
345 /***********************************************************************
346 * release_class_ptr
348 static void release_class_ptr( CLASS *ptr )
350 user_unlock();
353 static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name )
355 ATOM atom = get_int_atom_value( name );
356 ULONG_PTR instance = (UINT_PTR)module & ~0xffff;
357 CLASS *class;
359 user_lock();
360 LIST_FOR_EACH_ENTRY( class, &class_list, CLASS, entry )
362 if (atom)
364 if (class->atomName != atom) continue;
366 else
368 if (wcsnicmp( class->name, name->Buffer, name->Length / sizeof(WCHAR) ) ||
369 class->name[name->Length / sizeof(WCHAR)]) continue;
371 if (!class->local || !module || (class->instance & ~0xffff) == instance)
373 TRACE( "%s %lx -> %p\n", debugstr_us(name), instance, class );
374 return class;
377 user_unlock();
378 return NULL;
381 /***********************************************************************
382 * get_class_winproc
384 WNDPROC get_class_winproc( CLASS *class )
386 return class->winproc;
389 /***********************************************************************
390 * get_class_dce
392 struct dce *get_class_dce( CLASS *class )
394 return class->dce;
397 /***********************************************************************
398 * set_class_dce
400 struct dce *set_class_dce( CLASS *class, struct dce *dce )
402 if (class->dce) return class->dce; /* already set, don't change it */
403 class->dce = dce;
404 return dce;
407 /***********************************************************************
408 * NtUserRegisterClassExWOW (win32u.@)
410 ATOM WINAPI NtUserRegisterClassExWOW( const WNDCLASSEXW *wc, UNICODE_STRING *name, UNICODE_STRING *version,
411 struct client_menu_name *client_menu_name, DWORD fnid,
412 DWORD flags, DWORD *wow )
414 const BOOL is_builtin = fnid, ansi = flags;
415 HINSTANCE instance;
416 HICON sm_icon = 0;
417 CLASS *class;
418 ATOM atom;
419 BOOL ret;
421 /* create the desktop window to trigger builtin class registration */
422 if (!is_builtin) get_desktop_window();
424 if (wc->cbSize != sizeof(*wc) || wc->cbClsExtra < 0 || wc->cbWndExtra < 0 ||
425 (!is_builtin && wc->hInstance == user32_module)) /* we can't register a class for user32 */
427 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
428 return 0;
430 if (!(instance = wc->hInstance)) instance = NtCurrentTeb()->Peb->ImageBaseAddress;
432 TRACE( "name=%s hinst=%p style=0x%x clExtr=0x%x winExtr=0x%x\n",
433 debugstr_us(name), instance, wc->style, wc->cbClsExtra, wc->cbWndExtra );
435 /* Fix the extra bytes value */
437 if (wc->cbClsExtra > 40) /* Extra bytes are limited to 40 in Win32 */
438 WARN( "Class extra bytes %d is > 40\n", wc->cbClsExtra);
439 if (wc->cbWndExtra > 40) /* Extra bytes are limited to 40 in Win32 */
440 WARN("Win extra bytes %d is > 40\n", wc->cbWndExtra );
442 if (!(class = calloc( 1, sizeof(CLASS) + wc->cbClsExtra ))) return 0;
444 class->atomName = get_int_atom_value( name );
445 class->basename = class->name;
446 if (!class->atomName && name)
448 memcpy( class->name, name->Buffer, name->Length );
449 class->name[name->Length / sizeof(WCHAR)] = 0;
450 class->basename += version->Length / sizeof(WCHAR);
452 else
454 UNICODE_STRING str = { .MaximumLength = sizeof(class->name), .Buffer = class->name };
455 NtUserGetAtomName( class->atomName, &str );
458 class->style = wc->style;
459 class->local = !is_builtin && !(wc->style & CS_GLOBALCLASS);
460 class->cbWndExtra = wc->cbWndExtra;
461 class->cbClsExtra = wc->cbClsExtra;
462 class->instance = (UINT_PTR)instance;
464 SERVER_START_REQ( create_class )
466 req->local = class->local;
467 req->style = class->style;
468 req->instance = class->instance;
469 req->extra = class->cbClsExtra;
470 req->win_extra = class->cbWndExtra;
471 req->client_ptr = wine_server_client_ptr( class );
472 req->atom = class->atomName;
473 req->name_offset = version->Length / sizeof(WCHAR);
474 if (!req->atom && name) wine_server_add_data( req, name->Buffer, name->Length );
475 ret = !wine_server_call_err( req );
476 class->atomName = reply->atom;
478 SERVER_END_REQ;
479 if (!ret)
481 free( class );
482 return 0;
485 /* Other non-null values must be set by caller */
486 if (wc->hIcon && !wc->hIconSm)
487 sm_icon = CopyImage( wc->hIcon, IMAGE_ICON,
488 get_system_metrics( SM_CXSMICON ),
489 get_system_metrics( SM_CYSMICON ),
490 LR_COPYFROMRESOURCE );
492 user_lock();
493 if (class->local) list_add_head( &class_list, &class->entry );
494 else list_add_tail( &class_list, &class->entry );
496 atom = class->atomName;
498 TRACE( "name=%s->%s atom=%04x wndproc=%p hinst=%p bg=%p style=%08x clsExt=%d winExt=%d class=%p\n",
499 debugstr_w(wc->lpszClassName), debugstr_us(name), atom, wc->lpfnWndProc, instance,
500 wc->hbrBackground, wc->style, wc->cbClsExtra, wc->cbWndExtra, class );
502 class->hIcon = wc->hIcon;
503 class->hIconSm = wc->hIconSm;
504 class->hIconSmIntern = sm_icon;
505 class->hCursor = wc->hCursor;
506 class->hbrBackground = wc->hbrBackground;
507 class->winproc = alloc_winproc( wc->lpfnWndProc, ansi );
508 if (client_menu_name) class->menu_name = *client_menu_name;
509 release_class_ptr( class );
510 return atom;
513 /***********************************************************************
514 * NtUserUnregisterClass (win32u.@)
516 BOOL WINAPI NtUserUnregisterClass( UNICODE_STRING *name, HINSTANCE instance,
517 struct client_menu_name *client_menu_name )
519 CLASS *class = NULL;
521 /* create the desktop window to trigger builtin class registration */
522 get_desktop_window();
524 SERVER_START_REQ( destroy_class )
526 req->instance = wine_server_client_ptr( instance );
527 if (!(req->atom = get_int_atom_value( name )) && name->Length)
528 wine_server_add_data( req, name->Buffer, name->Length );
529 if (!wine_server_call_err( req )) class = wine_server_get_ptr( reply->client_ptr );
531 SERVER_END_REQ;
532 if (!class) return FALSE;
534 TRACE( "%p\n", class );
536 user_lock();
537 if (class->dce) free_dce( class->dce, 0 );
538 list_remove( &class->entry );
539 if (class->hbrBackground > (HBRUSH)(COLOR_GRADIENTINACTIVECAPTION + 1))
540 NtGdiDeleteObjectApp( class->hbrBackground );
541 *client_menu_name = class->menu_name;
542 NtUserDestroyCursor( class->hIconSmIntern, 0 );
543 free( class );
544 user_unlock();
545 return TRUE;
548 /***********************************************************************
549 * NtUserGetClassInfo (win32u.@)
551 ATOM WINAPI NtUserGetClassInfoEx( HINSTANCE instance, UNICODE_STRING *name, WNDCLASSEXW *wc,
552 struct client_menu_name *menu_name, BOOL ansi )
554 static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
555 CLASS *class;
556 ATOM atom;
558 /* create the desktop window to trigger builtin class registration */
559 if (name->Buffer != (const WCHAR *)DESKTOP_CLASS_ATOM &&
560 (IS_INTRESOURCE(name->Buffer) || name->Length != sizeof(messageW) ||
561 wcsnicmp( name->Buffer, messageW, ARRAYSIZE(messageW) )))
562 get_desktop_window();
564 if (!(class = find_class( instance, name ))) return 0;
566 if (wc)
568 wc->style = class->style;
569 wc->lpfnWndProc = get_winproc( class->winproc, ansi );
570 wc->cbClsExtra = class->cbClsExtra;
571 wc->cbWndExtra = class->cbWndExtra;
572 wc->hInstance = (instance == user32_module) ? 0 : instance;
573 wc->hIcon = class->hIcon;
574 wc->hIconSm = class->hIconSm ? class->hIconSm : class->hIconSmIntern;
575 wc->hCursor = class->hCursor;
576 wc->hbrBackground = class->hbrBackground;
577 wc->lpszMenuName = ansi ? (const WCHAR *)class->menu_name.nameA : class->menu_name.nameW;
578 wc->lpszClassName = name->Buffer;
581 if (menu_name) *menu_name = class->menu_name;
582 atom = class->atomName;
583 release_class_ptr( class );
584 return atom;
587 /***********************************************************************
588 * NtUserGetAtomName (win32u.@)
590 ULONG WINAPI NtUserGetAtomName( ATOM atom, UNICODE_STRING *name )
592 char buf[sizeof(ATOM_BASIC_INFORMATION) + MAX_ATOM_LEN * sizeof(WCHAR)];
593 ATOM_BASIC_INFORMATION *abi = (ATOM_BASIC_INFORMATION *)buf;
594 UINT size;
596 if (!set_ntstatus( NtQueryInformationAtom( atom, AtomBasicInformation,
597 buf, sizeof(buf), NULL )))
598 return 0;
600 if (name->MaximumLength < sizeof(WCHAR))
602 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
603 return 0;
606 size = min( abi->NameLength, name->MaximumLength - sizeof(WCHAR) );
607 if (size) memcpy( name->Buffer, abi->Name, size );
608 name->Buffer[size / sizeof(WCHAR)] = 0;
609 return size / sizeof(WCHAR);
612 /***********************************************************************
613 * NtUserGetClassName (win32u.@)
615 INT WINAPI NtUserGetClassName( HWND hwnd, BOOL real, UNICODE_STRING *name )
617 CLASS *class;
618 int ret;
620 TRACE( "%p %x %p\n", hwnd, real, name );
622 if (name->MaximumLength <= sizeof(WCHAR))
624 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
625 return 0;
628 if (!(class = get_class_ptr( hwnd, FALSE ))) return 0;
630 if (class == OBJ_OTHER_PROCESS)
632 ATOM atom = 0;
634 SERVER_START_REQ( set_class_info )
636 req->window = wine_server_user_handle( hwnd );
637 req->flags = 0;
638 req->extra_offset = -1;
639 req->extra_size = 0;
640 if (!wine_server_call_err( req ))
641 atom = reply->base_atom;
643 SERVER_END_REQ;
645 return NtUserGetAtomName( atom, name );
648 ret = min( name->MaximumLength / sizeof(WCHAR) - 1, lstrlenW(class->basename) );
649 if (ret) memcpy( name->Buffer, class->basename, ret * sizeof(WCHAR) );
650 name->Buffer[ret] = 0;
651 release_class_ptr( class );
652 return ret;
655 /* Set class info with the wine server. */
656 static BOOL set_server_info( HWND hwnd, INT offset, LONG_PTR newval, UINT size )
658 BOOL ret;
660 SERVER_START_REQ( set_class_info )
662 req->window = wine_server_user_handle( hwnd );
663 req->extra_offset = -1;
664 switch(offset)
666 case GCW_ATOM:
667 req->flags = SET_CLASS_ATOM;
668 req->atom = LOWORD(newval);
669 break;
670 case GCL_STYLE:
671 req->flags = SET_CLASS_STYLE;
672 req->style = newval;
673 break;
674 case GCL_CBWNDEXTRA:
675 req->flags = SET_CLASS_WINEXTRA;
676 req->win_extra = newval;
677 break;
678 case GCLP_HMODULE:
679 req->flags = SET_CLASS_INSTANCE;
680 req->instance = wine_server_client_ptr( (void *)newval );
681 break;
682 default:
683 assert( offset >= 0 );
684 req->flags = SET_CLASS_EXTRA;
685 req->extra_offset = offset;
686 req->extra_size = size;
687 if ( size == sizeof(LONG) )
689 LONG newlong = newval;
690 memcpy( &req->extra_value, &newlong, sizeof(LONG) );
692 else
693 memcpy( &req->extra_value, &newval, sizeof(LONG_PTR) );
694 break;
696 ret = !wine_server_call_err( req );
698 SERVER_END_REQ;
699 return ret;
702 static ULONG_PTR set_class_long( HWND hwnd, INT offset, LONG_PTR newval, UINT size, BOOL ansi )
704 ULONG_PTR retval = 0;
705 HICON small_icon = 0;
706 CLASS *class;
708 if (!(class = get_class_ptr( hwnd, TRUE ))) return 0;
710 if (offset >= 0)
712 if (set_server_info( hwnd, offset, newval, size ))
714 void *ptr = (char *)(class + 1) + offset;
715 if ( size == sizeof(LONG) )
717 DWORD retdword;
718 LONG newlong = newval;
719 memcpy( &retdword, ptr, sizeof(DWORD) );
720 memcpy( ptr, &newlong, sizeof(LONG) );
721 retval = retdword;
723 else
725 memcpy( &retval, ptr, sizeof(ULONG_PTR) );
726 memcpy( ptr, &newval, sizeof(LONG_PTR) );
730 else switch(offset)
732 case GCLP_MENUNAME:
734 struct client_menu_name *menu_name = (void *)newval;
735 struct client_menu_name prev = class->menu_name;
736 class->menu_name = *menu_name;
737 *menu_name = prev;
738 retval = 0; /* Old value is now meaningless anyway */
739 break;
741 case GCLP_WNDPROC:
742 retval = (ULONG_PTR)get_winproc( class->winproc, ansi );
743 class->winproc = alloc_winproc( (WNDPROC)newval, ansi );
744 break;
745 case GCLP_HBRBACKGROUND:
746 retval = (ULONG_PTR)class->hbrBackground;
747 class->hbrBackground = (HBRUSH)newval;
748 break;
749 case GCLP_HCURSOR:
750 retval = (ULONG_PTR)class->hCursor;
751 class->hCursor = (HCURSOR)newval;
752 break;
753 case GCLP_HICON:
754 retval = (ULONG_PTR)class->hIcon;
755 if (retval == newval) break;
756 if (newval && !class->hIconSm)
758 release_class_ptr( class );
760 small_icon = CopyImage( (HICON)newval, IMAGE_ICON,
761 get_system_metrics( SM_CXSMICON ),
762 get_system_metrics( SM_CYSMICON ),
763 LR_COPYFROMRESOURCE );
765 if (!(class = get_class_ptr( hwnd, TRUE )))
767 NtUserDestroyCursor( small_icon, 0 );
768 return 0;
770 if (retval != HandleToUlong( class->hIcon ) || class->hIconSm)
772 /* someone beat us, restart */
773 release_class_ptr( class );
774 NtUserDestroyCursor( small_icon, 0 );
775 return set_class_long( hwnd, offset, newval, size, ansi );
778 if (class->hIconSmIntern) NtUserDestroyCursor( class->hIconSmIntern, 0 );
779 class->hIcon = (HICON)newval;
780 class->hIconSmIntern = small_icon;
781 break;
782 case GCLP_HICONSM:
783 retval = (ULONG_PTR)class->hIconSm;
784 if (retval == newval) break;
785 if (retval && !newval && class->hIcon)
787 HICON icon = class->hIcon;
788 release_class_ptr( class );
790 small_icon = CopyImage( icon, IMAGE_ICON,
791 get_system_metrics( SM_CXSMICON ),
792 get_system_metrics( SM_CYSMICON ),
793 LR_COPYFROMRESOURCE );
795 if (!(class = get_class_ptr( hwnd, TRUE )))
797 NtUserDestroyCursor( small_icon, 0 );
798 return 0;
800 if (class->hIcon != icon || !class->hIconSm)
802 /* someone beat us, restart */
803 release_class_ptr( class );
804 NtUserDestroyCursor( small_icon, 0 );
805 return set_class_long( hwnd, offset, newval, size, ansi );
808 if (class->hIconSmIntern) NtUserDestroyCursor( class->hIconSmIntern, 0 );
809 class->hIconSm = (HICON)newval;
810 class->hIconSmIntern = small_icon;
811 break;
812 case GCL_STYLE:
813 if (!set_server_info( hwnd, offset, newval, size )) break;
814 retval = class->style;
815 class->style = newval;
816 break;
817 case GCL_CBWNDEXTRA:
818 if (!set_server_info( hwnd, offset, newval, size )) break;
819 retval = class->cbWndExtra;
820 class->cbWndExtra = newval;
821 break;
822 case GCLP_HMODULE:
823 if (!set_server_info( hwnd, offset, newval, size )) break;
824 retval = class->instance;
825 class->instance = newval;
826 break;
827 case GCW_ATOM:
829 UNICODE_STRING us;
830 if (!set_server_info( hwnd, offset, newval, size )) break;
831 retval = class->atomName;
832 class->atomName = newval;
833 us.Buffer = class->name;
834 us.MaximumLength = sizeof(class->name);
835 NtUserGetAtomName( newval, &us );
837 break;
838 case GCL_CBCLSEXTRA: /* cannot change this one */
839 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
840 break;
841 default:
842 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
843 break;
845 release_class_ptr( class );
846 return retval;
849 /***********************************************************************
850 * NtUserSetClassLong (win32u.@)
852 DWORD WINAPI NtUserSetClassLong( HWND hwnd, INT offset, LONG newval, BOOL ansi )
854 return set_class_long( hwnd, offset, newval, sizeof(LONG), ansi );
857 /***********************************************************************
858 * NtUserSetClassLongPtr (win32u.@)
860 ULONG_PTR WINAPI NtUserSetClassLongPtr( HWND hwnd, INT offset, LONG_PTR newval, BOOL ansi )
862 return set_class_long( hwnd, offset, newval, sizeof(LONG_PTR), ansi );
865 /***********************************************************************
866 * NtUserSetClassWord (win32u.@)
868 WORD WINAPI NtUserSetClassWord( HWND hwnd, INT offset, WORD newval )
870 CLASS *class;
871 WORD retval = 0;
873 if (offset < 0) return NtUserSetClassLong( hwnd, offset, (DWORD)newval, TRUE );
875 if (!(class = get_class_ptr( hwnd, TRUE ))) return 0;
877 SERVER_START_REQ( set_class_info )
879 req->window = wine_server_user_handle( hwnd );
880 req->flags = SET_CLASS_EXTRA;
881 req->extra_offset = offset;
882 req->extra_size = sizeof(newval);
883 memcpy( &req->extra_value, &newval, sizeof(newval) );
884 if (!wine_server_call_err( req ))
886 void *ptr = (char *)(class + 1) + offset;
887 memcpy( &retval, ptr, sizeof(retval) );
888 memcpy( ptr, &newval, sizeof(newval) );
891 SERVER_END_REQ;
892 release_class_ptr( class );
893 return retval;
896 static ULONG_PTR get_class_long_size( HWND hwnd, INT offset, UINT size, BOOL ansi )
898 CLASS *class;
899 ULONG_PTR retvalue = 0;
901 if (!(class = get_class_ptr( hwnd, FALSE ))) return 0;
903 if (class == OBJ_OTHER_PROCESS)
905 SERVER_START_REQ( set_class_info )
907 req->window = wine_server_user_handle( hwnd );
908 req->flags = 0;
909 req->extra_offset = (offset >= 0) ? offset : -1;
910 req->extra_size = (offset >= 0) ? size : 0;
911 if (!wine_server_call_err( req ))
913 switch(offset)
915 case GCLP_HBRBACKGROUND:
916 case GCLP_HCURSOR:
917 case GCLP_HICON:
918 case GCLP_HICONSM:
919 case GCLP_WNDPROC:
920 case GCLP_MENUNAME:
921 FIXME( "offset %d not supported on other process window %p\n", offset, hwnd );
922 RtlSetLastWin32Error( ERROR_INVALID_HANDLE );
923 break;
924 case GCL_STYLE:
925 retvalue = reply->old_style;
926 break;
927 case GCL_CBWNDEXTRA:
928 retvalue = reply->old_win_extra;
929 break;
930 case GCL_CBCLSEXTRA:
931 retvalue = reply->old_extra;
932 break;
933 case GCLP_HMODULE:
934 retvalue = (ULONG_PTR)wine_server_get_ptr( reply->old_instance );
935 break;
936 case GCW_ATOM:
937 retvalue = reply->old_atom;
938 break;
939 default:
940 if (offset >= 0)
942 if (size == sizeof(DWORD))
944 DWORD retdword;
945 memcpy( &retdword, &reply->old_extra_value, sizeof(DWORD) );
946 retvalue = retdword;
948 else
949 memcpy( &retvalue, &reply->old_extra_value,
950 sizeof(ULONG_PTR) );
952 else RtlSetLastWin32Error( ERROR_INVALID_INDEX );
953 break;
957 SERVER_END_REQ;
958 return retvalue;
961 if (offset >= 0)
963 if (offset <= class->cbClsExtra - size)
965 if (size == sizeof(DWORD))
967 DWORD retdword;
968 memcpy( &retdword, (char *)(class + 1) + offset, sizeof(DWORD) );
969 retvalue = retdword;
971 else
972 memcpy( &retvalue, (char *)(class + 1) + offset, sizeof(ULONG_PTR) );
974 else
975 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
976 release_class_ptr( class );
977 return retvalue;
980 switch(offset)
982 case GCLP_HBRBACKGROUND:
983 retvalue = (ULONG_PTR)class->hbrBackground;
984 break;
985 case GCLP_HCURSOR:
986 retvalue = (ULONG_PTR)class->hCursor;
987 break;
988 case GCLP_HICON:
989 retvalue = (ULONG_PTR)class->hIcon;
990 break;
991 case GCLP_HICONSM:
992 retvalue = (ULONG_PTR)(class->hIconSm ? class->hIconSm : class->hIconSmIntern);
993 break;
994 case GCL_STYLE:
995 retvalue = class->style;
996 break;
997 case GCL_CBWNDEXTRA:
998 retvalue = class->cbWndExtra;
999 break;
1000 case GCL_CBCLSEXTRA:
1001 retvalue = class->cbClsExtra;
1002 break;
1003 case GCLP_HMODULE:
1004 retvalue = class->instance;
1005 break;
1006 case GCLP_WNDPROC:
1007 retvalue = (ULONG_PTR)get_winproc( class->winproc, ansi );
1008 break;
1009 case GCLP_MENUNAME:
1010 retvalue = ansi ? (ULONG_PTR)class->menu_name.nameA : (ULONG_PTR)class->menu_name.nameW;
1011 break;
1012 case GCW_ATOM:
1013 retvalue = class->atomName;
1014 break;
1015 default:
1016 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1017 break;
1019 release_class_ptr( class );
1020 return retvalue;
1023 DWORD get_class_long( HWND hwnd, INT offset, BOOL ansi )
1025 return get_class_long_size( hwnd, offset, sizeof(DWORD), ansi );
1028 ULONG_PTR get_class_long_ptr( HWND hwnd, INT offset, BOOL ansi )
1030 return get_class_long_size( hwnd, offset, sizeof(ULONG_PTR), ansi );
1033 WORD get_class_word( HWND hwnd, INT offset )
1035 CLASS *class;
1036 WORD retvalue = 0;
1038 if (offset < 0) return get_class_long( hwnd, offset, TRUE );
1040 if (!(class = get_class_ptr( hwnd, FALSE ))) return 0;
1042 if (class == OBJ_OTHER_PROCESS)
1044 SERVER_START_REQ( set_class_info )
1046 req->window = wine_server_user_handle( hwnd );
1047 req->flags = 0;
1048 req->extra_offset = offset;
1049 req->extra_size = sizeof(retvalue);
1050 if (!wine_server_call_err( req ))
1051 memcpy( &retvalue, &reply->old_extra_value, sizeof(retvalue) );
1053 SERVER_END_REQ;
1054 return retvalue;
1057 if (offset <= class->cbClsExtra - sizeof(WORD))
1058 memcpy( &retvalue, (char *)(class + 1) + offset, sizeof(retvalue) );
1059 else
1060 RtlSetLastWin32Error( ERROR_INVALID_INDEX );
1061 release_class_ptr( class );
1062 return retvalue;
1065 BOOL needs_ime_window( HWND hwnd )
1067 static const WCHAR imeW[] = {'I','M','E',0};
1068 CLASS *class;
1069 BOOL ret;
1071 if (!(class = get_class_ptr( hwnd, FALSE ))) return FALSE;
1072 ret = !(class->style & CS_IME) && wcscmp( imeW, class->name );
1073 release_class_ptr( class );
1074 return ret;
1077 static const struct builtin_class_descr desktop_builtin_class =
1079 .name = MAKEINTRESOURCEA(DESKTOP_CLASS_ATOM),
1080 .style = CS_DBLCLKS,
1081 .proc = WINPROC_DESKTOP,
1082 .brush = (HBRUSH)(COLOR_BACKGROUND + 1),
1085 static const struct builtin_class_descr message_builtin_class =
1087 .name = "Message",
1088 .proc = WINPROC_MESSAGE,
1091 static const struct builtin_class_descr builtin_classes[] =
1093 /* button */
1095 .name = "Button",
1096 .style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
1097 .proc = WINPROC_BUTTON,
1098 .extra = sizeof(UINT) + 2 * sizeof(HANDLE),
1099 .cursor = IDC_ARROW,
1101 /* combo */
1103 .name = "ComboBox",
1104 .style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
1105 .proc = WINPROC_COMBO,
1106 .extra = sizeof(void *),
1107 .cursor = IDC_ARROW,
1109 /* combolbox */
1111 .name = "ComboLBox",
1112 .style = CS_DBLCLKS | CS_SAVEBITS,
1113 .proc = WINPROC_LISTBOX,
1114 .extra = sizeof(void *),
1115 .cursor = IDC_ARROW,
1117 /* dialog */
1119 .name = MAKEINTRESOURCEA(DIALOG_CLASS_ATOM),
1120 .style = CS_SAVEBITS | CS_DBLCLKS,
1121 .proc = WINPROC_DIALOG,
1122 .extra = DLGWINDOWEXTRA,
1123 .cursor = IDC_ARROW,
1125 /* edit */
1127 .name = "Edit",
1128 .style = CS_DBLCLKS | CS_PARENTDC,
1129 .proc = WINPROC_EDIT,
1130 .extra = sizeof(UINT64),
1131 .cursor = IDC_IBEAM,
1133 /* icon title */
1135 .name = MAKEINTRESOURCEA(ICONTITLE_CLASS_ATOM),
1136 .proc = WINPROC_ICONTITLE,
1137 .cursor = IDC_ARROW,
1139 /* IME */
1141 .name = "IME",
1142 .proc = WINPROC_IME,
1143 .extra = 2 * sizeof(LONG_PTR),
1144 .cursor = IDC_ARROW,
1146 /* listbox */
1148 .name = "ListBox",
1149 .style = CS_DBLCLKS,
1150 .proc = WINPROC_LISTBOX,
1151 .extra = sizeof(void *),
1152 .cursor = IDC_ARROW,
1154 /* menu */
1156 .name = MAKEINTRESOURCEA(POPUPMENU_CLASS_ATOM),
1157 .style = CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,
1158 .proc = WINPROC_MENU,
1159 .extra = sizeof(HMENU),
1160 .cursor = IDC_ARROW,
1161 .brush = (HBRUSH)(COLOR_MENU + 1),
1163 /* MDIClient */
1165 .name = "MDIClient",
1166 .proc = WINPROC_MDICLIENT,
1167 .extra = 2 * sizeof(void *),
1168 .cursor = IDC_ARROW,
1169 .brush = (HBRUSH)(COLOR_APPWORKSPACE + 1),
1171 /* scrollbar */
1173 .name = "ScrollBar",
1174 .style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC,
1175 .proc = WINPROC_SCROLLBAR,
1176 .extra = sizeof(struct scroll_bar_win_data),
1177 .cursor = IDC_ARROW,
1179 /* static */
1181 .name = "Static",
1182 .style = CS_DBLCLKS | CS_PARENTDC,
1183 .proc = WINPROC_STATIC,
1184 .extra = 2 * sizeof(HANDLE),
1185 .cursor = IDC_ARROW,
1189 /***********************************************************************
1190 * register_builtin
1192 * Register a builtin control class.
1193 * This allows having both ANSI and Unicode winprocs for the same class.
1195 static void register_builtin( const struct builtin_class_descr *descr )
1197 UNICODE_STRING name, version = { .Length = 0 };
1198 struct client_menu_name menu_name = { 0 };
1199 WCHAR nameW[64];
1200 WNDCLASSEXW class = {
1201 .cbSize = sizeof(class),
1202 .hInstance = user32_module,
1203 .style = descr->style,
1204 .cbWndExtra = descr->extra,
1205 .hbrBackground = descr->brush,
1206 .lpfnWndProc = BUILTIN_WINPROC( descr->proc ),
1209 if (descr->cursor)
1210 class.hCursor = LoadImageW( 0, (const WCHAR *)descr->cursor, IMAGE_CURSOR,
1211 0, 0, LR_SHARED | LR_DEFAULTSIZE );
1213 if (IS_INTRESOURCE( descr->name ))
1215 name.Buffer = (WCHAR *)descr->name;
1216 name.Length = name.MaximumLength = 0;
1218 else
1220 asciiz_to_unicode( nameW, descr->name );
1221 RtlInitUnicodeString( &name, nameW );
1224 if (!NtUserRegisterClassExWOW( &class, &name, &version, &menu_name, 1, 0, NULL ) && class.hCursor)
1225 NtUserDestroyCursor( class.hCursor, 0 );
1228 static void register_builtins(void)
1230 ULONG ret_len, i;
1231 void *ret_ptr;
1233 for (i = 0; i < ARRAYSIZE(builtin_classes); i++) register_builtin( &builtin_classes[i] );
1234 KeUserModeCallback( NtUserInitBuiltinClasses, NULL, 0, &ret_ptr, &ret_len );
1237 /***********************************************************************
1238 * register_builtin_classes
1240 void register_builtin_classes(void)
1242 static pthread_once_t init_once = PTHREAD_ONCE_INIT;
1243 pthread_once( &init_once, register_builtins );
1246 /***********************************************************************
1247 * register_desktop_class
1249 void register_desktop_class(void)
1251 register_builtin( &desktop_builtin_class );
1252 register_builtin( &message_builtin_class );