Add support for winevent hooks

Dmitry Timoshkov dmitry at baikal.ru
Wed Feb 2 02:59:09 CST 2005


Hello,

this patch adds support for winevent hooks and contains all the fixes
Alexandre suggested to made. Once this patch is accepted what remains
to be done is to add NotifyWinEvent calls here and there in Wine code
to make message tests pass.

Changelog:
    Dmitry Timoshkov <dmitry at codeweavers.com>
    Add support for winevent hooks.

diff -up cvs/hq/wine/dlls/ttydrv/wnd.c wine/dlls/ttydrv/wnd.c
--- cvs/hq/wine/dlls/ttydrv/wnd.c	2005-01-26 17:55:56.000000000 +0800
+++ wine/dlls/ttydrv/wnd.c	2005-02-02 15:33:45.000000000 +0800
@@ -187,6 +187,7 @@ BOOL TTYDRV_CreateWindow( HWND hwnd, CRE
         ret = SendMessageA( hwnd, WM_NCCREATE, 0, (LPARAM)cs );
         if (ret) ret = (SendMessageA( hwnd, WM_CREATE, 0, (LPARAM)cs ) != -1);
     }
+    if (ret) NotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0);
     return ret;
 }
 
diff -up cvs/hq/wine/dlls/user/hook.c wine/dlls/user/hook.c
--- cvs/hq/wine/dlls/user/hook.c	2005-01-31 08:54:03.000000000 +0800
+++ wine/dlls/user/hook.c	2005-02-02 15:33:45.000000000 +0800
@@ -2,6 +2,7 @@
  * Windows hook functions
  *
  * Copyright 2002 Alexandre Julliard
+ * Copyright 2005 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -65,6 +66,7 @@
 #include "wine/port.h"
 
 #include <stdarg.h>
+#include <assert.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -81,7 +83,17 @@
 WINE_DEFAULT_DEBUG_CHANNEL(hook);
 WINE_DECLARE_DEBUG_CHANNEL(relay);
 
-static const char * const hook_names[WH_MAXHOOK - WH_MINHOOK + 1] =
+struct hook_info
+{
+    FARPROC proc;
+    void *handle;
+    DWORD tid;
+    WCHAR module[MAX_PATH];
+};
+
+#define WH_WINEVENT (WH_MAXHOOK+1)
+
+static const char * const hook_names[WH_WINEVENT - WH_MINHOOK + 1] =
 {
     "WH_MSGFILTER",
     "WH_JOURNALRECORD",
@@ -98,7 +110,8 @@ static const char * const hook_names[WH_
     "WH_FOREGROUNDIDLE",
     "WH_CALLWNDPROCRET",
     "WH_KEYBOARD_LL",
-    "WH_MOUSE_LL"
+    "WH_MOUSE_LL",
+    "WH_WINEVENT"
 };
 
 
@@ -130,6 +143,13 @@ static HHOOK set_windows_hook( INT id, H
         return 0;
     }
 
+    if (!proc)
+    {
+        SetLastError( ERROR_INVALID_FILTER_PROC );
+        return 0;
+    }
+
+    /* FIXME: what if the tid belongs to another process? */
     if (tid)  /* thread-local hook */
     {
         if (id == WH_JOURNALRECORD ||
@@ -156,9 +176,13 @@ static HHOOK set_windows_hook( INT id, H
 
     SERVER_START_REQ( set_hook )
     {
-        req->id      = id;
-        req->tid     = tid;
-        req->unicode = unicode;
+        req->id        = id;
+        req->pid       = 0;
+        req->tid       = tid;
+        req->event_min = EVENT_MIN;
+        req->event_max = EVENT_MAX;
+        req->flags     = WINEVENT_INCONTEXT;
+        req->unicode   = unicode;
         if (inst) /* make proc relative to the module base */
         {
             req->proc = (void *)((char *)proc - (char *)inst);
@@ -281,7 +305,7 @@ static LRESULT call_hook( HOOKPROC proc,
  *
  * Retrieve the hook procedure real value for a module-relative proc
  */
-static HOOKPROC get_hook_proc( HOOKPROC proc, const WCHAR *module )
+static void *get_hook_proc( void *proc, const WCHAR *module )
 {
     HMODULE mod;
 
@@ -291,7 +315,7 @@ static HOOKPROC get_hook_proc( HOOKPROC 
         /* FIXME: the library will never be freed */
         if (!(mod = LoadLibraryW(module))) return NULL;
     }
-    return (HOOKPROC)((char *)mod + (ULONG_PTR)proc);
+    return (char *)mod + (ULONG_PTR)proc;
 }
 
 
@@ -312,6 +336,7 @@ LRESULT HOOK_CallHooks( INT id, INT code
     SERVER_START_REQ( start_hook_chain )
     {
         req->id = id;
+        req->event = EVENT_MIN;
         wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) );
         if (!wine_server_call( req ))
         {
@@ -340,6 +365,10 @@ LRESULT HOOK_CallHooks( INT id, INT code
             MSG_SendInternalMessageTimeout( pid, tid, WM_WINE_MOUSE_LL_HOOK, wparam, lparam,
                                             SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret );
             break;
+        default:
+            ERR("Unknown hook id %d\n", id);
+            assert(0);
+            break;
         }
     }
     else if (proc)
@@ -448,6 +477,7 @@ BOOL WINAPI UnhookWindowsHookEx( HHOOK h
     SERVER_START_REQ( remove_hook )
     {
         req->handle = hhook;
+        req->id     = 0;
         ret = !wine_server_call_err( req );
     }
     SERVER_END_REQ;
@@ -474,6 +504,7 @@ LRESULT WINAPI CallNextHookEx( HHOOK hho
     SERVER_START_REQ( get_next_hook )
     {
         req->handle = queue->hook;
+        req->event = EVENT_MIN;
         wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) );
         if (!wine_server_call_err( req ))
         {
@@ -504,6 +535,10 @@ LRESULT WINAPI CallNextHookEx( HHOOK hho
             MSG_SendInternalMessageTimeout( pid, tid, WM_WINE_MOUSE_LL_HOOK, wparam, lparam,
                                             SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret );
             break;
+        default:
+            ERR("Unknown hook id %d\n", id);
+            assert(0);
+            break;
         }
     }
     else if (proc)
@@ -549,28 +584,73 @@ BOOL WINAPI CallMsgFilterW( LPMSG msg, I
  * Set up an event hook for a set of events.
  *
  * PARAMS
- *  dwMin     [I] Lowest event handled by pfnProc
- *  dwMax     [I] Highest event handled by pfnProc
- *  hModule   [I] DLL containing pfnProc
- *  pfnProc   [I] Callback event hook function
- *  dwProcess [I] Process to get events from, or 0 for all processes
- *  dwThread  [I] Thread to get events from, or 0 for all threads
- *  dwFlags   [I] Flags indicating the status of pfnProc
+ *  event_min [I] Lowest event handled by pfnProc
+ *  event_max [I] Highest event handled by pfnProc
+ *  inst      [I] DLL containing pfnProc
+ *  proc      [I] Callback event hook function
+ *  pid       [I] Process to get events from, or 0 for all processes
+ *  tid       [I] Thread to get events from, or 0 for all threads
+ *  flags     [I] Flags indicating the status of pfnProc
  *
  * RETURNS
  *  Success: A handle representing the hook.
  *  Failure: A NULL handle.
- *
- * BUGS
- *  Not implemented.
  */
-HWINEVENTHOOK WINAPI SetWinEventHook(DWORD dwMin, DWORD dwMax, HMODULE hModule,
-                                     WINEVENTPROC pfnProc, DWORD dwProcess,
-                                     DWORD dwThread, DWORD dwFlags)
-{
-    FIXME("(%ld,%ld,%p,%p,%ld,%ld,0x%08lx)-stub!\n", dwMin, dwMax, hModule,
-          pfnProc, dwProcess, dwThread, dwFlags);
-    return 0;
+HWINEVENTHOOK WINAPI SetWinEventHook(DWORD event_min, DWORD event_max,
+                                     HMODULE inst, WINEVENTPROC proc,
+                                     DWORD pid, DWORD tid, DWORD flags)
+{
+    HWINEVENTHOOK handle = 0;
+    WCHAR module[MAX_PATH];
+    DWORD len;
+
+    TRACE("%ld,%ld,%p,%p,%08lx,%04lx,%08lx\n", event_min, event_max, inst,
+          proc, pid, tid, flags);
+
+    if (inst)
+    {
+        if (!(len = GetModuleFileNameW(inst, module, MAX_PATH)) || len >= MAX_PATH)
+            inst = 0;
+    }
+
+    if ((flags & WINEVENT_INCONTEXT) && !inst)
+    {
+        SetLastError(ERROR_HOOK_NEEDS_HMOD);
+        return 0;
+    }
+
+    if (event_min > event_max)
+    {
+        SetLastError(ERROR_INVALID_HOOK_FILTER);
+        return 0;
+    }
+
+    /* FIXME: what if the tid or pid belongs to another process? */
+    if (tid)  /* thread-local hook */
+        inst = 0;
+
+    SERVER_START_REQ( set_hook )
+    {
+        req->id        = WH_WINEVENT;
+        req->pid       = pid;
+        req->tid       = tid;
+        req->event_min = event_min;
+        req->event_max = event_max;
+        req->flags     = flags;
+        req->unicode   = 1;
+        if (inst) /* make proc relative to the module base */
+        {
+            req->proc = (void *)((char *)proc - (char *)inst);
+            wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) );
+        }
+        else req->proc = proc;
+
+        if (!wine_server_call_err( req )) handle = reply->handle;
+    }
+    SERVER_END_REQ;
+
+    TRACE("-> %p\n", handle);
+    return handle;
 }
 
 
@@ -585,17 +665,84 @@ HWINEVENTHOOK WINAPI SetWinEventHook(DWO
  * RETURNS
  *  Success: TRUE. The event hook has been removed.
  *  Failure: FALSE, if hEventHook is invalid.
- *
- * BUGS
- *  Not implemented.
  */
 BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hEventHook)
 {
-    FIXME("(%p)-stub!\n", hEventHook);
+    BOOL ret;
 
-    return (hEventHook != 0);
+    TRACE( "%p\n", hEventHook );
+
+    SERVER_START_REQ( remove_hook )
+    {
+        req->handle = hEventHook;
+        req->id     = WH_WINEVENT;
+        ret = !wine_server_call_err( req );
+    }
+    SERVER_END_REQ;
+    return ret;
 }
 
+inline static BOOL find_first_hook(DWORD id, DWORD event, HWND hwnd, LONG object_id,
+                                   LONG child_id, struct hook_info *info)
+{
+    BOOL ret;
+
+    SERVER_START_REQ( start_hook_chain )
+    {
+        req->id = id;
+        req->event = event;
+        req->window = hwnd;
+        req->object_id = object_id;
+        req->child_id = child_id;
+        wine_server_set_reply( req, info->module, sizeof(info->module)-sizeof(WCHAR) );
+        ret = !wine_server_call( req );
+        if (ret)
+        {
+            info->module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
+            info->handle    = reply->handle;
+            info->proc      = reply->proc;
+            info->tid       = reply->tid;
+        }
+    }
+    SERVER_END_REQ;
+    return ret && (info->tid || info->proc);
+}
+
+inline static BOOL find_next_hook(DWORD event, HWND hwnd, LONG object_id,
+                                  LONG child_id, struct hook_info *info)
+{
+    BOOL ret;
+
+    SERVER_START_REQ( get_next_hook )
+    {
+        req->handle = info->handle;
+        req->event = event;
+        req->window = hwnd;
+        req->object_id = object_id;
+        req->child_id = child_id;
+        wine_server_set_reply( req, info->module, sizeof(info->module)-sizeof(WCHAR) );
+        ret = !wine_server_call( req );
+        if (ret)
+        {
+            info->module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
+            info->handle    = reply->next;
+            info->proc      = reply->proc;
+            info->tid       = reply->tid;
+        }
+    }
+    SERVER_END_REQ;
+    return ret;
+}
+
+inline static void find_hook_close(DWORD id)
+{
+    SERVER_START_REQ( finish_hook_chain )
+    {
+        req->id = id;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
 
 /***********************************************************************
  *           NotifyWinEvent                             [USER32.@]
@@ -603,20 +750,83 @@ BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK
  * Inform the OS that an event has occurred.
  *
  * PARAMS
- *  dwEvent  [I] Id of the event
- *  hWnd     [I] Window holding the object that created the event
- *  nId      [I] Type of object that created the event
- *  nChildId [I] Child object of nId, or CHILDID_SELF.
+ *  event     [I] Id of the event
+ *  hwnd      [I] Window holding the object that created the event
+ *  object_id [I] Type of object that created the event
+ *  child_id  [I] Child object of nId, or CHILDID_SELF.
  *
  * RETURNS
  *  Nothing.
- *
- * BUGS
- *  Not implemented.
  */
-void WINAPI NotifyWinEvent(DWORD dwEvent, HWND hWnd, LONG nId, LONG nChildId)
+void WINAPI NotifyWinEvent(DWORD event, HWND hwnd, LONG object_id, LONG child_id)
 {
-    FIXME("(%ld,%p,%ld,%ld)-stub!\n", dwEvent, hWnd, nId, nChildId);
+    struct hook_info info;
+
+    TRACE("%04lx,%p,%ld,%ld\n", event, hwnd, object_id, child_id);
+
+    if (!hwnd)
+    {
+        SetLastError(ERROR_INVALID_WINDOW_HANDLE);
+        return;
+    }
+
+#if 0
+    if (event & 0x80000000)
+    {
+        /* FIXME: on 64-bit platforms we need to invent some other way for
+         * passing parameters, nId and nChildId can't hold full [W|L]PARAM.
+         * struct call_hook *hook = (LRESULT *)hWnd;
+         * wparam = hook->wparam;
+         * lparam = hook->lparam;
+         */
+        LRESULT *ret = (LRESULT *)hwnd;
+        INT id, code, unicode;
+
+        id = (dwEvent & 0x7fff0000) >> 16;
+        code = event & 0x7fff;
+        unicode = event & 0x8000;
+        *ret = HOOK_CallHooks(id, code, object_id, child_id, unicode);
+        return;
+    }
+#endif
+
+    if (!find_first_hook(WH_WINEVENT, event, hwnd, object_id, child_id, &info)) return;
+
+    do
+    {
+        if (info.proc)
+        {
+            TRACE( "calling WH_WINEVENT hook %p event %lx hwnd %p %lx %lx module %s\n",
+                   info.proc, event, hwnd, object_id, child_id, debugstr_w(info.module) );
+
+            if (!info.module[0] || (info.proc = get_hook_proc( info.proc, info.module )) != NULL)
+            {
+                int locks = WIN_SuspendWndsLock();
+
+                if (TRACE_ON(relay))
+                    DPRINTF( "%04lx:Call winevent hook proc %p module %s (hhook %p,event %lx,hwnd %p,object_id %lx,child_id %lx,tid %04lx,time %lx)\n",
+                             GetCurrentThreadId(), info.proc, debugstr_w(info.module),
+                             info.handle, event, hwnd, object_id, child_id,
+                             GetCurrentThreadId(), GetCurrentTime());
+
+                info.proc(info.handle, event, hwnd, object_id, child_id,
+                          GetCurrentThreadId(), GetCurrentTime());
+
+                if (TRACE_ON(relay))
+                    DPRINTF( "%04lx:Ret  winevent hook proc %p module %s (hhook %p,event %lx,hwnd %p,object_id %lx,child_id %lx,tid %04lx,time %lx)\n",
+                             GetCurrentThreadId(), info.proc, debugstr_w(info.module),
+                             info.handle, event, hwnd, object_id, child_id,
+                             GetCurrentThreadId(), GetCurrentTime());
+
+                WIN_RestoreWndsLock( locks );
+            }
+        }
+        else
+            break;
+    }
+    while (find_next_hook(event, hwnd, object_id, child_id, &info));
+
+    find_hook_close(WH_WINEVENT);
 }
 
 
diff -up cvs/hq/wine/dlls/user/message.c wine/dlls/user/message.c
--- cvs/hq/wine/dlls/user/message.c	2005-01-10 13:55:54.000000000 +0800
+++ wine/dlls/user/message.c	2005-02-02 16:46:12.000000000 +0800
@@ -64,6 +64,8 @@ struct received_message_info
     enum message_type type;
     MSG               msg;
     UINT              flags;  /* InSendMessageEx return flags */
+    HWINEVENTHOOK     hook;   /* winevent hook handle */
+    WINEVENTPROC      hook_proc; /* winevent hook proc address */
 };
 
 /* structure to group all parameters for sent messages of the various kinds */
@@ -1542,6 +1544,25 @@ static inline void call_sendmsg_callback
 
 
 /***********************************************************************
+ *		get_hook_proc
+ *
+ * Retrieve the hook procedure real value for a module-relative proc
+ */
+static void *get_hook_proc( void *proc, const WCHAR *module )
+{
+    HMODULE mod;
+
+    if (!(mod = GetModuleHandleW(module)))
+    {
+        TRACE( "loading %s\n", debugstr_w(module) );
+        /* FIXME: the library will never be freed */
+        if (!(mod = LoadLibraryW(module))) return NULL;
+    }
+    return (char *)mod + (ULONG_PTR)proc;
+}
+
+
+/***********************************************************************
  *           MSG_peek_message
  *
  * Peek for a message matching the given parameters. Return FALSE if none available.
@@ -1584,6 +1605,8 @@ BOOL MSG_peek_message( MSG *msg, HWND hw
                     info.msg.time    = reply->time;
                     info.msg.pt.x    = reply->x;
                     info.msg.pt.y    = reply->y;
+                    info.hook        = reply->hook;
+                    info.hook_proc   = reply->hook_proc;
                     extra_info       = reply->info;
                 }
                 else
@@ -1598,7 +1621,8 @@ BOOL MSG_peek_message( MSG *msg, HWND hw
         if (res) return FALSE;
 
         TRACE( "got type %d msg %x (%s) hwnd %p wp %x lp %lx\n",
-               info.type, info.msg.message, SPY_GetMsgName(info.msg.message, info.msg.hwnd),
+               info.type, info.msg.message,
+               (info.type == MSG_WINEVENT) ? "MSG_WINEVENT" : SPY_GetMsgName(info.msg.message, info.msg.hwnd),
                info.msg.hwnd, info.msg.wParam, info.msg.lParam );
 
         switch(info.type)
@@ -1617,6 +1641,35 @@ BOOL MSG_peek_message( MSG *msg, HWND hw
             call_sendmsg_callback( (SENDASYNCPROC)info.msg.wParam, info.msg.hwnd,
                                    info.msg.message, extra_info, info.msg.lParam );
             goto next;
+        case MSG_WINEVENT:
+        {
+            WCHAR empty[1] = { 0 };
+            WCHAR *module = buffer ? buffer : empty;
+
+            if (module) module[size / sizeof(WCHAR)] = 0;
+
+            if (!size || (check_string( module, size ) &&
+                (info.hook_proc = get_hook_proc( info.hook_proc, module )) != NULL))
+            {
+                if (TRACE_ON(relay))
+                    DPRINTF( "%04lx:Call winevent proc %p module %s (hook %p,event %x,hwnd %p,object_id %x,child_id %lx,tid %04lx,time %lx)\n",
+                             GetCurrentThreadId(), info.hook_proc, debugstr_w(module),
+                             info.hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
+                             info.msg.lParam, extra_info, info.msg.time);
+
+                info.hook_proc( info.hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
+                      info.msg.lParam, extra_info, info.msg.time );
+
+                if (TRACE_ON(relay))
+                    DPRINTF( "%04lx:Ret  winevent proc %p module %s (hook %p,event %x,hwnd %p,object_id %x,child_id %lx,tid %04lx,time %lx)\n",
+                             GetCurrentThreadId(), info.hook_proc, debugstr_w(module),
+                             info.hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
+                             info.msg.lParam, extra_info, info.msg.time);
+            }
+            else
+                ERR("invalid winevent hook module name\n");
+            goto next;
+        }
         case MSG_OTHER_PROCESS:
             info.flags = ISMEX_SEND;
             if (!unpack_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
diff -up cvs/hq/wine/dlls/user/tests/msg.c wine/dlls/user/tests/msg.c
--- cvs/hq/wine/dlls/user/tests/msg.c	2005-01-31 08:54:04.000000000 +0800
+++ wine/dlls/user/tests/msg.c	2005-02-02 15:33:45.000000000 +0800
@@ -4978,8 +4978,8 @@ static void test_winevents(void)
     HWINEVENTHOOK hhook;
     const struct message *events = WmWinEventsSeq;
     HMODULE user32 = GetModuleHandleA("user32.dll");
-    FARPROC pSetWinEventHook = 0;/*GetProcAddress(user32, "SetWinEventHook");*/
-    FARPROC pUnhookWinEvent = 0;/*GetProcAddress(user32, "UnhookWinEvent");*/
+    FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook");
+    FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent");
     FARPROC pNotifyWinEvent = GetProcAddress(user32, "NotifyWinEvent");
 
     hwnd = CreateWindowExA(0, "TestWindowClass", NULL,
@@ -5134,7 +5134,7 @@ static void test_set_hook(void)
     HHOOK hhook;
     HWINEVENTHOOK hwinevent_hook;
     HMODULE user32 = GetModuleHandleA("user32.dll");
-    FARPROC pSetWinEventHook = 0;/*GetProcAddress(user32, "SetWinEventHook");*/
+    FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook");
     FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent");
 
     hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, GetModuleHandleA(0), GetCurrentThreadId());
@@ -5208,12 +5208,16 @@ static void test_set_hook(void)
     ok(GetLastError() == 0xdeadbeef, "unexpected error %ld\n", GetLastError());
     ok(pUnhookWinEvent(hwinevent_hook), "UnhookWinEvent error %ld\n", GetLastError());
 
+todo_wine {
+    /* This call succeeds under win2k SP4, but fails under Wine.
+       Does win2k test/use passed process id? */
     SetLastError(0xdeadbeef);
     hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX,
 	0, win_event_proc, 0xdeadbeef, 0, WINEVENT_OUTOFCONTEXT);
     ok(hwinevent_hook != 0, "SetWinEventHook error %ld\n", GetLastError());
     ok(GetLastError() == 0xdeadbeef, "unexpected error %ld\n", GetLastError());
     ok(pUnhookWinEvent(hwinevent_hook), "UnhookWinEvent error %ld\n", GetLastError());
+}
 
     SetLastError(0xdeadbeef);
     ok(!pUnhookWinEvent((HWINEVENTHOOK)0xdeadbeef), "UnhookWinEvent succeeded\n");
@@ -5225,9 +5229,9 @@ static void test_set_hook(void)
 START_TEST(msg)
 {
     HMODULE user32 = GetModuleHandleA("user32.dll");
-    FARPROC pSetWinEventHook = 0;/*GetProcAddress(user32, "SetWinEventHook");*/
-    FARPROC pUnhookWinEvent = 0;/*GetProcAddress(user32, "UnhookWinEvent");*/
-    FARPROC pIsWinEventHookInstalled = GetProcAddress(user32, "IsWinEventHookInstalled");
+    FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook");
+    FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent");
+    FARPROC pIsWinEventHookInstalled = 0;/*GetProcAddress(user32, "IsWinEventHookInstalled");*/
 
     if (!RegisterWindowClasses()) assert(0);
 
@@ -5247,17 +5251,19 @@ START_TEST(msg)
 	    for (event = EVENT_MIN; event <= EVENT_MAX; event++)
 		ok(pIsWinEventHookInstalled(event), "IsWinEventHookInstalled(%u) failed\n", event);
 	}
-
-        /* Fix message sequences before removing 3 lines below */
-        ok(pUnhookWinEvent(hEvent_hook), "UnhookWinEvent error %ld\n", GetLastError());
-        pUnhookWinEvent = 0;
-        hEvent_hook = 0;
     }
 
     cbt_hook_thread_id = GetCurrentThreadId();
     hCBT_hook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId());
     assert(hCBT_hook);
 
+    test_winevents();
+
+    /* Fix message sequences before removing 3 lines below */
+    ok(pUnhookWinEvent(hEvent_hook), "UnhookWinEvent error %ld\n", GetLastError());
+    pUnhookWinEvent = 0;
+    hEvent_hook = 0;
+
     test_messages();
     test_mdi_messages();
     test_button_messages();
@@ -5267,7 +5273,6 @@ START_TEST(msg)
     test_accelerators();
     test_timers();
     test_set_hook();
-    test_winevents();
 
     UnhookWindowsHookEx(hCBT_hook);
     if (pUnhookWinEvent)
diff -up cvs/hq/wine/dlls/x11drv/window.c wine/dlls/x11drv/window.c
--- cvs/hq/wine/dlls/x11drv/window.c	2005-02-02 15:29:24.000000000 +0800
+++ wine/dlls/x11drv/window.c	2005-02-02 15:33:45.000000000 +0800
@@ -1005,6 +1005,8 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE
         return FALSE;
     }
 
+    NotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0);
+
     /* Send the size messages */
 
     if (!(wndPtr = WIN_GetPtr(hwnd)) || wndPtr == WND_OTHER_PROCESS) return FALSE;
diff -up cvs/hq/wine/include/wine/server_protocol.h wine/include/wine/server_protocol.h
--- cvs/hq/wine/include/wine/server_protocol.h	2005-01-19 14:21:24.000000000 +0800
+++ wine/include/wine/server_protocol.h	2005-02-02 16:05:07.000000000 +0800
@@ -2142,7 +2142,8 @@ enum message_type
     MSG_CALLBACK_RESULT,
     MSG_OTHER_PROCESS,
     MSG_POSTED,
-    MSG_HARDWARE
+    MSG_HARDWARE,
+    MSG_WINEVENT
 };
 #define SEND_MSG_ABORT_IF_HUNG  0x01
 
@@ -2166,6 +2167,8 @@ struct get_message_reply
     unsigned int    lparam;
     int             x;
     int             y;
+    user_handle_t   hook;
+    void*           hook_proc;
     unsigned int    time;
     unsigned int    info;
     size_t          total;
@@ -2948,7 +2951,11 @@ struct set_hook_request
 {
     struct request_header __header;
     int            id;
+    process_id_t   pid;
     thread_id_t    tid;
+    int            event_min;
+    int            event_max;
+    int            flags;
     void*          proc;
     int            unicode;
     /* VARARG(module,unicode_str); */
@@ -2979,6 +2986,10 @@ struct start_hook_chain_request
 {
     struct request_header __header;
     int            id;
+    int            event;
+    user_handle_t  window;
+    int            object_id;
+    int            child_id;
 };
 struct start_hook_chain_reply
 {
@@ -3009,6 +3020,10 @@ struct get_next_hook_request
 {
     struct request_header __header;
     user_handle_t  handle;
+    int            event;
+    user_handle_t  window;
+    int            object_id;
+    int            child_id;
 };
 struct get_next_hook_reply
 {
@@ -3712,6 +3727,6 @@ union generic_reply
     struct set_global_windows_reply set_global_windows_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 154
+#define SERVER_PROTOCOL_VERSION 155
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff -up cvs/hq/wine/server/hook.c wine/server/hook.c
--- cvs/hq/wine/server/hook.c	2005-01-31 08:54:07.000000000 +0800
+++ wine/server/hook.c	2005-02-02 15:40:43.000000000 +0800
@@ -2,6 +2,7 @@
  * Server-side window hooks support
  *
  * Copyright (C) 2002 Alexandre Julliard
+ * Copyright (C) 2005 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -40,15 +41,22 @@ struct hook
 {
     struct list         chain;    /* hook chain entry */
     user_handle_t       handle;   /* user handle for this hook */
-    struct thread      *thread;   /* thread owning the hook */
+    struct process     *process;  /* process the hook is set to */
+    struct thread      *thread;   /* thread the hook is set to */
+    struct thread      *owner;    /* owner of the out of context hook */
     int                 index;    /* hook table index */
+    int                 event_min;
+    int                 event_max;
+    int                 flags;
     void               *proc;     /* hook function */
     int                 unicode;  /* is it a unicode hook? */
     WCHAR              *module;   /* module name for global hooks */
     size_t              module_size;
 };
 
-#define NB_HOOKS (WH_MAXHOOK-WH_MINHOOK+1)
+#define WH_WINEVENT (WH_MAXHOOK+1)
+
+#define NB_HOOKS (WH_WINEVENT-WH_MINHOOK+1)
 #define HOOK_ENTRY(p)  LIST_ENTRY( (p), struct hook, chain )
 
 struct hook_table
@@ -124,6 +132,8 @@ static void free_hook( struct hook *hook
     free_user_handle( hook->handle );
     if (hook->module) free( hook->module );
     if (hook->thread) release_object( hook->thread );
+    if (hook->process) release_object( hook->process );
+    release_object( hook->owner );
     list_remove( &hook->chain );
     free( hook );
 }
@@ -162,27 +172,68 @@ inline static struct hook *get_first_hoo
 }
 
 /* find the first non-deleted hook in the chain */
-inline static struct hook *get_first_valid_hook( struct hook_table *table, int index )
+inline static struct hook *get_first_valid_hook( struct hook_table *table, int index,
+                                                 int event, user_handle_t win,
+                                                 int object_id, int child_id )
 {
     struct hook *hook = get_first_hook( table, index );
-    while (hook && !hook->proc)
+
+    while (hook)
+    {
+        if ((!hook->process || hook->process == current->process) &&
+            (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
+        {
+            if ((!hook->thread || hook->thread == current) &&
+                (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
+            {
+                if (hook->proc && event >= hook->event_min && event <= hook->event_max)
+                {
+                    if (hook->flags & WINEVENT_INCONTEXT) return hook;
+
+                    /* only winevent hooks may be out of context */
+                    assert(hook->index + WH_MINHOOK == WH_WINEVENT);
+                    post_win_event( hook->owner, event, win, object_id, child_id,
+                                    hook->proc, hook->module, hook->module_size,
+                                    hook->handle );
+                }
+            }
+        }
         hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) );
+    }
     return hook;
 }
 
 /* find the next hook in the chain, skipping the deleted ones */
-static struct hook *get_next_hook( struct hook *hook )
+static struct hook *get_next_hook( struct hook *hook, int event, user_handle_t win,
+                                   int object_id, int child_id )
 {
     struct hook_table *table = get_table( hook );
     int index = hook->index;
 
     while ((hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) )))
     {
-        if (hook->proc) return hook;
+        if ((!hook->process || hook->process == current->process) &&
+            (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
+        {
+            if ((!hook->thread || hook->thread == current) &&
+                (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
+            {
+                if (hook->proc && event >= hook->event_min && event <= hook->event_max)
+                {
+                    if (hook->flags & WINEVENT_INCONTEXT) return hook;
+
+                    /* only winevent hooks may be out of context */
+                    assert(hook->index + WH_MINHOOK == WH_WINEVENT);
+                    post_win_event( hook->owner, event, win, object_id, child_id,
+                                    hook->proc, hook->module, hook->module_size,
+                                    hook->handle );
+                }
+            }
+        }
     }
     if (global_hooks && table != global_hooks)  /* now search through the global table */
     {
-        hook = get_first_valid_hook( global_hooks, index );
+        hook = get_first_valid_hook( global_hooks, index, event, win, object_id, child_id );
     }
     return hook;
 }
@@ -265,17 +316,37 @@ void remove_thread_hooks( struct thread 
 /* set a window hook */
 DECL_HANDLER(set_hook)
 {
-    struct thread *thread;
+    struct process *process = NULL;
+    struct thread *thread = NULL;
     struct hook *hook;
     WCHAR *module;
     int global;
     size_t module_size = get_req_data_size();
 
-    if (!req->proc || req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+    if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
+
+    if (req->pid && !(process = get_process_from_id( req->pid ))) return;
+
+    if (req->tid)
+    {
+        if (!(thread = get_thread_from_id( req->tid )))
+        {
+            if (process) release_object( process );
+            return;
+        }
+        if (process && process != thread->process)
+        {
+            release_object( process );
+            release_object( thread );
+            set_error( STATUS_INVALID_PARAMETER );
+            return;
+        }
+    }
+
     if (req->id == WH_KEYBOARD_LL || req->id == WH_MOUSE_LL)
     {
         /* low-level hardware hooks are special: always global, but without a module */
@@ -285,11 +356,15 @@ DECL_HANDLER(set_hook)
     }
     else if (!req->tid)
     {
-        if (!module_size)
+        /* out of context hooks do not need a module handle */
+        if (!module_size && (req->flags & WINEVENT_INCONTEXT))
         {
+            if (process) release_object( process );
+            if (thread) release_object( thread );
             set_error( STATUS_INVALID_PARAMETER );
             return;
         }
+
         if (!(module = memdup( get_req_data(), module_size ))) return;
         thread = NULL;
         global = 1;
@@ -298,11 +373,15 @@ DECL_HANDLER(set_hook)
     {
         module = NULL;
         global = 0;
-        if (!(thread = get_thread_from_id( req->tid ))) return;
     }
 
     if ((hook = add_hook( thread, req->id - WH_MINHOOK, global )))
     {
+        hook->owner = (struct thread *)grab_object( current );
+        hook->process = process ? (struct process *)grab_object( process ) : NULL;
+        hook->event_min   = req->event_min;
+        hook->event_max   = req->event_max;
+        hook->flags       = req->flags;
         hook->proc        = req->proc;
         hook->unicode     = req->unicode;
         hook->module      = module;
@@ -311,6 +390,7 @@ DECL_HANDLER(set_hook)
     }
     else if (module) free( module );
 
+    if (process) release_object( process );
     if (thread) release_object( thread );
 }
 
@@ -324,13 +404,14 @@ DECL_HANDLER(remove_hook)
     {
         if (!(hook = get_user_object( req->handle, USER_HOOK )))
         {
-            set_win32_error( ERROR_INVALID_HOOK_HANDLE );
+            if (req->id == WH_WINEVENT) set_error( STATUS_INVALID_HANDLE );
+            else set_win32_error( ERROR_INVALID_HOOK_HANDLE );
             return;
         }
     }
     else
     {
-        if (!req->proc || req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+        if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT)
         {
             set_error( STATUS_INVALID_PARAMETER );
             return;
@@ -351,32 +432,31 @@ DECL_HANDLER(start_hook_chain)
     struct hook *hook;
     struct hook_table *table = get_queue_hooks( current );
 
-    if (req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+    if (req->id < WH_MINHOOK || req->id > WH_WINEVENT)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
 
-    if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK )))
+    if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event, req->window, req->object_id, req->child_id )))
     {
         /* try global table */
         if (!(table = global_hooks) ||
-            !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK )))
+            !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK, req->event, req->window, req->object_id, req->child_id )))
             return;  /* no hook set */
     }
 
-    if (hook->thread && hook->thread != current)  /* must run in other thread */
+    if ( hook->thread && hook->thread != current) /* must run in other thread */
     {
         reply->pid  = get_process_id( hook->thread->process );
         reply->tid  = get_thread_id( hook->thread );
-        reply->proc = 0;
     }
     else
     {
         reply->pid  = 0;
         reply->tid  = 0;
-        reply->proc = hook->proc;
     }
+    reply->proc    = hook->proc;
     reply->handle  = hook->handle;
     reply->unicode = hook->unicode;
     table->counts[hook->index]++;
@@ -390,7 +470,7 @@ DECL_HANDLER(finish_hook_chain)
     struct hook_table *table = get_queue_hooks( current );
     int index = req->id - WH_MINHOOK;
 
-    if (req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+    if (req->id < WH_MINHOOK || req->id > WH_WINEVENT)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
@@ -411,7 +491,7 @@ DECL_HANDLER(get_next_hook)
         set_error( STATUS_INVALID_HANDLE );
         return;
     }
-    if ((next = get_next_hook( hook )))
+    if ((next = get_next_hook( hook, req->event, req->window, req->object_id, req->child_id )))
     {
         reply->next = next->handle;
         reply->id   = next->index + WH_MINHOOK;
@@ -422,13 +502,12 @@ DECL_HANDLER(get_next_hook)
         {
             reply->pid  = get_process_id( next->thread->process );
             reply->tid  = get_thread_id( next->thread );
-            reply->proc = 0;
         }
         else
         {
             reply->pid  = 0;
             reply->tid  = 0;
-            reply->proc = next->proc;
         }
+        reply->proc = next->proc;
     }
 }
diff -up cvs/hq/wine/server/protocol.def wine/server/protocol.def
--- cvs/hq/wine/server/protocol.def	2005-01-19 14:21:26.000000000 +0800
+++ wine/server/protocol.def	2005-02-02 15:56:45.000000000 +0800
@@ -1519,7 +1519,8 @@ enum message_type
     MSG_CALLBACK_RESULT,/* result of a callback message */
     MSG_OTHER_PROCESS,  /* sent from other process, may include vararg data, always Unicode */
     MSG_POSTED,         /* posted message (from PostMessageW), always Unicode */
-    MSG_HARDWARE        /* hardware message */
+    MSG_HARDWARE,       /* hardware message */
+    MSG_WINEVENT        /* winevent message */
 };
 #define SEND_MSG_ABORT_IF_HUNG  0x01
 
@@ -1538,6 +1539,8 @@ enum message_type
     unsigned int    lparam;    /* parameters (result for MSG_CALLBACK_RESULT) */
     int             x;         /* x position */
     int             y;         /* y position */
+    user_handle_t   hook;      /* winevent hook handle */
+    void*           hook_proc; /* winevent hook proc address */
     unsigned int    time;      /* message time */
     unsigned int    info;      /* extra info (callback argument for MSG_CALLBACK_RESULT) */
     size_t          total;     /* total size of extra data */
@@ -2064,7 +2067,11 @@ enum message_type
 /* Set a window hook */
 @REQ(set_hook)
     int            id;             /* id of the hook */
+    process_id_t   pid;            /* id of process to set the hook into */
     thread_id_t    tid;            /* id of thread to set the hook into */
+    int            event_min;
+    int            event_max;
+    int            flags;
     void*          proc;           /* hook procedure */
     int            unicode;        /* is it a unicode hook? */
     VARARG(module,unicode_str);    /* module name */
@@ -2084,6 +2091,10 @@ enum message_type
 /* Start calling a hook chain */
 @REQ(start_hook_chain)
     int            id;             /* id of the hook */
+    int            event;          /* signalled event */
+    user_handle_t  window;         /* handle to the event window */
+    int            object_id;      /* object id for out of context winevent */
+    int            child_id;       /* child id for out of context winevent */
 @REPLY
     user_handle_t  handle;         /* handle to the next hook */
     process_id_t   pid;            /* process id for low-level keyboard/mouse hooks */
@@ -2103,6 +2114,10 @@ enum message_type
 /* Get the next hook to call */
 @REQ(get_next_hook)
     user_handle_t  handle;         /* handle to the current hook */
+    int            event;          /* signalled event */
+    user_handle_t  window;         /* handle to the event window */
+    int            object_id;      /* object id for out of context winevent */
+    int            child_id;       /* child id for out of context winevent */
 @REPLY
     user_handle_t  next;           /* handle to the next hook */
     int            id;             /* id of the next hook */
diff -up cvs/hq/wine/server/queue.c wine/server/queue.c
--- cvs/hq/wine/server/queue.c	2004-12-02 14:04:53.000000000 +0800
+++ wine/server/queue.c	2005-02-02 16:44:15.000000000 +0800
@@ -70,6 +70,8 @@ struct message
     int                    y;         /* y position */
     unsigned int           time;      /* message time */
     unsigned int           info;      /* extra info */
+    user_handle_t          hook;      /* winevent hook handle */
+    void                  *hook_proc; /* winevent hook proc address */
     void                  *data;      /* message data for sent messages */
     unsigned int           data_size; /* size of message data */
     struct message_result *result;    /* result in sender queue */
@@ -550,6 +552,8 @@ static void receive_message( struct msg_
     reply->y      = msg->y;
     reply->time   = msg->time;
     reply->info   = msg->info;
+    reply->hook   = msg->hook;
+    reply->hook_proc = msg->hook_proc;
 
     if (msg->data) set_reply_data_ptr( msg->data, msg->data_size );
 
@@ -1296,6 +1300,45 @@ void post_message( user_handle_t win, un
     release_object( thread );
 }
 
+/* post a win event */
+void post_win_event( struct thread *thread, unsigned int event,
+                     user_handle_t win, unsigned int object_id,
+                     unsigned int child_id, void *hook_proc,
+                     const WCHAR *module, size_t module_size,
+                     user_handle_t hook)
+{
+    struct message *msg;
+
+    if (thread->queue && (msg = mem_alloc( sizeof(*msg) )))
+    {
+        msg->type      = MSG_WINEVENT;
+        msg->win       = get_user_full_handle( win );
+        msg->msg       = event;
+        msg->wparam    = object_id;
+        msg->lparam    = child_id;
+        msg->time      = get_tick_count();
+        msg->x         = 0;
+        msg->y         = 0;
+        msg->info      = get_thread_id( current );
+        msg->result    = NULL;
+        msg->hook      = hook;
+        msg->hook_proc = hook_proc;
+
+        if ((msg->data = malloc( module_size )))
+        {
+            msg->data_size = module_size;
+            memcpy( msg->data, module, module_size );
+
+            if (debug_level)
+                fprintf( stderr, "post_win_event: tid %04x event %04x win %p object_id %d child_id %d\n",
+                         get_thread_id(thread), event, win, object_id, child_id );
+            append_message( &thread->queue->msg_list[SEND_MESSAGE], msg );
+            set_queue_bits( thread->queue, QS_SENDMESSAGE );
+        }
+        else
+            free( msg );
+    }
+}
 
 /* get the message queue of the current thread */
 DECL_HANDLER(get_msg_queue)
diff -up cvs/hq/wine/server/thread.c wine/server/thread.c
--- cvs/hq/wine/server/thread.c	2005-01-19 14:21:26.000000000 +0800
+++ wine/server/thread.c	2005-02-02 15:33:45.000000000 +0800
@@ -275,7 +275,7 @@ struct thread *get_thread_from_id( threa
     struct object *obj = get_ptid_entry( id );
 
     if (obj && obj->ops == &thread_ops) return (struct thread *)grab_object( obj );
-    set_error( STATUS_INVALID_PARAMETER );
+    set_win32_error( ERROR_INVALID_THREAD_ID );
     return NULL;
 }
 
diff -up cvs/hq/wine/server/trace.c wine/server/trace.c
--- cvs/hq/wine/server/trace.c	2005-01-31 08:54:07.000000000 +0800
+++ wine/server/trace.c	2005-02-02 16:04:25.000000000 +0800
@@ -1866,6 +1866,8 @@ static void dump_get_message_reply( cons
     fprintf( stderr, " lparam=%08x,", req->lparam );
     fprintf( stderr, " x=%d,", req->x );
     fprintf( stderr, " y=%d,", req->y );
+    fprintf( stderr, " hook=%p,", req->hook );
+    fprintf( stderr, " hook_proc=%p,", req->hook_proc );
     fprintf( stderr, " time=%08x,", req->time );
     fprintf( stderr, " info=%08x,", req->info );
     fprintf( stderr, " total=%d,", req->total );
@@ -2458,7 +2460,11 @@ static void dump_set_caret_info_reply( c
 static void dump_set_hook_request( const struct set_hook_request *req )
 {
     fprintf( stderr, " id=%d,", req->id );
+    fprintf( stderr, " pid=%04x,", req->pid );
     fprintf( stderr, " tid=%04x,", req->tid );
+    fprintf( stderr, " event_min=%d,", req->event_min );
+    fprintf( stderr, " event_max=%d,", req->event_max );
+    fprintf( stderr, " flags=%d,", req->flags );
     fprintf( stderr, " proc=%p,", req->proc );
     fprintf( stderr, " unicode=%d,", req->unicode );
     fprintf( stderr, " module=" );
@@ -2479,7 +2485,11 @@ static void dump_remove_hook_request( co
 
 static void dump_start_hook_chain_request( const struct start_hook_chain_request *req )
 {
-    fprintf( stderr, " id=%d", req->id );
+    fprintf( stderr, " id=%d,", req->id );
+    fprintf( stderr, " event=%d,", req->event );
+    fprintf( stderr, " window=%p,", req->window );
+    fprintf( stderr, " object_id=%d,", req->object_id );
+    fprintf( stderr, " child_id=%d", req->child_id );
 }
 
 static void dump_start_hook_chain_reply( const struct start_hook_chain_reply *req )
@@ -2500,7 +2510,11 @@ static void dump_finish_hook_chain_reque
 
 static void dump_get_next_hook_request( const struct get_next_hook_request *req )
 {
-    fprintf( stderr, " handle=%p", req->handle );
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " event=%d,", req->event );
+    fprintf( stderr, " window=%p,", req->window );
+    fprintf( stderr, " object_id=%d,", req->object_id );
+    fprintf( stderr, " child_id=%d", req->child_id );
 }
 
 static void dump_get_next_hook_reply( const struct get_next_hook_reply *req )
@@ -3212,6 +3226,7 @@ static const char *get_status_name( unsi
         NAME(WAS_LOCKED),
         NAME_WIN32(ERROR_INVALID_HOOK_HANDLE),
         NAME_WIN32(ERROR_INVALID_INDEX),
+        NAME_WIN32(ERROR_INVALID_THREAD_ID),
         NAME_WIN32(ERROR_NEGATIVE_SEEK),
         NAME_WIN32(ERROR_SEEK),
         { NULL, 0 }  /* terminator */
diff -up cvs/hq/wine/server/user.h wine/server/user.h
--- cvs/hq/wine/server/user.h	2004-10-28 09:58:18.000000000 +0900
+++ wine/server/user.h	2005-02-02 15:33:45.000000000 +0800
@@ -64,6 +64,11 @@ extern void queue_cleanup_window( struct
 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 );
+extern void post_win_event( struct thread *thread, unsigned int event,
+                            user_handle_t win, unsigned int object_id,
+                            unsigned int child_id, void *proc,
+                            const WCHAR *module, size_t module_size,
+                            user_handle_t handle );
 
 /* region functions */
 
diff -up cvs/hq/wine/windows/nonclient.c wine/windows/nonclient.c
--- cvs/hq/wine/windows/nonclient.c	2005-01-26 17:56:05.000000000 +0800
+++ wine/windows/nonclient.c	2005-02-02 15:33:45.000000000 +0800
@@ -1561,7 +1561,7 @@ LONG NC_HandleSysCommand( HWND hwnd, WPA
     case SC_ARRANGE:
     case SC_NEXTWINDOW:
     case SC_PREVWINDOW:
- 	FIXME("unimplemented!\n");
+ 	FIXME("unimplemented WM_SYSCOMMAND %04x!\n", wParam);
         break;
     }
     return 0;






More information about the wine-patches mailing list