Added server-side infrastructure for the thread input structure.
Reimplemented AttachThreadInput() and added GetGUIThreadInfo().

diff --git a/dlls/user/message.c b/dlls/user/message.c
index 55270a5..8621241 100644
--- a/dlls/user/message.c
+++ b/dlls/user/message.c
@@ -2231,3 +2231,58 @@
     /* now obsolete the message queue will be expanded dynamically as necessary */
     return TRUE;
 }
+
+
+/**********************************************************************
+ *		AttachThreadInput (USER32.@)
+ *
+ * Attaches the input processing mechanism of one thread to that of
+ * another thread.
+ */
+BOOL WINAPI AttachThreadInput( DWORD from, DWORD to, BOOL attach )
+{
+    BOOL ret;
+
+    SERVER_START_REQ( attach_thread_input )
+    {
+        req->tid_from = from;
+        req->tid_to   = to;
+        req->attach   = attach;
+        ret = !wine_server_call_err( req );
+    }
+    SERVER_END_REQ;
+    return ret;
+}
+
+
+/**********************************************************************
+ *		GetGUIThreadInfo  (USER32.@)
+ */
+BOOL WINAPI GetGUIThreadInfo( DWORD id, GUITHREADINFO *info )
+{
+    BOOL ret;
+
+    SERVER_START_REQ( get_thread_input )
+    {
+        req->tid = id;
+        if ((ret = !wine_server_call_err( req )))
+        {
+            info->flags          = 0;
+            info->hwndActive     = reply->active;
+            info->hwndFocus      = reply->focus;
+            info->hwndCapture    = reply->capture;
+            info->hwndMenuOwner  = reply->menu_owner;
+            info->hwndMoveSize   = reply->move_size;
+            info->hwndCaret      = reply->caret;
+            info->rcCaret.left   = reply->rect.left;
+            info->rcCaret.top    = reply->rect.top;
+            info->rcCaret.right  = reply->rect.right;
+            info->rcCaret.bottom = reply->rect.bottom;
+            if (reply->menu_owner) info->flags |= GUI_INMENUMODE;
+            if (reply->move_size) info->flags |= GUI_INMOVESIZE;
+            if (reply->caret) info->flags |= GUI_CARETBLINKING;
+        }
+    }
+    SERVER_END_REQ;
+    return ret;
+}
diff --git a/dlls/user/user32.spec b/dlls/user/user32.spec
index 19da4e6..e7cfc38 100644
--- a/dlls/user/user32.spec
+++ b/dlls/user/user32.spec
@@ -244,6 +244,7 @@
 @ stdcall GetDoubleClickTime() GetDoubleClickTime
 @ stdcall GetFocus() GetFocus
 @ stdcall GetForegroundWindow() GetForegroundWindow
+@ stdcall GetGUIThreadInfo(long ptr) GetGUIThreadInfo
 @ stdcall GetIconInfo(long ptr) GetIconInfo
 @ stub GetInputDesktop
 @ stdcall GetInputState() GetInputState
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 8b2a63f..01b6e19 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2729,6 +2729,40 @@
 };
 
 
+
+struct attach_thread_input_request
+{
+    struct request_header __header;
+    thread_id_t    tid_from;
+    thread_id_t    tid_to;
+    int            attach;
+};
+struct attach_thread_input_reply
+{
+    struct reply_header __header;
+};
+
+
+
+struct get_thread_input_request
+{
+    struct request_header __header;
+    thread_id_t    tid;
+};
+struct get_thread_input_reply
+{
+    struct reply_header __header;
+    user_handle_t  focus;
+    user_handle_t  capture;
+    user_handle_t  active;
+    user_handle_t  foreground;
+    user_handle_t  menu_owner;
+    user_handle_t  move_size;
+    user_handle_t  caret;
+    rectangle_t    rect;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -2887,6 +2921,8 @@
     REQ_remove_window_property,
     REQ_get_window_property,
     REQ_get_window_properties,
+    REQ_attach_thread_input,
+    REQ_get_thread_input,
     REQ_NB_REQUESTS
 };
 
@@ -3050,6 +3086,8 @@
     struct remove_window_property_request remove_window_property_request;
     struct get_window_property_request get_window_property_request;
     struct get_window_properties_request get_window_properties_request;
+    struct attach_thread_input_request attach_thread_input_request;
+    struct get_thread_input_request get_thread_input_request;
 };
 union generic_reply
 {
@@ -3211,8 +3249,10 @@
     struct remove_window_property_reply remove_window_property_reply;
     struct get_window_property_reply get_window_property_reply;
     struct get_window_properties_reply get_window_properties_reply;
+    struct attach_thread_input_reply attach_thread_input_reply;
+    struct get_thread_input_reply get_thread_input_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 84
+#define SERVER_PROTOCOL_VERSION 85
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winuser.h b/include/winuser.h
index cce01e4..f7c29ff 100644
--- a/include/winuser.h
+++ b/include/winuser.h
@@ -397,6 +397,27 @@
 } INPUT, *PINPUT, *LPINPUT;
 
 
+typedef struct tagGUITHREADINFO
+{
+    DWORD   cbSize;
+    DWORD   flags;
+    HWND    hwndActive;
+    HWND    hwndFocus;
+    HWND    hwndCapture;
+    HWND    hwndMenuOwner;
+    HWND    hwndMoveSize;
+    HWND    hwndCaret;
+    RECT    rcCaret;
+} GUITHREADINFO, *PGUITHREADINFO, *LPGUITHREADINFO;
+
+#define GUI_CARETBLINKING   0x00000001
+#define GUI_INMOVESIZE      0x00000002
+#define GUI_INMENUMODE      0x00000004
+#define GUI_SYSTEMMENUMODE  0x00000008
+#define GUI_POPUPMENUMODE   0x00000010
+#define GUI_16BITTASK       0x00000020
+
+
 /***** Dialogs *****/
 
 /* Gcc on Solaris has a version of this that we don't care about */
@@ -4079,15 +4100,16 @@
 INT         WINAPI GetDlgItemTextA(HWND,INT,LPSTR,UINT);
 INT         WINAPI GetDlgItemTextW(HWND,INT,LPWSTR,UINT);
 #define     GetDlgItemText WINELIB_NAME_AW(GetDlgItemText)
-UINT      WINAPI GetDoubleClickTime(void);
-HWND      WINAPI GetFocus(void);
-HWND      WINAPI GetForegroundWindow(void);
-BOOL      WINAPI GetInputState(void);
-UINT      WINAPI GetInternalWindowPos(HWND,LPRECT,LPPOINT);
-UINT      WINAPI GetKBCodePage(void);
-INT       WINAPI GetKeyboardType(INT);
-INT       WINAPI GetKeyNameTextA(LONG,LPSTR,INT);
-INT       WINAPI GetKeyNameTextW(LONG,LPWSTR,INT);
+UINT        WINAPI GetDoubleClickTime(void);
+HWND        WINAPI GetFocus(void);
+HWND        WINAPI GetForegroundWindow(void);
+BOOL        WINAPI GetGUIThreadInfo(DWORD,GUITHREADINFO*);
+BOOL        WINAPI GetInputState(void);
+UINT        WINAPI GetInternalWindowPos(HWND,LPRECT,LPPOINT);
+UINT        WINAPI GetKBCodePage(void);
+INT         WINAPI GetKeyboardType(INT);
+INT         WINAPI GetKeyNameTextA(LONG,LPSTR,INT);
+INT         WINAPI GetKeyNameTextW(LONG,LPWSTR,INT);
 #define     GetKeyNameText WINELIB_NAME_AW(GetKeyNameText)
 INT       WINAPI GetKeyboardLayoutNameA(LPSTR);
 INT       WINAPI GetKeyboardLayoutNameW(LPWSTR);
diff --git a/server/protocol.def b/server/protocol.def
index 1aeb6c4..43f4fa0 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1908,3 +1908,26 @@
     int            total;         /* total number of properties */
     VARARG(props,properties);     /* list of properties */
 @END
+
+
+/* Attach (or detach) thread inputs */
+@REQ(attach_thread_input)
+    thread_id_t    tid_from;       /* thread to be attached */
+    thread_id_t    tid_to;         /* thread to which tid_from should be attached */
+    int            attach;         /* is it an attach? */
+@END
+
+
+/* Get input data for a given thread */
+@REQ(get_thread_input)
+    thread_id_t    tid;           /* id of thread */
+@REPLY
+    user_handle_t  focus;         /* handle to the focus window */
+    user_handle_t  capture;       /* handle to the capture window */
+    user_handle_t  active;        /* handle to the active window */
+    user_handle_t  foreground;    /* handle to the global foreground window */
+    user_handle_t  menu_owner;    /* handle to the menu owner */
+    user_handle_t  move_size;     /* handle to the moving/resizing window */
+    user_handle_t  caret;         /* handle to the caret window */
+    rectangle_t    rect;          /* caret rectangle */
+@END
diff --git a/server/queue.c b/server/queue.c
index e250fae..5a08668 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -89,6 +89,19 @@
     unsigned int    lparam;    /* lparam for message */
 };
 
+struct thread_input
+{
+    struct object          obj;           /* object header */
+    user_handle_t          focus;         /* focus window */
+    user_handle_t          capture;       /* capture window */
+    user_handle_t          active;        /* active window */
+    user_handle_t          menu_owner;    /* current menu owner window */
+    user_handle_t          move_size;     /* current moving/resizing window */
+    user_handle_t          caret;         /* caret window */
+    rectangle_t            rect;          /* caret rectangle */
+    unsigned char          keystate[256]; /* state of each key */
+};
+
 struct msg_queue
 {
     struct object          obj;           /* object header */
@@ -106,6 +119,7 @@
     struct timer          *last_timer;    /* tail of timer list */
     struct timer          *next_timer;    /* next timer to expire */
     struct timeout_user   *timeout;       /* timeout for next timer to expire */
+    struct thread_input   *input;         /* thread input descriptor */
 };
 
 static void msg_queue_dump( struct object *obj, int verbose );
@@ -114,6 +128,8 @@
 static int msg_queue_signaled( struct object *obj, struct thread *thread );
 static int msg_queue_satisfied( struct object *obj, struct thread *thread );
 static void msg_queue_destroy( struct object *obj );
+static void thread_input_dump( struct object *obj, int verbose );
+static void thread_input_destroy( struct object *obj );
 static void timer_callback( void *private );
 
 static const struct object_ops msg_queue_ops =
@@ -134,11 +150,55 @@
 };
 
 
-static struct msg_queue *create_msg_queue( struct thread *thread )
+static const struct object_ops thread_input_ops =
+{
+    sizeof(struct thread_input),  /* size */
+    thread_input_dump,            /* dump */
+    no_add_queue,                 /* add_queue */
+    NULL,                         /* remove_queue */
+    NULL,                         /* signaled */
+    NULL,                         /* satisfied */
+    NULL,                         /* get_poll_events */
+    NULL,                         /* poll_event */
+    no_get_fd,                    /* get_fd */
+    no_flush,                     /* flush */
+    no_get_file_info,             /* get_file_info */
+    NULL,                         /* queue_async */
+    thread_input_destroy          /* destroy */
+};
+
+/* create a thread input object */
+static struct thread_input *create_thread_input(void)
+{
+    struct thread_input *input;
+
+    if ((input = alloc_object( &thread_input_ops, -1 )))
+    {
+        input->focus       = 0;
+        input->capture     = 0;
+        input->active      = 0;
+        input->menu_owner  = 0;
+        input->move_size   = 0;
+        input->caret       = 0;
+        input->rect.left   = 0;
+        input->rect.top    = 0;
+        input->rect.right  = 0;
+        input->rect.bottom = 0;
+        memset( input->keystate, 0, sizeof(input->keystate) );
+    }
+    return input;
+}
+
+/* pointer to input structure of foreground thread */
+static struct thread_input *foreground_input;
+
+/* create a message queue object */
+static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_input *input )
 {
     struct msg_queue *queue;
     int i;
 
+    if (!input && !(input = create_thread_input())) return NULL;
     if ((queue = alloc_object( &msg_queue_ops, -1 )))
     {
         queue->wake_bits       = 0;
@@ -153,6 +213,7 @@
         queue->last_timer      = NULL;
         queue->next_timer      = NULL;
         queue->timeout         = NULL;
+        queue->input           = (struct thread_input *)grab_object( input );
         for (i = 0; i < NB_MSG_KINDS; i++)
             queue->msg_list[i].first = queue->msg_list[i].last = NULL;
 
@@ -160,6 +221,7 @@
         if (!thread->process->queue)
             thread->process->queue = (struct msg_queue *)grab_object( queue );
     }
+    release_object( input );
     return queue;
 }
 
@@ -217,7 +279,7 @@
 inline static struct msg_queue *get_current_queue(void)
 {
     struct msg_queue *queue = current->queue;
-    if (!queue) queue = create_msg_queue( current );
+    if (!queue) queue = create_msg_queue( current, NULL );
     return queue;
 }
 
@@ -529,8 +591,76 @@
         timer = next;
     }
     if (queue->timeout) remove_timeout_user( queue->timeout );
+    if (queue->input) release_object( queue->input );
 }
 
+static void thread_input_dump( struct object *obj, int verbose )
+{
+    struct thread_input *input = (struct thread_input *)obj;
+    fprintf( stderr, "Thread input focus=%x capture=%x active=%x\n",
+             input->focus, input->capture, input->active );
+}
+
+static void thread_input_destroy( struct object *obj )
+{
+    struct thread_input *input = (struct thread_input *)obj;
+
+    if (foreground_input == input) foreground_input = NULL;
+}
+
+/* fix the thread input data when a window is destroyed */
+inline static void thread_input_cleanup_window( struct msg_queue *queue, user_handle_t window )
+{
+    struct thread_input *input = queue->input;
+
+    if (window == input->focus) input->focus = 0;
+    if (window == input->capture) input->capture = 0;
+    if (window == input->active) input->active = 0;
+    if (window == input->menu_owner) input->menu_owner = 0;
+    if (window == input->move_size) input->move_size = 0;
+    if (window == input->caret) input->caret = 0;
+}
+
+/* attach two thread input data structures */
+int attach_thread_input( struct thread *thread_from, struct thread *thread_to )
+{
+    struct thread_input *input;
+
+    if (!thread_to->queue && !(thread_to->queue = create_msg_queue( thread_to, NULL ))) return 0;
+    input = (struct thread_input *)grab_object( thread_to->queue->input );
+
+    if (thread_from->queue)
+    {
+        release_object( thread_from->queue->input );
+        thread_from->queue->input = input;
+    }
+    else
+    {
+        if (!(thread_from->queue = create_msg_queue( thread_from, input ))) return 0;
+    }
+    memset( input->keystate, 0, sizeof(input->keystate) );
+    return 1;
+}
+
+/* detach two thread input data structures */
+static void detach_thread_input( struct thread *thread_from, struct thread *thread_to )
+{
+    struct thread_input *input;
+
+    if (!thread_from->queue || !thread_to->queue ||
+        thread_from->queue->input != thread_to->queue->input)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return;
+    }
+    if ((input = create_thread_input()))
+    {
+        release_object( thread_from->queue->input );
+        thread_from->queue->input = input;
+    }
+}
+
+
 /* set the next timer to expire */
 static void set_next_timer( struct msg_queue *queue, struct timer *timer )
 {
@@ -706,6 +836,8 @@
             msg = next;
         }
     }
+
+    thread_input_cleanup_window( queue, win );
 }
 
 /* post a message to a window; used by socket handling */
@@ -1098,3 +1230,65 @@
     if (!queue || !kill_timer( queue, get_user_full_handle(req->win), req->msg, req->id ))
         set_error( STATUS_INVALID_PARAMETER );
 }
+
+
+/* attach (or detach) thread inputs */
+DECL_HANDLER(attach_thread_input)
+{
+    struct thread *thread_from = get_thread_from_id( req->tid_from );
+    struct thread *thread_to = get_thread_from_id( req->tid_to );
+
+    if (!thread_from || !thread_to)
+    {
+        if (thread_from) release_object( thread_from );
+        if (thread_to) release_object( thread_to );
+        return;
+    }
+    if (thread_from != thread_to)
+    {
+        if (req->attach) attach_thread_input( thread_from, thread_to );
+        else detach_thread_input( thread_from, thread_to );
+    }
+    else set_error( STATUS_ACCESS_DENIED );
+    release_object( thread_from );
+    release_object( thread_to );
+}
+
+
+/* get thread input data */
+DECL_HANDLER(get_thread_input)
+{
+    struct thread *thread = NULL;
+    struct thread_input *input;
+
+    if (req->tid)
+    {
+        if (!(thread = get_thread_from_id( req->tid ))) return;
+        input = thread->queue ? thread->queue->input : NULL;
+    }
+    else input = foreground_input;  /* get the foreground thread info */
+
+    if (input)
+    {
+        reply->focus      = input->focus;
+        reply->capture    = input->capture;
+        reply->active     = input->active;
+        reply->menu_owner = input->menu_owner;
+        reply->move_size  = input->move_size;
+        reply->caret      = input->caret;
+        reply->rect       = input->rect;
+    }
+    else
+    {
+        reply->focus      = 0;
+        reply->capture    = 0;
+        reply->active     = 0;
+        reply->menu_owner = 0;
+        reply->move_size  = 0;
+        reply->caret      = 0;
+        reply->rect.left = reply->rect.top = reply->rect.right = reply->rect.bottom = 0;
+    }
+    /* foreground window is active window of foreground thread */
+    reply->foreground = foreground_input ? foreground_input->active : 0;
+    if (thread) release_object( thread );
+}
diff --git a/server/request.h b/server/request.h
index 0c744b4..3f1a036 100644
--- a/server/request.h
+++ b/server/request.h
@@ -259,6 +259,8 @@
 DECL_HANDLER(remove_window_property);
 DECL_HANDLER(get_window_property);
 DECL_HANDLER(get_window_properties);
+DECL_HANDLER(attach_thread_input);
+DECL_HANDLER(get_thread_input);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -421,6 +423,8 @@
     (req_handler)req_remove_window_property,
     (req_handler)req_get_window_property,
     (req_handler)req_get_window_properties,
+    (req_handler)req_attach_thread_input,
+    (req_handler)req_get_thread_input,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/trace.c b/server/trace.c
index 18cda73..776370f 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2175,6 +2175,31 @@
     dump_varargs_properties( cur_size );
 }
 
+static void dump_attach_thread_input_request( const struct attach_thread_input_request *req )
+{
+    fprintf( stderr, " tid_from=%08x,", req->tid_from );
+    fprintf( stderr, " tid_to=%08x,", req->tid_to );
+    fprintf( stderr, " attach=%d", req->attach );
+}
+
+static void dump_get_thread_input_request( const struct get_thread_input_request *req )
+{
+    fprintf( stderr, " tid=%08x", req->tid );
+}
+
+static void dump_get_thread_input_reply( const struct get_thread_input_reply *req )
+{
+    fprintf( stderr, " focus=%08x,", req->focus );
+    fprintf( stderr, " capture=%08x,", req->capture );
+    fprintf( stderr, " active=%08x,", req->active );
+    fprintf( stderr, " foreground=%08x,", req->foreground );
+    fprintf( stderr, " menu_owner=%08x,", req->menu_owner );
+    fprintf( stderr, " move_size=%08x,", req->move_size );
+    fprintf( stderr, " caret=%08x,", req->caret );
+    fprintf( stderr, " rect=" );
+    dump_rectangle( &req->rect );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_get_new_process_info_request,
@@ -2332,6 +2357,8 @@
     (dump_func)dump_remove_window_property_request,
     (dump_func)dump_get_window_property_request,
     (dump_func)dump_get_window_properties_request,
+    (dump_func)dump_attach_thread_input_request,
+    (dump_func)dump_get_thread_input_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -2491,6 +2518,8 @@
     (dump_func)dump_remove_window_property_reply,
     (dump_func)dump_get_window_property_reply,
     (dump_func)dump_get_window_properties_reply,
+    (dump_func)0,
+    (dump_func)dump_get_thread_input_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -2650,6 +2679,8 @@
     "remove_window_property",
     "get_window_property",
     "get_window_properties",
+    "attach_thread_input",
+    "get_thread_input",
 };
 
 /* ### make_requests end ### */
diff --git a/server/user.h b/server/user.h
index 6d5766e..b1cc439 100644
--- a/server/user.h
+++ b/server/user.h
@@ -46,6 +46,7 @@
 extern void free_msg_queue( struct thread *thread );
 extern void inc_queue_paint_count( struct thread *thread, int incr );
 extern void queue_cleanup_window( struct thread *thread, user_handle_t win );
+extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to );
 extern void post_message( user_handle_t win, unsigned int message,
                           unsigned int wparam, unsigned int lparam );
 
diff --git a/server/window.c b/server/window.c
index e513b87..ad51288 100644
--- a/server/window.c
+++ b/server/window.c
@@ -287,6 +287,9 @@
     }
     else win->next = win->prev = NULL;
 
+    /* if parent belongs to a different thread, attach the two threads */
+    if (parent && parent->thread && parent->thread != current)
+        attach_thread_input( current, parent->thread );
     return win;
 }
 
diff --git a/windows/queue.c b/windows/queue.c
index 83c2d3e..291c2cc 100644
--- a/windows/queue.c
+++ b/windows/queue.c
@@ -593,88 +593,3 @@
     if (!(queue = QUEUE_Current())) return 0;
     return queue->GetMessageExtraInfoVal;
 }
-
-
-/**********************************************************************
- *		AttachThreadInput (USER32.@) Attaches input of 1 thread to other
- *
- * Attaches the input processing mechanism of one thread to that of
- * another thread.
- *
- * RETURNS
- *    Success: TRUE
- *    Failure: FALSE
- *
- * TODO:
- *    1. Reset the Key State (currenly per thread key state is not maintained)
- */
-BOOL WINAPI AttachThreadInput(
-    DWORD idAttach,   /* [in] Thread to attach */
-    DWORD idAttachTo, /* [in] Thread to attach to */
-    BOOL fAttach)   /* [in] Attach or detach */
-{
-    MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
-    BOOL16 bRet = 0;
-
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-
-    /* A thread cannot attach to itself */
-    if ( idAttach == idAttachTo )
-        goto CLEANUP;
-
-    /* According to the docs this method should fail if a
-     * "Journal record" hook is installed. (attaches all input queues together)
-     */
-    if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
-        goto CLEANUP;
-
-    /* Retrieve message queues corresponding to the thread id's */
-    pTgtMsgQ = QUEUE_Lock( GetThreadQueue16( idAttach ) );
-    pSrcMsgQ = QUEUE_Lock( GetThreadQueue16( idAttachTo ) );
-
-    /* Ensure we have message queues and that Src and Tgt threads
-     * are not system threads.
-     */
-    if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
-        goto CLEANUP;
-
-    if (fAttach)   /* Attach threads */
-    {
-        /* Only attach if currently detached  */
-        if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
-        {
-            /* First release the target threads perQData */
-            PERQDATA_Release( pTgtMsgQ->pQData );
-
-            /* Share a reference to the source threads perQDATA */
-            PERQDATA_Addref( pSrcMsgQ->pQData );
-            pTgtMsgQ->pQData = pSrcMsgQ->pQData;
-        }
-    }
-    else    /* Detach threads */
-    {
-        /* Only detach if currently attached */
-        if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
-        {
-            /* First release the target threads perQData */
-            PERQDATA_Release( pTgtMsgQ->pQData );
-
-            /* Give the target thread its own private perQDATA once more */
-            pTgtMsgQ->pQData = PERQDATA_CreateInstance();
-        }
-    }
-
-    /* TODO: Reset the Key State */
-
-    bRet = 1;      /* Success */
-
-CLEANUP:
-
-    /* Unlock the queues before returning */
-    if ( pSrcMsgQ )
-        QUEUE_Unlock( pSrcMsgQ );
-    if ( pTgtMsgQ )
-        QUEUE_Unlock( pTgtMsgQ );
-
-    return bRet;
-}