Added window classes in the server, and support for inter-process
GetClassWord/Long (based on a patch by Mike McCormack).
Various fixes to the class instance handling.

diff --git a/server/Makefile.in b/server/Makefile.in
index 1955d67..2f4275d 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -9,6 +9,7 @@
 	async.c \
 	atom.c \
 	change.c \
+	class.c \
 	clipboard.c \
 	console.c \
 	context_i386.c \
diff --git a/server/class.c b/server/class.c
new file mode 100644
index 0000000..13d9d5c
--- /dev/null
+++ b/server/class.c
@@ -0,0 +1,204 @@
+/*
+ * Server-side window class management
+ *
+ * Copyright (C) 2002 Mike McCormack
+ * Copyright (C) 2003 Alexandre Julliard
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "wine/list.h"
+
+#include "request.h"
+#include "object.h"
+#include "process.h"
+#include "user.h"
+
+struct window_class
+{
+    struct list     entry;           /* entry in process list */
+    struct process *process;         /* process owning the class */
+    int             count;           /* reference count */
+    int             local;           /* local class? */
+    atom_t          atom;            /* class atom */
+    void           *instance;        /* module instance */
+    unsigned int    style;           /* class style */
+    int             win_extra;       /* number of window extra bytes */
+    int             nb_extra_bytes;  /* number of extra bytes */
+    char            extra_bytes[1];  /* extra bytes storage */
+};
+
+static struct window_class *create_class( struct process *process, int extra_bytes, int local )
+{
+    struct window_class *class;
+
+    if (!(class = mem_alloc( sizeof(*class) + extra_bytes - 1 ))) return NULL;
+
+    class->process = (struct process *)grab_object( process );
+    class->count = 0;
+    class->local = local;
+    class->nb_extra_bytes = extra_bytes;
+    memset( class->extra_bytes, 0, extra_bytes );
+    /* other fields are initialized by caller */
+
+    /* local classes have priority so we put them first in the list */
+    if (local) list_add_head( &process->classes, &class->entry );
+    else list_add_tail( &process->classes, &class->entry );
+    return class;
+}
+
+static void destroy_class( struct window_class *class )
+{
+    list_remove( &class->entry );
+    release_object( class->process );
+    free( class );
+}
+
+void destroy_process_classes( struct process *process )
+{
+    struct list *ptr;
+
+    while ((ptr = list_head( &process->classes )))
+    {
+        struct window_class *class = LIST_ENTRY( ptr, struct window_class, entry );
+        destroy_class( class );
+    }
+}
+
+static struct window_class *find_class( struct process *process, atom_t atom, void *instance )
+{
+    struct list *ptr;
+
+    LIST_FOR_EACH( ptr, &process->classes )
+    {
+        struct window_class *class = LIST_ENTRY( ptr, struct window_class, entry );
+        if (class->atom != atom) continue;
+        if (!instance || !class->local || class->instance == instance) return class;
+    }
+    return NULL;
+}
+
+struct window_class *grab_class( struct process *process, atom_t atom, void *instance )
+{
+    struct window_class *class = find_class( process, atom, instance );
+    if (class) class->count++;
+    else set_error( STATUS_INVALID_HANDLE );
+    return class;
+}
+
+void release_class( struct window_class *class )
+{
+    assert( class->count > 0 );
+    class->count--;
+}
+
+atom_t get_class_atom( struct window_class *class )
+{
+    return class->atom;
+}
+
+/* create a window class */
+DECL_HANDLER(create_class)
+{
+    struct window_class *class = find_class( current->process, req->atom, req->instance );
+
+    if (class && !class->local == !req->local)
+    {
+        set_win32_error( ERROR_CLASS_ALREADY_EXISTS );
+        return;
+    }
+    if (req->extra < 0 || req->extra > 4096)  /* don't allow stupid values here */
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+    if (!grab_global_atom( req->atom )) return;
+
+    if (!(class = create_class( current->process, req->extra, req->local )))
+    {
+        release_global_atom( req->atom );
+        return;
+    }
+    class->atom      = req->atom;
+    class->instance  = req->instance;
+    class->style     = req->style;
+    class->win_extra = req->win_extra;
+}
+
+/* destroy a window class */
+DECL_HANDLER(destroy_class)
+{
+    struct window_class *class = find_class( current->process, req->atom, req->instance );
+
+    if (!class)
+        set_error( STATUS_INVALID_HANDLE );
+    else if (class->count)
+        set_win32_error( ERROR_CLASS_HAS_WINDOWS );
+    else
+        destroy_class( class );
+}
+
+
+/* set some information in a class */
+DECL_HANDLER(set_class_info)
+{
+    struct window_class *class = get_window_class( req->window );
+
+    if (!class) return;
+
+    if (req->extra_size > sizeof(req->extra_value) ||
+        req->extra_offset < -1 ||
+        req->extra_offset > class->nb_extra_bytes - (int)req->extra_size)
+    {
+        set_win32_error( ERROR_INVALID_INDEX );
+        return;
+    }
+    if (req->extra_offset != -1)
+    {
+        memcpy( &reply->old_extra_value, class->extra_bytes + req->extra_offset, req->extra_size );
+    }
+    else if (req->flags & SET_CLASS_EXTRA)
+    {
+        set_win32_error( ERROR_INVALID_INDEX );
+        return;
+    }
+
+    reply->old_atom      = class->atom;
+    reply->old_style     = class->style;
+    reply->old_extra     = class->nb_extra_bytes;
+    reply->old_win_extra = class->win_extra;
+    reply->old_instance  = class->instance;
+
+    if (req->flags & SET_CLASS_ATOM)
+    {
+        if (!grab_global_atom( req->atom )) return;
+        release_global_atom( class->atom );
+        class->atom = req->atom;
+    }
+    if (req->flags & SET_CLASS_STYLE) class->style = req->style;
+    if (req->flags & SET_CLASS_WINEXTRA) class->win_extra = req->win_extra;
+    if (req->flags & SET_CLASS_INSTANCE) class->instance = req->instance;
+    if (req->flags & SET_CLASS_EXTRA) memcpy( class->extra_bytes + req->extra_offset,
+                                              &req->extra_value, req->extra_size );
+}
+
diff --git a/server/process.c b/server/process.c
index fd96614..0927559 100644
--- a/server/process.c
+++ b/server/process.c
@@ -44,6 +44,7 @@
 #include "thread.h"
 #include "request.h"
 #include "console.h"
+#include "user.h"
 
 /* process structure */
 
@@ -283,6 +284,7 @@
     process->group_id        = 0;
     process->token           = create_token();
     list_init( &process->locks );
+    list_init( &process->classes );
 
     gettimeofday( &process->start_time, NULL );
     if ((process->next = first_process) != NULL) process->next->prev = process;
@@ -597,6 +599,7 @@
         if (dll->filename) free( dll->filename );
         free( dll );
     }
+    destroy_process_classes( process );
     set_process_startup_state( process, STARTUP_ABORTED );
     if (process->exe.file) release_object( process->exe.file );
     process->exe.file = NULL;
diff --git a/server/process.h b/server/process.h
index cd759f2..cbf41f9 100644
--- a/server/process.h
+++ b/server/process.h
@@ -68,6 +68,7 @@
     int                  suspend;         /* global process suspend count */
     int                  create_flags;    /* process creation flags */
     struct list          locks;           /* list of file locks owned by the process */
+    struct list          classes;         /* window classes owned by the process */
     struct console_input*console;         /* console input */
     enum startup_state   startup_state;   /* startup state */
     struct startup_info *startup_info;    /* startup info while init is in progress */
diff --git a/server/protocol.def b/server/protocol.def
index c9d4df9..16c6c50 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1780,6 +1780,7 @@
     user_handle_t  parent;      /* parent window */
     user_handle_t  owner;       /* owner window */
     atom_t         atom;        /* class atom */
+    void*          instance;    /* module instance */
     int            extra;       /* number of extra bytes */
 @REPLY
     user_handle_t  handle;      /* created window */
@@ -2129,6 +2130,50 @@
 @END
 
 
+/* Create a window class */
+@REQ(create_class)
+    int            local;          /* is it a local class? */
+    atom_t         atom;           /* class atom */
+    unsigned int   style;          /* class style */
+    void*          instance;       /* module instance */
+    int            extra;          /* number of extra class bytes */
+    int            win_extra;      /* number of window extra bytes */
+@END
+
+
+/* Destroy a window class */
+@REQ(destroy_class)
+    atom_t         atom;           /* class atom */
+    void*          instance;       /* module instance */
+@END
+
+
+/* Set some information in a class */
+@REQ(set_class_info)
+    user_handle_t  window;         /* handle to the window */
+    unsigned int   flags;          /* flags for info to set (see below) */
+    atom_t         atom;           /* class atom */
+    unsigned int   style;          /* class style */
+    int            win_extra;      /* number of window extra bytes */
+    void*          instance;       /* module instance */
+    int            extra_offset;   /* offset to set in extra bytes */
+    size_t         extra_size;     /* size to set in extra bytes */
+    unsigned int   extra_value;    /* value to set in extra bytes */
+@REPLY
+    atom_t         old_atom;       /* previous class atom */
+    unsigned int   old_style;      /* previous class style */
+    int            old_extra;      /* previous number of class extra bytes */
+    int            old_win_extra;  /* previous number of window extra bytes */
+    void*          old_instance;   /* previous module instance */
+    unsigned int   old_extra_value; /* old value in extra bytes */
+@END
+#define SET_CLASS_ATOM      0x0001
+#define SET_CLASS_STYLE     0x0002
+#define SET_CLASS_WINEXTRA  0x0004
+#define SET_CLASS_INSTANCE  0x0008
+#define SET_CLASS_EXTRA     0x0010
+
+
 /* Set/get clipboard information */
 @REQ(set_clipboard_info)
     unsigned int   flags;       /* flags for fields to set (see below) */
diff --git a/server/request.h b/server/request.h
index a0287ad..ec2d5a1 100644
--- a/server/request.h
+++ b/server/request.h
@@ -279,6 +279,9 @@
 DECL_HANDLER(start_hook_chain);
 DECL_HANDLER(finish_hook_chain);
 DECL_HANDLER(get_next_hook);
+DECL_HANDLER(create_class);
+DECL_HANDLER(destroy_class);
+DECL_HANDLER(set_class_info);
 DECL_HANDLER(set_clipboard_info);
 DECL_HANDLER(open_token);
 DECL_HANDLER(set_global_windows);
@@ -464,6 +467,9 @@
     (req_handler)req_start_hook_chain,
     (req_handler)req_finish_hook_chain,
     (req_handler)req_get_next_hook,
+    (req_handler)req_create_class,
+    (req_handler)req_destroy_class,
+    (req_handler)req_set_class_info,
     (req_handler)req_set_clipboard_info,
     (req_handler)req_open_token,
     (req_handler)req_set_global_windows,
diff --git a/server/trace.c b/server/trace.c
index 3405a00..97ec3f5 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2096,6 +2096,7 @@
     fprintf( stderr, " parent=%p,", req->parent );
     fprintf( stderr, " owner=%p,", req->owner );
     fprintf( stderr, " atom=%04x,", req->atom );
+    fprintf( stderr, " instance=%p,", req->instance );
     fprintf( stderr, " extra=%d", req->extra );
 }
 
@@ -2503,6 +2504,45 @@
     dump_varargs_unicode_str( cur_size );
 }
 
+static void dump_create_class_request( const struct create_class_request *req )
+{
+    fprintf( stderr, " local=%d,", req->local );
+    fprintf( stderr, " atom=%04x,", req->atom );
+    fprintf( stderr, " style=%08x,", req->style );
+    fprintf( stderr, " instance=%p,", req->instance );
+    fprintf( stderr, " extra=%d,", req->extra );
+    fprintf( stderr, " win_extra=%d", req->win_extra );
+}
+
+static void dump_destroy_class_request( const struct destroy_class_request *req )
+{
+    fprintf( stderr, " atom=%04x,", req->atom );
+    fprintf( stderr, " instance=%p", req->instance );
+}
+
+static void dump_set_class_info_request( const struct set_class_info_request *req )
+{
+    fprintf( stderr, " window=%p,", req->window );
+    fprintf( stderr, " flags=%08x,", req->flags );
+    fprintf( stderr, " atom=%04x,", req->atom );
+    fprintf( stderr, " style=%08x,", req->style );
+    fprintf( stderr, " win_extra=%d,", req->win_extra );
+    fprintf( stderr, " instance=%p,", req->instance );
+    fprintf( stderr, " extra_offset=%d,", req->extra_offset );
+    fprintf( stderr, " extra_size=%d,", req->extra_size );
+    fprintf( stderr, " extra_value=%08x", req->extra_value );
+}
+
+static void dump_set_class_info_reply( const struct set_class_info_reply *req )
+{
+    fprintf( stderr, " old_atom=%04x,", req->old_atom );
+    fprintf( stderr, " old_style=%08x,", req->old_style );
+    fprintf( stderr, " old_extra=%d,", req->old_extra );
+    fprintf( stderr, " old_win_extra=%d,", req->old_win_extra );
+    fprintf( stderr, " old_instance=%p,", req->old_instance );
+    fprintf( stderr, " old_extra_value=%08x", req->old_extra_value );
+}
+
 static void dump_set_clipboard_info_request( const struct set_clipboard_info_request *req )
 {
     fprintf( stderr, " flags=%08x,", req->flags );
@@ -2726,6 +2766,9 @@
     (dump_func)dump_start_hook_chain_request,
     (dump_func)dump_finish_hook_chain_request,
     (dump_func)dump_get_next_hook_request,
+    (dump_func)dump_create_class_request,
+    (dump_func)dump_destroy_class_request,
+    (dump_func)dump_set_class_info_request,
     (dump_func)dump_set_clipboard_info_request,
     (dump_func)dump_open_token_request,
     (dump_func)dump_set_global_windows_request,
@@ -2908,6 +2951,9 @@
     (dump_func)dump_start_hook_chain_reply,
     (dump_func)0,
     (dump_func)dump_get_next_hook_reply,
+    (dump_func)0,
+    (dump_func)0,
+    (dump_func)dump_set_class_info_reply,
     (dump_func)dump_set_clipboard_info_reply,
     (dump_func)dump_open_token_reply,
     (dump_func)dump_set_global_windows_reply,
@@ -3090,6 +3136,9 @@
     "start_hook_chain",
     "finish_hook_chain",
     "get_next_hook",
+    "create_class",
+    "destroy_class",
+    "set_class_info",
     "set_clipboard_info",
     "open_token",
     "set_global_windows",
diff --git a/server/user.h b/server/user.h
index a075dd7..8c07b7f 100644
--- a/server/user.h
+++ b/server/user.h
@@ -27,6 +27,7 @@
 struct window;
 struct msg_queue;
 struct hook_table;
+struct window_class;
 
 enum user_object
 {
@@ -72,5 +73,13 @@
 extern struct thread *get_window_thread( user_handle_t handle );
 extern user_handle_t window_from_point( int x, int y );
 extern user_handle_t find_window_to_repaint( user_handle_t parent, struct thread *thread );
+extern struct window_class *get_window_class( user_handle_t window );
+
+/* window class functions */
+
+extern void destroy_process_classes( struct process *process );
+extern struct window_class *grab_class( struct process *process, atom_t atom, void *instance );
+extern void release_class( struct window_class *class );
+extern atom_t get_class_atom( struct window_class *class );
 
 #endif  /* __WINE_SERVER_USER_H */
diff --git a/server/window.c b/server/window.c
index 6373068..d6a1b6f 100644
--- a/server/window.c
+++ b/server/window.c
@@ -63,6 +63,7 @@
     struct window   *prev;            /* prev window in Z-order */
     user_handle_t    handle;          /* full handle for this window */
     struct thread   *thread;          /* thread owning the window */
+    struct window_class *class;       /* window class */
     atom_t           atom;            /* class atom */
     user_handle_t    last_active;     /* last active popup */
     rectangle_t      window_rect;     /* window rectangle */
@@ -260,6 +261,7 @@
     free_user_handle( win->handle );
     destroy_properties( win );
     unlink_window( win );
+    release_class( win->class );
     if (win->text) free( win->text );
     memset( win, 0x55, sizeof(*win) );
     free( win );
@@ -267,13 +269,23 @@
 
 /* create a new window structure (note: the window is not linked in the window tree) */
 static struct window *create_window( struct window *parent, struct window *owner, atom_t atom,
-                                     int extra_bytes )
+                                     void *instance, int extra_bytes )
 {
-    struct window *win = mem_alloc( sizeof(*win) + extra_bytes - 1 );
-    if (!win) return NULL;
+    struct window *win;
+    struct window_class *class = grab_class( current->process, atom, instance );
+
+    if (!class) return NULL;
+
+    win = mem_alloc( sizeof(*win) + extra_bytes - 1 );
+    if (!win)
+    {
+        release_class( class );
+        return NULL;
+    }
 
     if (!(win->handle = alloc_user_handle( win, USER_WINDOW )))
     {
+        release_class( class );
         free( win );
         return NULL;
     }
@@ -283,6 +295,7 @@
     win->last_child     = NULL;
     win->first_unlinked = NULL;
     win->thread         = current;
+    win->class          = class;
     win->atom           = atom;
     win->last_active    = win->handle;
     win->style          = 0;
@@ -449,6 +462,15 @@
 }
 
 
+/* get the window class of a window */
+struct window_class* get_window_class( user_handle_t window )
+{
+    struct window *win;
+    if (!(win = get_window( window ))) return NULL;
+    return win->class;
+}
+
+
 /* create a window */
 DECL_HANDLER(create_window)
 {
@@ -462,7 +484,7 @@
     {
         if (!top_window)
         {
-            if (!(top_window = create_window( NULL, NULL, req->atom, req->extra ))) return;
+            if (!(top_window = create_window( NULL, NULL, req->atom, req->instance, req->extra ))) return;
             top_window->thread = NULL;  /* no thread owns the desktop */
             top_window->style  = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
         }
@@ -481,7 +503,7 @@
             set_error( STATUS_ACCESS_DENIED );
             return;
         }
-        if (!(win = create_window( parent, owner, req->atom, req->extra ))) return;
+        if (!(win = create_window( parent, owner, req->atom, req->instance, req->extra ))) return;
         reply->handle = win->handle;
     }
 }
@@ -569,7 +591,7 @@
         {
             reply->tid  = get_thread_id( win->thread );
             reply->pid  = get_process_id( win->thread->process );
-            reply->atom = win->atom;
+            reply->atom = get_class_atom( win->class );
         }
     }
 }
@@ -648,7 +670,7 @@
     if (parent)
         for (ptr = parent->first_child, total = 0; ptr; ptr = ptr->next)
         {
-            if (req->atom && ptr->atom != req->atom) continue;
+            if (req->atom && get_class_atom(ptr->class) != req->atom) continue;
             if (req->tid && get_thread_id(ptr->thread) != req->tid) continue;
             total++;
         }
@@ -659,7 +681,7 @@
     {
         for (ptr = parent->first_child; ptr && len; ptr = ptr->next)
         {
-            if (req->atom && ptr->atom != req->atom) continue;
+            if (req->atom && get_class_atom(ptr->class) != req->atom) continue;
             if (req->tid && get_thread_id(ptr->thread) != req->tid) continue;
             *data++ = ptr->handle;
             len -= sizeof(*data);