Implement NtQueryTimer.

diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 7d7d212..aab4c3e 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -187,7 +187,7 @@
 @ stub NtQuerySystemEnvironmentValue
 @ stdcall NtQuerySystemInformation(long long long long)
 @ stdcall NtQuerySystemTime(ptr)
-@ stub NtQueryTimer
+@ stdcall NtQueryTimer(ptr long ptr long ptr)
 @ stdcall NtQueryTimerResolution(long long long)
 @ stdcall NtQueryValueKey(long long long long long long)
 @ stdcall NtQueryVirtualMemory(long ptr long ptr long ptr)
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index a2e4418..75739e5 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -42,6 +42,7 @@
 extern void dump_ObjectAttributes (const OBJECT_ATTRIBUTES *ObjectAttributes);
 
 extern void NTDLL_get_server_timeout( abs_time_t *when, const LARGE_INTEGER *timeout );
+extern void NTDLL_from_server_timeout( LARGE_INTEGER *timeout, const abs_time_t *when );
 extern NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags,
                                                  const LARGE_INTEGER *timeout );
 
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 324cdf9..9b1dc49 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -511,6 +511,74 @@
 }
 
 /******************************************************************************
+ *  NtQueryTimer (NTDLL.@)
+ *
+ * Retrieves information about a timer.
+ *
+ * PARAMS
+ *  TimerHandle           [I] The timer to retrieve information about.
+ *  TimerInformationClass [I] The type of information to retrieve.
+ *  TimerInformation      [O] Pointer to buffer to store information in.
+ *  Length                [I] The length of the buffer pointed to by TimerInformation.
+ *  ReturnLength          [O] Optional. The size of buffer actually used.
+ *
+ * RETURNS
+ *  Success: STATUS_SUCCESS
+ *  Failure: STATUS_INFO_LENGTH_MISMATCH, if Length doesn't match the required data
+ *           size for the class specified.
+ *           STATUS_INVALID_INFO_CLASS, if an invalid TimerInformationClass was specified.
+ *           STATUS_ACCESS_DENIED, if TimerHandle does not have TIMER_QUERY_STATE access
+ *           to the timer.
+ */
+NTSTATUS WINAPI NtQueryTimer(
+    HANDLE TimerHandle,
+    TIMER_INFORMATION_CLASS TimerInformationClass,
+    PVOID TimerInformation,
+    ULONG Length,
+    PULONG ReturnLength)
+{
+    TIMER_BASIC_INFORMATION * basic_info = (TIMER_BASIC_INFORMATION *)TimerInformation;
+    NTSTATUS status;
+    LARGE_INTEGER now;
+
+    TRACE("(%p,%d,%p,0x%08lx,%p)\n", TimerHandle, TimerInformationClass,
+       TimerInformation, Length, ReturnLength);
+
+    switch (TimerInformationClass)
+    {
+    case TimerBasicInformation:
+        if (Length < sizeof(TIMER_BASIC_INFORMATION))
+            return STATUS_INFO_LENGTH_MISMATCH;
+
+        SERVER_START_REQ(get_timer_info)
+        {
+            req->handle = TimerHandle;
+            status = wine_server_call(req);
+
+            /* convert server time to absolute NTDLL time */
+            NTDLL_from_server_timeout(&basic_info->RemainingTime, &reply->when);
+            basic_info->TimerState = reply->signaled;
+        }
+        SERVER_END_REQ;
+
+        /* convert from absolute into relative time */
+        NtQuerySystemTime(&now);
+        if (now.QuadPart > basic_info->RemainingTime.QuadPart)
+            basic_info->RemainingTime.QuadPart = 0;
+        else
+            basic_info->RemainingTime.QuadPart -= now.QuadPart;
+
+        if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
+
+        return status;
+    }
+
+    FIXME("Unhandled class %d\n", TimerInformationClass);
+    return STATUS_INVALID_INFO_CLASS;
+}
+
+
+/******************************************************************************
  * NtQueryTimerResolution [NTDLL.@]
  */
 NTSTATUS WINAPI NtQueryTimerResolution(OUT ULONG* min_resolution,
diff --git a/dlls/ntdll/time.c b/dlls/ntdll/time.c
index 6b5fd09..9e520ab 100644
--- a/dlls/ntdll/time.c
+++ b/dlls/ntdll/time.c
@@ -398,6 +398,18 @@
 }
 
 
+/***********************************************************************
+ *              NTDLL_from_server_timeout
+ *
+ * Convert a timeval struct from the server into an NTDLL timeout.
+ */
+void NTDLL_from_server_timeout( LARGE_INTEGER *timeout, const abs_time_t *when )
+{
+    timeout->QuadPart = when->sec * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970;
+    timeout->QuadPart += when->usec * 10;
+}
+
+
 /******************************************************************************
  *       RtlTimeToTimeFields [NTDLL.@]
  *
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 17e7827..f775392 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1928,6 +1928,19 @@
 };
 
 
+struct get_timer_info_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+};
+struct get_timer_info_reply
+{
+    struct reply_header __header;
+    abs_time_t   when;
+    int          signaled;
+};
+
+
 
 struct get_thread_context_request
 {
@@ -3239,6 +3252,7 @@
     REQ_open_timer,
     REQ_set_timer,
     REQ_cancel_timer,
+    REQ_get_timer_info,
     REQ_get_thread_context,
     REQ_set_thread_context,
     REQ_get_selector_entry,
@@ -3424,6 +3438,7 @@
     struct open_timer_request open_timer_request;
     struct set_timer_request set_timer_request;
     struct cancel_timer_request cancel_timer_request;
+    struct get_timer_info_request get_timer_info_request;
     struct get_thread_context_request get_thread_context_request;
     struct set_thread_context_request set_thread_context_request;
     struct get_selector_entry_request get_selector_entry_request;
@@ -3607,6 +3622,7 @@
     struct open_timer_reply open_timer_reply;
     struct set_timer_reply set_timer_reply;
     struct cancel_timer_reply cancel_timer_reply;
+    struct get_timer_info_reply get_timer_info_reply;
     struct get_thread_context_reply get_thread_context_reply;
     struct set_thread_context_reply set_thread_context_reply;
     struct get_selector_entry_reply get_selector_entry_reply;
@@ -3681,6 +3697,6 @@
     struct set_global_windows_reply set_global_windows_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 152
+#define SERVER_PROTOCOL_VERSION 153
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winternl.h b/include/winternl.h
index 8a58bed..5946ec7 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -632,6 +632,16 @@
     BOOLEAN     AbandonedState;
 } MUTANT_BASIC_INFORMATION, *PMUTANT_BASIC_INFORMATION;
 
+typedef enum _TIMER_INFORMATION_CLASS
+{
+    TimerBasicInformation = 0
+} TIMER_INFORMATION_CLASS;
+
+typedef struct _TIMER_BASIC_INFORMATION
+{
+    LARGE_INTEGER RemainingTime;
+    BOOLEAN       TimerState;
+} TIMER_BASIC_INFORMATION, *PTIMER_BASIC_INFORMATION;
 
 
 /* return type of RtlDetermineDosPathNameType_U (FIXME: not the correct names) */
@@ -1401,6 +1411,7 @@
 NTSTATUS  WINAPI NtQuerySecurityObject(HANDLE,SECURITY_INFORMATION,PSECURITY_DESCRIPTOR,ULONG,PULONG);
 NTSTATUS  WINAPI NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS,PVOID,ULONG,PULONG);
 NTSTATUS  WINAPI NtQuerySystemTime(PLARGE_INTEGER);
+NTSTATUS  WINAPI NtQueryTimer(HANDLE,TIMER_INFORMATION_CLASS,PVOID,ULONG,PULONG);
 NTSTATUS  WINAPI NtQueryValueKey(HKEY,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,void *,DWORD,DWORD *);
 NTSTATUS  WINAPI NtQueryVirtualMemory(HANDLE,LPCVOID,MEMORY_INFORMATION_CLASS,PVOID,ULONG,ULONG*);
 NTSTATUS  WINAPI NtQueryVolumeInformationFile(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,FS_INFORMATION_CLASS);
diff --git a/server/protocol.def b/server/protocol.def
index 5423b7c..0cfd8c5 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1378,6 +1378,14 @@
      int         signaled;      /* was the timer signaled before this calltime ? */
 @END
 
+/* Get information on a waitable timer */
+@REQ(get_timer_info)
+    obj_handle_t handle;        /* handle to the timer */
+@REPLY
+    abs_time_t   when;          /* absolute time when the timer next expires */
+    int          signaled;      /* is the timer signaled? */
+@END
+
 
 /* Retrieve the current context of a thread */
 @REQ(get_thread_context)
diff --git a/server/request.h b/server/request.h
index d4ceb24..0a2319f 100644
--- a/server/request.h
+++ b/server/request.h
@@ -209,6 +209,7 @@
 DECL_HANDLER(open_timer);
 DECL_HANDLER(set_timer);
 DECL_HANDLER(cancel_timer);
+DECL_HANDLER(get_timer_info);
 DECL_HANDLER(get_thread_context);
 DECL_HANDLER(set_thread_context);
 DECL_HANDLER(get_selector_entry);
@@ -393,6 +394,7 @@
     (req_handler)req_open_timer,
     (req_handler)req_set_timer,
     (req_handler)req_cancel_timer,
+    (req_handler)req_get_timer_info,
     (req_handler)req_get_thread_context,
     (req_handler)req_set_thread_context,
     (req_handler)req_get_selector_entry,
diff --git a/server/timer.c b/server/timer.c
index ef07f2a..1903ba7 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -241,3 +241,18 @@
         release_object( timer );
     }
 }
+
+/* Get information on a waitable timer */
+DECL_HANDLER(get_timer_info)
+{
+    struct timer *timer;
+
+    if ((timer = (struct timer *)get_handle_obj( current->process, req->handle,
+                                                 TIMER_QUERY_STATE, &timer_ops )))
+    {
+        reply->when.sec  = timer->when.tv_sec;
+        reply->when.usec = timer->when.tv_usec;
+        reply->signaled  = timer->signaled;
+        release_object( timer );
+    }
+}
diff --git a/server/trace.c b/server/trace.c
index daafbf5..bcba1d1 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1692,6 +1692,19 @@
     fprintf( stderr, " signaled=%d", req->signaled );
 }
 
+static void dump_get_timer_info_request( const struct get_timer_info_request *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_get_timer_info_reply( const struct get_timer_info_reply *req )
+{
+    fprintf( stderr, " when=" );
+    dump_abs_time( &req->when );
+    fprintf( stderr, "," );
+    fprintf( stderr, " signaled=%d", req->signaled );
+}
+
 static void dump_get_thread_context_request( const struct get_thread_context_request *req )
 {
     fprintf( stderr, " handle=%p,", req->handle );
@@ -2695,6 +2708,7 @@
     (dump_func)dump_open_timer_request,
     (dump_func)dump_set_timer_request,
     (dump_func)dump_cancel_timer_request,
+    (dump_func)dump_get_timer_info_request,
     (dump_func)dump_get_thread_context_request,
     (dump_func)dump_set_thread_context_request,
     (dump_func)dump_get_selector_entry_request,
@@ -2876,6 +2890,7 @@
     (dump_func)dump_open_timer_reply,
     (dump_func)dump_set_timer_reply,
     (dump_func)dump_cancel_timer_reply,
+    (dump_func)dump_get_timer_info_reply,
     (dump_func)dump_get_thread_context_reply,
     (dump_func)0,
     (dump_func)dump_get_selector_entry_reply,
@@ -3057,6 +3072,7 @@
     "open_timer",
     "set_timer",
     "cancel_timer",
+    "get_timer_info",
     "get_thread_context",
     "set_thread_context",
     "get_selector_entry",