2022-03-04 14:27:58 +01:00
|
|
|
/*
|
|
|
|
* 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>
|
2022-03-07 14:42:47 +01:00
|
|
|
#include <assert.h>
|
2022-03-04 14:27:58 +01:00
|
|
|
#include "ntstatus.h"
|
|
|
|
#define WIN32_NO_STATUS
|
|
|
|
#include "win32u_private.h"
|
|
|
|
#include "ntuser_private.h"
|
2022-03-04 14:28:31 +01:00
|
|
|
#include "wine/server.h"
|
2022-03-04 14:27:58 +01:00
|
|
|
#include "wine/debug.h"
|
|
|
|
|
2022-03-04 14:28:31 +01:00
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(class);
|
2022-03-04 14:27:58 +01:00
|
|
|
WINE_DECLARE_DEBUG_CHANNEL(win);
|
|
|
|
|
|
|
|
#define MAX_WINPROCS 4096
|
|
|
|
#define WINPROC_PROC16 ((WINDOWPROC *)1) /* placeholder for 16-bit window procs */
|
|
|
|
|
|
|
|
static WINDOWPROC winproc_array[MAX_WINPROCS];
|
|
|
|
static UINT winproc_used = NB_BUILTIN_WINPROCS;
|
|
|
|
static pthread_mutex_t winproc_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
2022-03-07 14:42:32 +01:00
|
|
|
static struct list class_list = LIST_INIT( class_list );
|
|
|
|
|
|
|
|
static HINSTANCE user32_module;
|
|
|
|
|
2022-03-04 14:27:58 +01:00
|
|
|
/* 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 < NB_BUILTIN_AW_WINPROCS; 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 = NB_BUILTIN_AW_WINPROCS; 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. */
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2022-03-07 14:42:32 +01:00
|
|
|
/* Get a window procedure pointer that can be passed to the Windows program. */
|
2022-03-08 14:23:52 +01:00
|
|
|
WNDPROC get_winproc( WNDPROC proc, BOOL ansi )
|
2022-03-07 14:42:32 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 15:35:25 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2022-03-04 14:27:58 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* NtUserInitializeClientPfnArrays (win32u.@)
|
|
|
|
*/
|
|
|
|
NTSTATUS WINAPI NtUserInitializeClientPfnArrays( const struct user_client_procs *client_procsA,
|
|
|
|
const struct user_client_procs *client_procsW,
|
|
|
|
const void *client_workers, HINSTANCE user_module )
|
|
|
|
{
|
|
|
|
winproc_array[WINPROC_BUTTON].procA = client_procsA->pButtonWndProc;
|
|
|
|
winproc_array[WINPROC_BUTTON].procW = client_procsW->pButtonWndProc;
|
|
|
|
winproc_array[WINPROC_COMBO].procA = client_procsA->pComboWndProc;
|
|
|
|
winproc_array[WINPROC_COMBO].procW = client_procsW->pComboWndProc;
|
|
|
|
winproc_array[WINPROC_DEFWND].procA = client_procsA->pDefWindowProc;
|
|
|
|
winproc_array[WINPROC_DEFWND].procW = client_procsW->pDefWindowProc;
|
|
|
|
winproc_array[WINPROC_DIALOG].procA = client_procsA->pDefDlgProc;
|
|
|
|
winproc_array[WINPROC_DIALOG].procW = client_procsW->pDefDlgProc;
|
|
|
|
winproc_array[WINPROC_EDIT].procA = client_procsA->pEditWndProc;
|
|
|
|
winproc_array[WINPROC_EDIT].procW = client_procsW->pEditWndProc;
|
|
|
|
winproc_array[WINPROC_LISTBOX].procA = client_procsA->pListBoxWndProc;
|
|
|
|
winproc_array[WINPROC_LISTBOX].procW = client_procsW->pListBoxWndProc;
|
|
|
|
winproc_array[WINPROC_MDICLIENT].procA = client_procsA->pMDIClientWndProc;
|
|
|
|
winproc_array[WINPROC_MDICLIENT].procW = client_procsW->pMDIClientWndProc;
|
|
|
|
winproc_array[WINPROC_SCROLLBAR].procA = client_procsA->pScrollBarWndProc;
|
|
|
|
winproc_array[WINPROC_SCROLLBAR].procW = client_procsW->pScrollBarWndProc;
|
|
|
|
winproc_array[WINPROC_STATIC].procA = client_procsA->pStaticWndProc;
|
|
|
|
winproc_array[WINPROC_STATIC].procW = client_procsW->pStaticWndProc;
|
|
|
|
winproc_array[WINPROC_IME].procA = client_procsA->pImeWndProc;
|
|
|
|
winproc_array[WINPROC_IME].procW = client_procsW->pImeWndProc;
|
|
|
|
winproc_array[WINPROC_DESKTOP].procA = client_procsA->pDesktopWndProc;
|
|
|
|
winproc_array[WINPROC_DESKTOP].procW = client_procsW->pDesktopWndProc;
|
|
|
|
winproc_array[WINPROC_ICONTITLE].procA = client_procsA->pIconTitleWndProc;
|
|
|
|
winproc_array[WINPROC_ICONTITLE].procW = client_procsW->pIconTitleWndProc;
|
|
|
|
winproc_array[WINPROC_MENU].procA = client_procsA->pPopupMenuWndProc;
|
|
|
|
winproc_array[WINPROC_MENU].procW = client_procsW->pPopupMenuWndProc;
|
|
|
|
winproc_array[WINPROC_MESSAGE].procA = client_procsA->pMessageWndProc;
|
|
|
|
winproc_array[WINPROC_MESSAGE].procW = client_procsW->pMessageWndProc;
|
|
|
|
|
2022-03-07 14:42:32 +01:00
|
|
|
user32_module = user_module;
|
2022-03-04 14:27:58 +01:00
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
2022-03-04 14:28:16 +01:00
|
|
|
|
2022-03-07 14:42:32 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* get_int_atom_value
|
|
|
|
*/
|
2022-03-11 14:21:31 +01:00
|
|
|
ATOM get_int_atom_value( UNICODE_STRING *name )
|
2022-03-07 14:42:32 +01:00
|
|
|
{
|
|
|
|
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 > 0xffff) return 0;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-03-04 14:28:31 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* 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 ))
|
|
|
|
{
|
|
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* release_class_ptr
|
|
|
|
*/
|
2022-03-07 14:42:32 +01:00
|
|
|
static void release_class_ptr( CLASS *ptr )
|
|
|
|
{
|
|
|
|
user_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
static CLASS *find_class( HINSTANCE module, UNICODE_STRING *name )
|
2022-03-04 14:28:31 +01:00
|
|
|
{
|
2022-03-07 14:42:32 +01:00
|
|
|
ATOM atom = get_int_atom_value( name );
|
|
|
|
ULONG_PTR instance = (UINT_PTR)module & ~0xffff;
|
|
|
|
CLASS *class;
|
|
|
|
|
|
|
|
user_lock();
|
|
|
|
LIST_FOR_EACH_ENTRY( class, &class_list, CLASS, entry )
|
|
|
|
{
|
|
|
|
if (atom)
|
|
|
|
{
|
|
|
|
if (class->atomName != atom) continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-03-15 14:26:39 +01:00
|
|
|
if (wcsnicmp( class->name, name->Buffer, name->Length / sizeof(WCHAR) ) ||
|
|
|
|
class->name[name->Length / sizeof(WCHAR)]) continue;
|
2022-03-07 14:42:32 +01:00
|
|
|
}
|
|
|
|
if (!class->local || !module || (class->instance & ~0xffff) == instance)
|
|
|
|
{
|
|
|
|
TRACE( "%s %lx -> %p\n", debugstr_us(name), instance, class );
|
|
|
|
return class;
|
|
|
|
}
|
|
|
|
}
|
2022-03-04 14:28:31 +01:00
|
|
|
user_unlock();
|
2022-03-07 14:42:32 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-03-14 11:43:55 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2022-03-07 14:42:32 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* 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;
|
|
|
|
CLASS *class;
|
|
|
|
ATOM atom;
|
|
|
|
BOOL ret;
|
|
|
|
|
|
|
|
/* create the desktop window to trigger builtin class registration */
|
2022-03-09 15:20:01 +01:00
|
|
|
if (!is_builtin) get_desktop_window();
|
2022-03-07 14:42:32 +01:00
|
|
|
|
|
|
|
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 */
|
|
|
|
{
|
|
|
|
SetLastError( 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;
|
|
|
|
|
|
|
|
class->atomName = get_int_atom_value( name );
|
|
|
|
class->basename = class->name;
|
|
|
|
if (!class->atomName && name)
|
|
|
|
{
|
|
|
|
memcpy( class->name, name->Buffer, name->Length );
|
|
|
|
class->name[name->Length / sizeof(WCHAR)] = 0;
|
|
|
|
class->basename += version->Length / sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UNICODE_STRING str = { .MaximumLength = sizeof(class->name), .Buffer = class->name };
|
|
|
|
NtUserGetAtomName( class->atomName, &str );
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = class->atomName;
|
|
|
|
req->name_offset = version->Length / sizeof(WCHAR);
|
|
|
|
if (!req->atom && name) wine_server_add_data( req, name->Buffer, name->Length );
|
|
|
|
ret = !wine_server_call_err( req );
|
|
|
|
class->atomName = reply->atom;
|
|
|
|
}
|
|
|
|
SERVER_END_REQ;
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
free( class );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Other non-null values must be set by caller */
|
|
|
|
|
|
|
|
user_lock();
|
|
|
|
if (class->local) list_add_head( &class_list, &class->entry );
|
|
|
|
else list_add_tail( &class_list, &class->entry );
|
|
|
|
|
|
|
|
atom = class->atomName;
|
|
|
|
|
|
|
|
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->hCursor = wc->hCursor;
|
|
|
|
class->hbrBackground = wc->hbrBackground;
|
|
|
|
class->winproc = alloc_winproc( wc->lpfnWndProc, ansi );
|
|
|
|
class->menu_name = *client_menu_name;
|
|
|
|
if (wc->hIcon && !wc->hIconSm && user_callbacks)
|
|
|
|
class->hIconSmIntern = user_callbacks->pCopyImage( wc->hIcon, IMAGE_ICON,
|
|
|
|
get_system_metrics( SM_CXSMICON ),
|
|
|
|
get_system_metrics( SM_CYSMICON ),
|
|
|
|
LR_COPYFROMRESOURCE );
|
|
|
|
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;
|
|
|
|
|
2022-03-09 15:20:01 +01:00
|
|
|
/* create the desktop window to trigger builtin class registration */
|
|
|
|
get_desktop_window();
|
2022-03-07 14:42:32 +01:00
|
|
|
|
|
|
|
SERVER_START_REQ( destroy_class )
|
|
|
|
{
|
|
|
|
req->instance = wine_server_client_ptr( instance );
|
|
|
|
if (!(req->atom = get_int_atom_value( name )) && name->Length)
|
|
|
|
wine_server_add_data( req, name->Buffer, name->Length );
|
|
|
|
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();
|
2022-03-14 11:43:55 +01:00
|
|
|
if (class->dce) free_dce( class->dce, 0 );
|
2022-03-07 14:42:32 +01:00
|
|
|
list_remove( &class->entry );
|
|
|
|
if (class->hbrBackground > (HBRUSH)(COLOR_GRADIENTINACTIVECAPTION + 1))
|
|
|
|
NtGdiDeleteObjectApp( class->hbrBackground );
|
|
|
|
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 )
|
|
|
|
{
|
|
|
|
static const WCHAR messageW[] = {'M','e','s','s','a','g','e'};
|
|
|
|
CLASS *class;
|
|
|
|
ATOM atom;
|
|
|
|
|
|
|
|
/* create the desktop window to trigger builtin class registration */
|
|
|
|
if (name->Buffer != (const WCHAR *)DESKTOP_CLASS_ATOM &&
|
|
|
|
(IS_INTRESOURCE(name->Buffer) || name->Length != sizeof(messageW) ||
|
|
|
|
wcsnicmp( name->Buffer, messageW, ARRAYSIZE(messageW) )))
|
2022-03-09 15:20:01 +01:00
|
|
|
get_desktop_window();
|
2022-03-07 14:42:32 +01:00
|
|
|
|
|
|
|
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->atomName;
|
|
|
|
release_class_ptr( class );
|
|
|
|
return atom;
|
2022-03-04 14:28:31 +01:00
|
|
|
}
|
|
|
|
|
2022-03-04 14:28:16 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* NtUserGetAtomName (win32u.@)
|
|
|
|
*/
|
|
|
|
ULONG WINAPI NtUserGetAtomName( ATOM atom, UNICODE_STRING *name )
|
|
|
|
{
|
|
|
|
char buf[sizeof(ATOM_BASIC_INFORMATION) + MAX_ATOM_LEN * sizeof(WCHAR)];
|
|
|
|
ATOM_BASIC_INFORMATION *abi = (ATOM_BASIC_INFORMATION *)buf;
|
|
|
|
UINT size;
|
|
|
|
|
|
|
|
if (!set_ntstatus( NtQueryInformationAtom( atom, AtomBasicInformation,
|
|
|
|
buf, sizeof(buf), NULL )))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (name->MaximumLength < sizeof(WCHAR))
|
|
|
|
{
|
|
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = min( abi->NameLength, name->MaximumLength - sizeof(WCHAR) );
|
|
|
|
if (size) memcpy( name->Buffer, abi->Name, size );
|
|
|
|
name->Buffer[size / sizeof(WCHAR)] = 0;
|
|
|
|
return size / sizeof(WCHAR);
|
|
|
|
}
|
2022-03-04 14:28:31 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* 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))
|
|
|
|
{
|
|
|
|
SetLastError( 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( set_class_info )
|
|
|
|
{
|
|
|
|
req->window = wine_server_user_handle( hwnd );
|
|
|
|
req->flags = 0;
|
|
|
|
req->extra_offset = -1;
|
|
|
|
req->extra_size = 0;
|
|
|
|
if (!wine_server_call_err( req ))
|
|
|
|
atom = reply->base_atom;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2022-03-07 14:42:47 +01:00
|
|
|
|
|
|
|
/* 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->extra_offset = -1;
|
|
|
|
switch(offset)
|
|
|
|
{
|
|
|
|
case GCW_ATOM:
|
|
|
|
req->flags = SET_CLASS_ATOM;
|
|
|
|
req->atom = LOWORD(newval);
|
|
|
|
break;
|
|
|
|
case GCL_STYLE:
|
|
|
|
req->flags = SET_CLASS_STYLE;
|
|
|
|
req->style = newval;
|
|
|
|
break;
|
|
|
|
case GCL_CBWNDEXTRA:
|
|
|
|
req->flags = SET_CLASS_WINEXTRA;
|
|
|
|
req->win_extra = newval;
|
|
|
|
break;
|
|
|
|
case GCLP_HMODULE:
|
|
|
|
req->flags = SET_CLASS_INSTANCE;
|
|
|
|
req->instance = wine_server_client_ptr( (void *)newval );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert( offset >= 0 );
|
|
|
|
req->flags = SET_CLASS_EXTRA;
|
|
|
|
req->extra_offset = offset;
|
|
|
|
req->extra_size = size;
|
|
|
|
if ( size == sizeof(LONG) )
|
|
|
|
{
|
|
|
|
LONG newlong = newval;
|
|
|
|
memcpy( &req->extra_value, &newlong, sizeof(LONG) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
memcpy( &req->extra_value, &newval, sizeof(LONG_PTR) );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = !wine_server_call_err( req );
|
|
|
|
}
|
|
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG_PTR set_class_long( HWND hwnd, INT offset, LONG_PTR newval, UINT size, BOOL ansi )
|
|
|
|
{
|
|
|
|
ULONG_PTR retval = 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;
|
|
|
|
if ( size == sizeof(LONG) )
|
|
|
|
{
|
|
|
|
DWORD retdword;
|
|
|
|
LONG newlong = newval;
|
|
|
|
memcpy( &retdword, ptr, sizeof(DWORD) );
|
|
|
|
memcpy( ptr, &newlong, sizeof(LONG) );
|
|
|
|
retval = retdword;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy( &retval, ptr, sizeof(ULONG_PTR) );
|
|
|
|
memcpy( ptr, &newval, sizeof(LONG_PTR) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 (class->hIconSmIntern)
|
|
|
|
{
|
|
|
|
NtUserDestroyCursor( class->hIconSmIntern, 0 );
|
|
|
|
class->hIconSmIntern = NULL;
|
|
|
|
}
|
|
|
|
if (newval && !class->hIconSm && user_callbacks)
|
|
|
|
class->hIconSmIntern = user_callbacks->pCopyImage( (HICON)newval, IMAGE_ICON,
|
|
|
|
get_system_metrics( SM_CXSMICON ),
|
|
|
|
get_system_metrics( SM_CYSMICON ),
|
|
|
|
LR_COPYFROMRESOURCE );
|
|
|
|
class->hIcon = (HICON)newval;
|
|
|
|
break;
|
|
|
|
case GCLP_HICONSM:
|
|
|
|
retval = (ULONG_PTR)class->hIconSm;
|
|
|
|
if (retval && !newval && class->hIcon && user_callbacks)
|
|
|
|
class->hIconSmIntern = user_callbacks->pCopyImage( class->hIcon, IMAGE_ICON,
|
|
|
|
get_system_metrics( SM_CXSMICON ),
|
|
|
|
get_system_metrics( SM_CYSMICON ),
|
|
|
|
LR_COPYFROMRESOURCE );
|
|
|
|
else if (newval && class->hIconSmIntern)
|
|
|
|
{
|
|
|
|
NtUserDestroyCursor( class->hIconSmIntern, 0 );
|
|
|
|
class->hIconSmIntern = NULL;
|
|
|
|
}
|
|
|
|
class->hIconSm = (HICON)newval;
|
|
|
|
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 GCW_ATOM:
|
|
|
|
{
|
|
|
|
UNICODE_STRING us;
|
|
|
|
if (!set_server_info( hwnd, offset, newval, size )) break;
|
|
|
|
retval = class->atomName;
|
|
|
|
class->atomName = newval;
|
|
|
|
us.Buffer = class->name;
|
|
|
|
us.MaximumLength = sizeof(class->name);
|
|
|
|
NtUserGetAtomName( newval, &us );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GCL_CBCLSEXTRA: /* cannot change this one */
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SetLastError( 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( 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( hwnd, offset, newval, sizeof(LONG_PTR), ansi );
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* NtUserSetClassWord (win32u.@)
|
|
|
|
*/
|
|
|
|
WORD WINAPI NtUserSetClassWord( HWND hwnd, INT offset, WORD newval )
|
|
|
|
{
|
|
|
|
CLASS *class;
|
|
|
|
WORD retval = 0;
|
|
|
|
|
|
|
|
if (offset < 0) return NtUserSetClassLong( hwnd, offset, (DWORD)newval, TRUE );
|
|
|
|
|
|
|
|
if (!(class = get_class_ptr( hwnd, TRUE ))) return 0;
|
|
|
|
|
|
|
|
SERVER_START_REQ( set_class_info )
|
|
|
|
{
|
|
|
|
req->window = wine_server_user_handle( hwnd );
|
|
|
|
req->flags = SET_CLASS_EXTRA;
|
|
|
|
req->extra_offset = offset;
|
|
|
|
req->extra_size = sizeof(newval);
|
|
|
|
memcpy( &req->extra_value, &newval, sizeof(newval) );
|
|
|
|
if (!wine_server_call_err( req ))
|
|
|
|
{
|
|
|
|
void *ptr = (char *)(class + 1) + offset;
|
|
|
|
memcpy( &retval, ptr, sizeof(retval) );
|
|
|
|
memcpy( ptr, &newval, sizeof(newval) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SERVER_END_REQ;
|
|
|
|
release_class_ptr( class );
|
|
|
|
return retval;
|
|
|
|
}
|
2022-03-07 14:43:07 +01:00
|
|
|
|
|
|
|
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( set_class_info )
|
|
|
|
{
|
|
|
|
req->window = wine_server_user_handle( hwnd );
|
|
|
|
req->flags = 0;
|
|
|
|
req->extra_offset = (offset >= 0) ? offset : -1;
|
|
|
|
req->extra_size = (offset >= 0) ? size : 0;
|
|
|
|
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 );
|
|
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
|
|
break;
|
|
|
|
case GCL_STYLE:
|
|
|
|
retvalue = reply->old_style;
|
|
|
|
break;
|
|
|
|
case GCL_CBWNDEXTRA:
|
|
|
|
retvalue = reply->old_win_extra;
|
|
|
|
break;
|
|
|
|
case GCL_CBCLSEXTRA:
|
|
|
|
retvalue = reply->old_extra;
|
|
|
|
break;
|
|
|
|
case GCLP_HMODULE:
|
|
|
|
retvalue = (ULONG_PTR)wine_server_get_ptr( reply->old_instance );
|
|
|
|
break;
|
|
|
|
case GCW_ATOM:
|
|
|
|
retvalue = reply->old_atom;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (offset >= 0)
|
|
|
|
{
|
|
|
|
if (size == sizeof(DWORD))
|
|
|
|
{
|
|
|
|
DWORD retdword;
|
|
|
|
memcpy( &retdword, &reply->old_extra_value, sizeof(DWORD) );
|
|
|
|
retvalue = retdword;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
memcpy( &retvalue, &reply->old_extra_value,
|
|
|
|
sizeof(ULONG_PTR) );
|
|
|
|
}
|
|
|
|
else SetLastError( ERROR_INVALID_INDEX );
|
|
|
|
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
|
|
|
|
SetLastError( 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->atomName;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SetLastError( 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 )
|
|
|
|
{
|
|
|
|
CLASS *class;
|
|
|
|
WORD retvalue = 0;
|
|
|
|
|
|
|
|
if (offset < 0) return get_class_long( hwnd, offset, TRUE );
|
|
|
|
|
|
|
|
if (!(class = get_class_ptr( hwnd, FALSE ))) return 0;
|
|
|
|
|
|
|
|
if (class == OBJ_OTHER_PROCESS)
|
|
|
|
{
|
|
|
|
SERVER_START_REQ( set_class_info )
|
|
|
|
{
|
|
|
|
req->window = wine_server_user_handle( hwnd );
|
|
|
|
req->flags = 0;
|
|
|
|
req->extra_offset = offset;
|
|
|
|
req->extra_size = sizeof(retvalue);
|
|
|
|
if (!wine_server_call_err( req ))
|
|
|
|
memcpy( &retvalue, &reply->old_extra_value, sizeof(retvalue) );
|
|
|
|
}
|
|
|
|
SERVER_END_REQ;
|
|
|
|
return retvalue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset <= class->cbClsExtra - sizeof(WORD))
|
|
|
|
memcpy( &retvalue, (char *)(class + 1) + offset, sizeof(retvalue) );
|
|
|
|
else
|
|
|
|
SetLastError( ERROR_INVALID_INDEX );
|
|
|
|
release_class_ptr( class );
|
|
|
|
return retvalue;
|
|
|
|
}
|