server: Add support for returning the object name in NtQueryObject.
diff --git a/dlls/ntdll/om.c b/dlls/ntdll/om.c
index c2567be..e9677d0 100644
--- a/dlls/ntdll/om.c
+++ b/dlls/ntdll/om.c
@@ -85,6 +85,42 @@
             SERVER_END_REQ;
         }
         break;
+    case ObjectNameInformation:
+        {
+            OBJECT_NAME_INFORMATION* p = ptr;
+
+            SERVER_START_REQ( get_object_info )
+            {
+                req->handle = wine_server_obj_handle( handle );
+                if (len > sizeof(*p)) wine_server_set_reply( req, p + 1, len - sizeof(*p) );
+                status = wine_server_call( req );
+                if (status == STATUS_SUCCESS)
+                {
+                    if (!reply->total)  /* no name */
+                    {
+                        if (sizeof(*p) > len) status = STATUS_INFO_LENGTH_MISMATCH;
+                        else memset( p, 0, sizeof(*p) );
+                        if (used_len) *used_len = sizeof(*p);
+                    }
+                    else if (sizeof(*p) + reply->total + sizeof(WCHAR) > len)
+                    {
+                        if (used_len) *used_len = sizeof(*p) + reply->total + sizeof(WCHAR);
+                        status = STATUS_INFO_LENGTH_MISMATCH;
+                    }
+                    else
+                    {
+                        ULONG res = wine_server_reply_size( reply );
+                        p->Name.Buffer = (WCHAR *)(p + 1);
+                        p->Name.Length = res;
+                        p->Name.MaximumLength = res + sizeof(WCHAR);
+                        p->Name.Buffer[res / sizeof(WCHAR)] = 0;
+                        if (used_len) *used_len = sizeof(*p) + p->Name.MaximumLength;
+                    }
+                }
+            }
+            SERVER_END_REQ;
+        }
+        break;
     case ObjectDataInformation:
         {
             OBJECT_DATA_INFORMATION* p = ptr;
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c
index d5cf158..cf9a1a5 100644
--- a/dlls/ntdll/tests/om.c
+++ b/dlls/ntdll/tests/om.c
@@ -44,6 +44,7 @@
 static NTSTATUS (WINAPI *pNtCreateDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
 static NTSTATUS (WINAPI *pNtOpenSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
 static NTSTATUS (WINAPI *pNtCreateSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PUNICODE_STRING);
+static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE,OBJECT_INFORMATION_CLASS,PVOID,ULONG,PULONG);
 
 
 static void test_case_sensitive (void)
@@ -617,6 +618,57 @@
     pNtClose(dir);
 }
 
+static void test_query_object(void)
+{
+    static const WCHAR name[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s',
+                                 '\\','t','e','s','t','_','e','v','e','n','t'};
+    HANDLE handle;
+    char buffer[1024];
+    NTSTATUS status;
+    ULONG len;
+    UNICODE_STRING *str;
+
+    handle = CreateEventA( NULL, FALSE, FALSE, "test_event" );
+
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, 0, &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
+
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(UNICODE_STRING), &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
+
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+    ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
+    ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+    str = (UNICODE_STRING *)buffer;
+    ok( sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR) == len, "unexpected len %u\n", len );
+    ok( str->Length >= sizeof(name), "unexpected len %u\n", str->Length );
+    /* there can be a \\Sessions prefix in the name */
+    ok( !memcmp( str->Buffer + (str->Length - sizeof(name)) / sizeof(WCHAR), name, sizeof(name) ),
+        "wrong name %s\n", wine_dbgstr_w(str->Buffer) );
+
+    len -= sizeof(WCHAR);
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, len, &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
+
+    pNtClose( handle );
+
+    handle = CreateEventA( NULL, FALSE, FALSE, NULL );
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+    ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
+    ok( len == sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+    str = (UNICODE_STRING *)buffer;
+    ok( str->Length == 0, "unexpected len %u\n", len );
+    ok( str->Buffer == NULL, "unexpected ptr %p\n", str->Buffer );
+    pNtClose( handle );
+}
+
 START_TEST(om)
 {
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -646,10 +698,12 @@
     pNtCreateSemaphore      =  (void *)GetProcAddress(hntdll, "NtCreateSemaphore");
     pNtCreateTimer          =  (void *)GetProcAddress(hntdll, "NtCreateTimer");
     pNtCreateSection        =  (void *)GetProcAddress(hntdll, "NtCreateSection");
+    pNtQueryObject          =  (void *)GetProcAddress(hntdll, "NtQueryObject");
 
     test_case_sensitive();
     test_namespace_pipe();
     test_name_collisions();
     test_directory();
     test_symboliclink();
+    test_query_object();
 }
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 6a860df..47c032e 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -4349,6 +4349,9 @@
     struct reply_header __header;
     unsigned int   access;
     unsigned int   ref_count;
+    data_size_t    total;
+    /* VARARG(name,unicode_str); */
+    char __pad_20[4];
 };
 
 
@@ -5394,6 +5397,6 @@
     struct free_user_handle_reply free_user_handle_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 394
+#define SERVER_PROTOCOL_VERSION 395
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/handle.c b/server/handle.c
index 3baaa6d..4e52f0e 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -593,11 +593,14 @@
 DECL_HANDLER(get_object_info)
 {
     struct object *obj;
+    WCHAR *name;
 
     if (!(obj = get_handle_obj( current->process, req->handle, 0, NULL ))) return;
 
     reply->access = get_handle_access( current->process, req->handle );
     reply->ref_count = obj->refcount;
+    if ((name = get_object_full_name( obj, &reply->total )))
+        set_reply_data_ptr( name, min( reply->total, get_reply_max_size() ));
     release_object( obj );
 }
 
diff --git a/server/object.c b/server/object.c
index f08548c..d2a3930 100644
--- a/server/object.c
+++ b/server/object.c
@@ -171,6 +171,35 @@
     return ptr->name;
 }
 
+/* get the full path name of an existing object */
+WCHAR *get_object_full_name( struct object *obj, data_size_t *ret_len )
+{
+    static const WCHAR backslash = '\\';
+    struct object *ptr = obj;
+    data_size_t len = 0;
+    char *ret;
+
+    while (ptr && ptr->name)
+    {
+        struct object_name *name = ptr->name;
+        len += name->len + sizeof(WCHAR);
+        ptr = name->parent;
+    }
+    if (!len) return NULL;
+    if (!(ret = malloc( len ))) return NULL;
+
+    *ret_len = len;
+    while (obj && obj->name)
+    {
+        struct object_name *name = obj->name;
+        memcpy( ret + len - name->len, name->name, name->len );
+        len -= name->len + sizeof(WCHAR);
+        memcpy( ret + len, &backslash, sizeof(WCHAR) );
+        obj = name->parent;
+    }
+    return (WCHAR *)ret;
+}
+
 /* allocate and initialize an object */
 void *alloc_object( const struct object_ops *ops )
 {
diff --git a/server/object.h b/server/object.h
index a5d0ffd..01dc00e 100644
--- a/server/object.h
+++ b/server/object.h
@@ -115,6 +115,7 @@
 extern void *memdup( const void *data, size_t len );
 extern void *alloc_object( const struct object_ops *ops );
 extern const WCHAR *get_object_name( struct object *obj, data_size_t *len );
+extern WCHAR *get_object_full_name( struct object *obj, data_size_t *ret_len );
 extern void dump_object_name( struct object *obj );
 extern void *create_object( struct namespace *namespace, const struct object_ops *ops,
                             const struct unicode_str *name, struct object *parent );
diff --git a/server/protocol.def b/server/protocol.def
index bba6788..04f6e2b 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3061,6 +3061,8 @@
 @REPLY
     unsigned int   access;        /* granted access mask */
     unsigned int   ref_count;     /* object ref count */
+    data_size_t    total;         /* total needed size for name */
+    VARARG(name,unicode_str);     /* object name */
 @END
 
 
diff --git a/server/request.h b/server/request.h
index ca5fc88..41c6409 100644
--- a/server/request.h
+++ b/server/request.h
@@ -1817,7 +1817,8 @@
 C_ASSERT( FIELD_OFFSET(struct get_object_info_request, handle) == 12 );
 C_ASSERT( FIELD_OFFSET(struct get_object_info_reply, access) == 8 );
 C_ASSERT( FIELD_OFFSET(struct get_object_info_reply, ref_count) == 12 );
-C_ASSERT( sizeof(struct get_object_info_reply) == 16 );
+C_ASSERT( FIELD_OFFSET(struct get_object_info_reply, total) == 16 );
+C_ASSERT( sizeof(struct get_object_info_reply) == 24 );
 C_ASSERT( FIELD_OFFSET(struct unlink_object_request, handle) == 12 );
 C_ASSERT( sizeof(struct unlink_object_request) == 16 );
 C_ASSERT( FIELD_OFFSET(struct get_token_impersonation_level_request, handle) == 12 );
diff --git a/server/trace.c b/server/trace.c
index df47e8f..03e4c04 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3600,6 +3600,8 @@
 {
     fprintf( stderr, " access=%08x", req->access );
     fprintf( stderr, ", ref_count=%08x", req->ref_count );
+    fprintf( stderr, ", total=%u", req->total );
+    dump_varargs_unicode_str( ", name=", cur_size );
 }
 
 static void dump_unlink_object_request( const struct unlink_object_request *req )