server: Return multiple events in read_changes.
diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index 711a0b0..cc4e287 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -2902,49 +2902,72 @@
 static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, void **apc )
 {
     struct read_changes_info *info = user;
-    char path[PATH_MAX];
-    NTSTATUS ret = STATUS_SUCCESS;
-    int len, action, i;
+    char data[PATH_MAX];
+    NTSTATUS ret;
+    int size;
 
     SERVER_START_REQ( read_change )
     {
         req->handle = wine_server_obj_handle( info->FileHandle );
-        wine_server_set_reply( req, path, PATH_MAX );
+        wine_server_set_reply( req, data, PATH_MAX );
         ret = wine_server_call( req );
-        action = reply->action;
-        len = wine_server_reply_size( reply );
+        size = wine_server_reply_size( reply );
     }
     SERVER_END_REQ;
 
-    if (ret == STATUS_SUCCESS && info->Buffer && 
-        (info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
+    if (ret == STATUS_SUCCESS && info->Buffer)
     {
-        PFILE_NOTIFY_INFORMATION pfni;
+        PFILE_NOTIFY_INFORMATION pfni = info->Buffer;
+        int i, left = info->BufferSize;
+        DWORD *last_entry_offset = NULL;
+        struct filesystem_event *event = (struct filesystem_event*)data;
 
-        pfni = info->Buffer;
+        while (size && left >= sizeof(*pfni))
+        {
+            /* convert to an NT style path */
+            for (i=0; i<event->len; i++)
+                if (event->name[i] == '/')
+                    event->name[i] = '\\';
 
-        /* convert to an NT style path */
-        for (i=0; i<len; i++)
-            if (path[i] == '/')
-                path[i] = '\\';
+            pfni->Action = event->action;
+            pfni->FileNameLength = ntdll_umbstowcs( 0, event->name, event->len, pfni->FileName,
+                    (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR));
+            last_entry_offset = &pfni->NextEntryOffset;
 
-        len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
-                               info->BufferSize - sizeof (*pfni) );
+            if(pfni->FileNameLength == -1 || pfni->FileNameLength == -2)
+                break;
 
-        pfni->NextEntryOffset = 0;
-        pfni->Action = action;
-        pfni->FileNameLength = len * sizeof (WCHAR);
-        pfni->FileName[len] = 0;
-        len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
+            i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
+            pfni->FileNameLength *= sizeof(WCHAR);
+            pfni->NextEntryOffset = i;
+            pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
+            left -= i;
+
+            i = (offsetof(struct filesystem_event, name[event->len])
+                    + sizeof(int)-1) / sizeof(int) * sizeof(int);
+            event = (struct filesystem_event*)((char*)event + i);
+            size -= i;
+        }
+
+        if (size)
+        {
+            ret = STATUS_NOTIFY_ENUM_DIR;
+            size = 0;
+        }
+        else
+        {
+            *last_entry_offset = 0;
+            size = info->BufferSize - left;
+        }
     }
     else
     {
         ret = STATUS_NOTIFY_ENUM_DIR;
-        len = 0;
+        size = 0;
     }
 
     iosb->u.Status = ret;
-    iosb->Information = len;
+    iosb->Information = size;
     *apc = read_changes_user_apc;
     return ret;
 }
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 72f3cde..2e940b7 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -316,6 +316,14 @@
     unsigned short attr;
 } char_info_t;
 
+
+struct filesystem_event
+{
+    int         action;
+    data_size_t len;
+    char        name[1];
+};
+
 typedef struct
 {
     unsigned int low_part;
@@ -1879,9 +1887,7 @@
 struct read_change_reply
 {
     struct reply_header __header;
-    int          action;
-    /* VARARG(name,string); */
-    char __pad_12[4];
+    /* VARARG(events,filesystem_event); */
 };
 
 
@@ -5519,6 +5525,6 @@
     struct set_cursor_reply set_cursor_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 411
+#define SERVER_PROTOCOL_VERSION 412
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/change.c b/server/change.c
index 5eb1df6..e448c13 100644
--- a/server/change.c
+++ b/server/change.c
@@ -123,9 +123,7 @@
 
 struct change_record {
     struct list entry;
-    int action;
-    int len;
-    char name[1];
+    struct filesystem_event event;
 };
 
 struct dir
@@ -616,13 +614,13 @@
     if (dir->want_data)
     {
         size_t len = strlen(relpath);
-        record = malloc( offsetof(struct change_record, name[len]) );
+        record = malloc( offsetof(struct change_record, event.name[len]) );
         if (!record)
             return;
 
-        record->action = action;
-        memcpy( record->name, relpath, len );
-        record->len = len;
+        record->event.action = action;
+        memcpy( record->event.name, relpath, len );
+        record->event.len = len;
 
         list_add_tail( &dir->change_records, &record->entry );
     }
@@ -1145,21 +1143,54 @@
 
 DECL_HANDLER(read_change)
 {
-    struct change_record *record;
+    struct change_record *record, *next;
     struct dir *dir;
+    struct list events;
+    char *data, *event;
+    int size = 0;
 
     dir = get_dir_obj( current->process, req->handle, 0 );
     if (!dir)
         return;
 
-    if ((record = get_first_change_record( dir )) != NULL)
+    list_init( &events );
+    list_move_tail( &events, &dir->change_records );
+    release_object( dir );
+
+    if (list_empty( &events ))
     {
-        reply->action = record->action;
-        set_reply_data( record->name, record->len );
+        set_error( STATUS_NO_DATA_DETECTED );
+        return;
+    }
+
+    LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
+    {
+        size += (offsetof(struct filesystem_event, name[record->event.len])
+                + sizeof(int)-1) / sizeof(int) * sizeof(int);
+    }
+
+    if (size > get_reply_max_size())
+        set_error( STATUS_BUFFER_TOO_SMALL );
+    else if ((data = mem_alloc( size )) != NULL)
+    {
+        event = data;
+        LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
+        {
+            data_size_t len = offsetof( struct filesystem_event, name[record->event.len] );
+            memcpy( event, &record->event, len );
+            event += len;
+            if (len % sizeof(int))
+            {
+                memset( event, 0, sizeof(int) - len % sizeof(int) );
+                event += sizeof(int) - len % sizeof(int);
+            }
+        }
+        set_reply_data_ptr( data, size );
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE( record, next, &events, struct change_record, entry )
+    {
+        list_remove( &record->entry );
         free( record );
     }
-    else
-        set_error( STATUS_NO_DATA_DETECTED );
-
-    release_object( dir );
 }
diff --git a/server/protocol.def b/server/protocol.def
index 6245f4c..728898f 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -332,6 +332,14 @@
     unsigned short attr;
 } char_info_t;
 
+/* structure returned in filesystem events */
+struct filesystem_event
+{
+    int         action;
+    data_size_t len;
+    char        name[1];
+};
+
 typedef struct
 {
     unsigned int low_part;
@@ -1448,8 +1456,7 @@
 @REQ(read_change)
     obj_handle_t handle;
 @REPLY
-    int          action;        /* type of change */
-    VARARG(name,string);        /* name of directory entry that changed */
+    VARARG(events,filesystem_event);  /* collected filesystem events */
 @END
 
 
diff --git a/server/request.h b/server/request.h
index e0bffaf..ae45ab8 100644
--- a/server/request.h
+++ b/server/request.h
@@ -1103,8 +1103,7 @@
 C_ASSERT( sizeof(struct read_directory_changes_request) == 64 );
 C_ASSERT( FIELD_OFFSET(struct read_change_request, handle) == 12 );
 C_ASSERT( sizeof(struct read_change_request) == 16 );
-C_ASSERT( FIELD_OFFSET(struct read_change_reply, action) == 8 );
-C_ASSERT( sizeof(struct read_change_reply) == 16 );
+C_ASSERT( sizeof(struct read_change_reply) == 8 );
 C_ASSERT( FIELD_OFFSET(struct create_mapping_request, access) == 12 );
 C_ASSERT( FIELD_OFFSET(struct create_mapping_request, attributes) == 16 );
 C_ASSERT( FIELD_OFFSET(struct create_mapping_request, protect) == 20 );
diff --git a/server/trace.c b/server/trace.c
index 83d5b16..d083d2d 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -991,6 +991,24 @@
     fputc( '}', stderr );
 }
 
+static void dump_varargs_filesystem_event( const char *prefix, data_size_t size )
+{
+    fprintf( stderr,"%s{", prefix );
+    while (size)
+    {
+        const struct filesystem_event *event = cur_data;
+        data_size_t len = (offsetof( struct filesystem_event, name[event->len] ) + sizeof(int)-1)
+                           / sizeof(int) * sizeof(int);
+        if (size < len) break;
+        fprintf( stderr, "{action=%x,len=%u,name=\"%.*s\"}",
+                 event->action, event->len, event->len, event->name );
+        size -= len;
+        remove_data( len );
+        if (size)fputc( ',', stderr );
+    }
+    fputc( '}', stderr );
+}
+
 typedef void (*dump_func)( const void *req );
 
 /* Everything below this line is generated automatically by tools/make_requests */
@@ -1865,8 +1883,7 @@
 
 static void dump_read_change_reply( const struct read_change_reply *req )
 {
-    fprintf( stderr, " action=%d", req->action );
-    dump_varargs_string( ", name=", cur_size );
+    dump_varargs_filesystem_event( " events=", cur_size );
 }
 
 static void dump_create_mapping_request( const struct create_mapping_request *req )