tinus <o112w8r02@sneakemail.com>:
Implement RegisterShellHookWindow
Modified: trunk/reactos/include/win32k/ntuser.h
Modified: trunk/reactos/lib/user32/windows/hook.c
Modified: trunk/reactos/ntoskrnl/include/internal/ex.h
Modified: trunk/reactos/subsys/win32k/include/desktop.h
Modified: trunk/reactos/subsys/win32k/include/window.h
Modified: trunk/reactos/subsys/win32k/ntuser/class.c
Modified: trunk/reactos/subsys/win32k/ntuser/desktop.c
Modified: trunk/reactos/subsys/win32k/ntuser/focus.c
Modified: trunk/reactos/subsys/win32k/ntuser/stubs.c
Modified: trunk/reactos/subsys/win32k/ntuser/window.c

Modified: trunk/reactos/include/win32k/ntuser.h
--- trunk/reactos/include/win32k/ntuser.h	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/include/win32k/ntuser.h	2005-03-13 23:08:51 UTC (rev 14041)
@@ -103,6 +103,11 @@
   DWORD BufferSize,
   DWORD *Count);
 
+enum {
+	HWND_ROUTINE_REGISTERSHELLHOOKWINDOW,
+	HWND_ROUTINE_DEREGISTERSHELLHOOKWINDOW
+};
+
 DWORD
 STDCALL
 NtUserCallHwnd(

Modified: trunk/reactos/lib/user32/windows/hook.c
--- trunk/reactos/lib/user32/windows/hook.c	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/lib/user32/windows/hook.c	2005-03-13 23:08:51 UTC (rev 14041)
@@ -156,8 +156,7 @@
 STDCALL
 DeregisterShellHookWindow(HWND hWnd)
 {
-  UNIMPLEMENTED;
-  return FALSE;
+  return NtUserCallHwnd(HWND_ROUTINE_DEREGISTERSHELLHOOKWINDOW, (DWORD)hWnd);
 }
 
 /*
@@ -167,8 +166,7 @@
 STDCALL
 RegisterShellHookWindow(HWND hWnd)
 {
-  UNIMPLEMENTED;
-  return FALSE;
+  return NtUserCallHwnd(HWND_ROUTINE_REGISTERSHELLHOOKWINDOW, (DWORD)hWnd);
 }
 
 /*

Modified: trunk/reactos/ntoskrnl/include/internal/ex.h
--- trunk/reactos/ntoskrnl/include/internal/ex.h	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/ntoskrnl/include/internal/ex.h	2005-03-13 23:08:51 UTC (rev 14041)
@@ -68,6 +68,8 @@
   HANDLE PrevActiveWindow;
   /* Thread blocking input */
   PVOID BlockInputThread;
+
+  LIST_ENTRY ShellHookWindows;
 } DESKTOP_OBJECT, *PDESKTOP_OBJECT;
 
 

Modified: trunk/reactos/subsys/win32k/include/desktop.h
--- trunk/reactos/subsys/win32k/include/desktop.h	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/subsys/win32k/include/desktop.h	2005-03-13 23:08:51 UTC (rev 14041)
@@ -14,6 +14,12 @@
 extern HDC ScreenDeviceContext;
 extern BOOL g_PaintDesktopVersion;
 
+typedef struct _SHELL_HOOK_WINDOW
+{
+  LIST_ENTRY ListEntry;
+  HWND hWnd;
+} SHELL_HOOK_WINDOW, *PSHELL_HOOK_WINDOW;
+
 NTSTATUS FASTCALL
 InitDesktopImpl(VOID);
 
@@ -79,6 +85,11 @@
 BOOL FASTCALL
 IntDesktopUpdatePerUserSettings(BOOL bEnable);
 
+BOOL IntRegisterShellHookWindow(HWND hWnd);
+BOOL IntDeRegisterShellHookWindow(HWND hWnd);
+
+VOID IntShellHookNotify(WPARAM Message, LPARAM lParam);
+
 #define IntIsActiveDesktop(Desktop) \
   ((Desktop)->WindowStation->ActiveDesktop == (Desktop))
 

Modified: trunk/reactos/subsys/win32k/include/window.h
--- trunk/reactos/subsys/win32k/include/window.h	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/subsys/win32k/include/window.h	2005-03-13 23:08:51 UTC (rev 14041)
@@ -200,6 +200,9 @@
 IntGetParent(PWINDOW_OBJECT Wnd);
 
 PWINDOW_OBJECT FASTCALL
+IntGetOwner(PWINDOW_OBJECT Wnd);
+
+PWINDOW_OBJECT FASTCALL
 IntGetParentObject(PWINDOW_OBJECT Wnd);
 
 INT FASTCALL

Modified: trunk/reactos/subsys/win32k/ntuser/class.c
--- trunk/reactos/subsys/win32k/ntuser/class.c	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/subsys/win32k/ntuser/class.c	2005-03-13 23:08:51 UTC (rev 14041)
@@ -534,6 +534,8 @@
 void FASTCALL
 IntSetClassLong(PWINDOW_OBJECT WindowObject, ULONG Offset, LONG dwNewLong, BOOL Ansi)
 {
+  PWINDOW_OBJECT Parent, Owner;
+
   if ((int)Offset >= 0)
     {
       DPRINT("SetClassLong(%x, %d, %x)\n", WindowObject->Self, Offset, dwNewLong);
@@ -562,6 +564,25 @@
       break;
     case GCL_HICON:
       WindowObject->Class->hIcon = (HICON)dwNewLong;
+      Owner = IntGetOwner(WindowObject);
+      Parent = IntGetParent(WindowObject);
+
+      if ((!Owner) && (!Parent))
+        {
+          IntShellHookNotify(HSHELL_REDRAW, (LPARAM) WindowObject->Self);
+        }
+
+      if (Parent)
+        {
+          IntReleaseWindowObject(Parent);
+        }
+
+      if (Owner)
+        {
+          IntReleaseWindowObject(Owner);
+        }
+
+
       break;
     case GCL_HICONSM:
       WindowObject->Class->hIconSm = (HICON)dwNewLong;

Modified: trunk/reactos/subsys/win32k/ntuser/desktop.c
--- trunk/reactos/subsys/win32k/ntuser/desktop.c	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/subsys/win32k/ntuser/desktop.c	2005-03-13 23:08:51 UTC (rev 14041)
@@ -92,6 +92,7 @@
   DPRINT("Creating desktop (0x%X)  Name (%wZ)\n", Desktop, &UnicodeString);
 
   KeInitializeSpinLock(&Desktop->Lock);
+  InitializeListHead(&Desktop->ShellHookWindows);
 
   Desktop->WindowStation = (PWINSTATION_OBJECT)Parent;
 
@@ -541,6 +542,147 @@
 }
 
 /*
+ * Send the Message to the windows registered for ShellHook
+ * notifications. The lParam contents depend on the Message. See
+ * MSDN for more details (RegisterShellHookWindow)
+ */
+VOID IntShellHookNotify(WPARAM Message, LPARAM lParam)
+{
+  PDESKTOP_OBJECT Desktop = IntGetActiveDesktop();
+  PLIST_ENTRY Entry, Entry2;
+  PSHELL_HOOK_WINDOW Current;
+  KIRQL OldLevel;
+
+  static UINT MsgType = 0;
+
+  if (!MsgType) {
+
+    /* Too bad, this doesn't work.*/
+#if 0
+    UNICODE_STRING Str;
+    RtlInitUnicodeString(&Str, L"SHELLHOOK");
+    MsgType = NtUserRegisterWindowMessage(&Str);
+#endif
+    MsgType = IntAddAtom(L"SHELLHOOK");
+
+    DPRINT("MsgType = %x\n", MsgType);
+    if (!MsgType)
+      DPRINT1("LastError: %x\n", GetLastNtError());
+  }
+
+  if (!Desktop) {
+    DPRINT1("IntShellHookNotify: No desktop!\n");
+    return;
+  }
+
+  /* We have to do some tricks because the list could change
+   * between calls, and we can't keep the lock during the call
+   */
+
+  KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
+  Entry = Desktop->ShellHookWindows.Flink;
+  while (Entry != &Desktop->ShellHookWindows) {
+    Current = CONTAINING_RECORD(Entry, SHELL_HOOK_WINDOW, ListEntry);
+    KeReleaseSpinLock(&Desktop->Lock, OldLevel);
+
+    DPRINT("Sending notify\n");
+    IntPostOrSendMessage(Current->hWnd,
+                         MsgType,
+                         Message,
+                         lParam);
+
+    /* Loop again to find the window we were sending to. If it doesn't
+     * exist anymore, we just stop. This could leave an infinite loop
+     * if a window is removed and readded to the list. That's quite
+     * unlikely though.
+     */
+
+    KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
+    Entry2 = Desktop->ShellHookWindows.Flink;
+    while (Entry2 != Entry &&
+           Entry2 != &Desktop->ShellHookWindows) {
+      Entry2 = Entry2->Flink;
+    }
+
+    if (Entry2 == Entry)
+      Entry = Entry->Flink;
+    else
+      break;
+  }
+  KeReleaseSpinLock(&Desktop->Lock, OldLevel);
+}
+ 
+/*
+ * Add the window to the ShellHookWindows list. The windows
+ * on that list get notifications that are important to shell
+ * type applications.
+ *
+ * TODO: Validate the window? I'm not sure if sending these messages to
+ * an unsuspecting application that is not your own is a nice thing to do.
+ */
+BOOL IntRegisterShellHookWindow(HWND hWnd)
+{
+  PDESKTOP_OBJECT Desktop = PsGetWin32Thread()->Desktop;
+  PSHELL_HOOK_WINDOW Entry;
+  KIRQL OldLevel;
+
+  DPRINT("IntRegisterShellHookWindow\n");
+
+  /* First deregister the window, so we can be sure it's never twice in the
+   * list.
+   */
+  IntDeRegisterShellHookWindow(hWnd);
+
+  Entry = ExAllocatePoolWithTag(NonPagedPool,
+                                sizeof(SHELL_HOOK_WINDOW),
+                                TAG_WINSTA);
+  /* We have to walk this structure with while holding a spinlock, so we
+   * need NonPagedPool */
+
+  if (!Entry)
+    return FALSE;
+
+  Entry->hWnd = hWnd;
+
+  KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
+  InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
+  KeReleaseSpinLock(&Desktop->Lock, OldLevel);
+
+  return TRUE;
+}
+
+/*
+ * Remove the window from the ShellHookWindows list. The windows
+ * on that list get notifications that are important to shell
+ * type applications.
+ */
+BOOL IntDeRegisterShellHookWindow(HWND hWnd)
+{
+  PDESKTOP_OBJECT Desktop = PsGetWin32Thread()->Desktop;
+  PLIST_ENTRY Entry;
+  PSHELL_HOOK_WINDOW Current;
+  KIRQL OldLevel;
+
+  KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
+
+  Entry = Desktop->ShellHookWindows.Flink;
+  while (Entry != &Desktop->ShellHookWindows) {
+    Current = CONTAINING_RECORD(Entry, SHELL_HOOK_WINDOW, ListEntry);
+    if (Current->hWnd == hWnd) {
+      RemoveEntryList(Entry);
+      KeReleaseSpinLock(&Desktop->Lock, OldLevel);
+      ExFreePool(Entry);
+      return TRUE;
+    }
+    Entry = Entry->Flink;
+  }
+
+  KeReleaseSpinLock(&Desktop->Lock, OldLevel);
+
+  return FALSE;
+}
+
+/*
  * NtUserCreateDesktop
  *
  * Creates a new desktop.

Modified: trunk/reactos/subsys/win32k/ntuser/focus.c
--- trunk/reactos/subsys/win32k/ntuser/focus.c	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/subsys/win32k/ntuser/focus.c	2005-03-13 23:08:51 UTC (rev 14041)
@@ -62,6 +62,8 @@
 VOID FASTCALL
 IntSendActivateMessages(HWND hWndPrev, HWND hWnd, BOOL MouseActivate)
 {
+   PWINDOW_OBJECT Window, Owner, Parent;
+
    if (hWnd)
    {
       /* Send palette messages */
@@ -75,6 +77,22 @@
          WinPosSetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
             SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
 
+      Window = IntGetWindowObject(hWnd);
+      if (Window) {
+        Owner = IntGetOwner(Window);
+        if (!Owner) {
+          Parent = IntGetParent(Window);
+          if (!Parent)
+            IntShellHookNotify(HSHELL_WINDOWACTIVATED, (LPARAM) hWnd);
+          else
+            IntReleaseWindowObject(Parent);
+        } else {
+          IntReleaseWindowObject(Owner);
+        }
+
+        IntReleaseWindowObject(Window);
+      }
+
       /* FIXME: IntIsWindow */
 
       IntPostOrSendMessage(hWnd, WM_NCACTIVATE, (WPARAM)(hWnd == NtUserGetForegroundWindow()), 0);

Modified: trunk/reactos/subsys/win32k/ntuser/stubs.c
--- trunk/reactos/subsys/win32k/ntuser/stubs.c	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/subsys/win32k/ntuser/stubs.c	2005-03-13 23:08:51 UTC (rev 14041)
@@ -59,6 +59,17 @@
   DWORD Unknown0,
   DWORD Unknown1)
 {
+  switch (Unknown0) {
+    case HWND_ROUTINE_REGISTERSHELLHOOKWINDOW:
+      if (IntIsWindow((HWND) Unknown1))
+        return IntRegisterShellHookWindow((HWND) Unknown1);
+      return FALSE;
+      break;
+    case HWND_ROUTINE_DEREGISTERSHELLHOOKWINDOW:
+      if (IntIsWindow((HWND) Unknown1))
+        return IntDeRegisterShellHookWindow((HWND) Unknown1);
+      return FALSE;
+  }
   UNIMPLEMENTED
 
   return 0;

Modified: trunk/reactos/subsys/win32k/ntuser/window.c
--- trunk/reactos/subsys/win32k/ntuser/window.c	2005-03-13 23:03:31 UTC (rev 14040)
+++ trunk/reactos/subsys/win32k/ntuser/window.c	2005-03-13 23:08:51 UTC (rev 14041)
@@ -147,7 +147,18 @@
   return NULL;
 }
 
+PWINDOW_OBJECT FASTCALL
+IntGetOwner(PWINDOW_OBJECT Wnd)
+{
+  HWND hWnd;
 
+  IntLockRelatives(Wnd);
+  hWnd = Wnd->Owner;
+  IntUnLockRelatives(Wnd);
+
+  return IntGetWindowObject(hWnd);
+}
+
 PWINDOW_OBJECT FASTCALL
 IntGetParentObject(PWINDOW_OBJECT Wnd)
 {
@@ -205,6 +216,8 @@
  */
 static void IntSendDestroyMsg(HWND Wnd)
 {
+
+  PWINDOW_OBJECT Window, Owner, Parent;
 #if 0 /* FIXME */
   GUITHREADINFO info;
 
@@ -217,9 +230,28 @@
     }
 #endif
 
+  Window = IntGetWindowObject(Wnd);
+  if (Window) {
+    Owner = IntGetOwner(Window);
+    if (!Owner) {
+      Parent = IntGetParent(Window);
+      if (!Parent) 
+        IntShellHookNotify(HSHELL_WINDOWDESTROYED, (LPARAM) Wnd);
+      else
+        IntReleaseWindowObject(Parent);
+    } else {
+      IntReleaseWindowObject(Owner);
+    }
+
+    IntReleaseWindowObject(Window);
+  }
+
+  /* The window could already be destroyed here */
+
   /*
    * Send the WM_DESTROY to the window.
    */
+
   IntSendMessage(Wnd, WM_DESTROY, 0, 0);
 
   /*
@@ -284,6 +316,8 @@
   IntUnLockThreadWindows(Window->OwnerThread->Tcb.Win32Thread);
   
   BelongsToThreadData = IntWndBelongsToThread(Window, ThreadData);
+
+  IntDeRegisterShellHookWindow(Window->Self);
   
   if(SendMessages)
   {
@@ -1417,6 +1451,8 @@
   BOOL MenuChanged;
   BOOL ClassFound;
 
+  BOOL HasOwner;
+
   ParentWindowHandle = PsGetWin32Thread()->Desktop->DesktopWindow;
   OwnerWindowHandle = NULL;
 
@@ -1542,9 +1578,11 @@
   {
     WindowObject->Owner = OwnerWindowHandle;
     IntReleaseWindowObject(OwnerWindow);
+    HasOwner = TRUE;
+  } else {
+    WindowObject->Owner = NULL;
+    HasOwner = FALSE;
   }
-  else
-    WindowObject->Owner = NULL;
   WindowObject->UserData = 0;
   if ((((DWORD)ClassObject->lpfnWndProcA & 0xFFFF0000) != 0xFFFF0000)
       && (((DWORD)ClassObject->lpfnWndProcW & 0xFFFF0000) != 0xFFFF0000)) 
@@ -1969,6 +2007,13 @@
                      (LPARAM)WindowObject->Self);
     }
 
+  if ((!hWndParent) && (!HasOwner)) {
+      DPRINT("Sending CREATED notify\n");
+      IntShellHookNotify(HSHELL_WINDOWCREATED, (LPARAM)Handle);
+  } else {
+      DPRINT("Not sending CREATED notify, %x %d\n", ParentWindow, HasOwner);
+  }
+
   if (NULL != ParentWindow)
     {
       IntReleaseWindowObject(ParentWindow);
@@ -4060,7 +4105,7 @@
 BOOL STDCALL
 NtUserDefSetText(HWND WindowHandle, PUNICODE_STRING WindowText)
 {
-  PWINDOW_OBJECT WindowObject;
+  PWINDOW_OBJECT WindowObject, Parent, Owner;
   UNICODE_STRING SafeText;
   NTSTATUS Status;
   
@@ -4090,6 +4135,26 @@
   RtlFreeUnicodeString(&WindowObject->WindowName);
   
   WindowObject->WindowName = SafeText;
+
+  /* Send shell notifications */
+
+  Owner = IntGetOwner(WindowObject);
+  Parent = IntGetParent(WindowObject);
+
+  if ((!Owner) && (!Parent))
+  {
+    IntShellHookNotify(HSHELL_REDRAW, (LPARAM) WindowHandle);
+  }
+
+  if (Owner)
+  {
+    IntReleaseWindowObject(Owner);
+  }
+
+  if (Parent)
+  {
+    IntReleaseWindowObject(Parent);
+  }
   
   IntReleaseWindowObject(WindowObject);
   return TRUE;