[SHELL32] Rewrite the wrapping code for shell taskbar notifications.
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 4 Feb 2018 17:02:41 +0000 (18:02 +0100)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 4 Feb 2018 17:11:50 +0000 (18:11 +0100)
- Introduce the TRAYNOTIFYDATAW structure, as documented by Geoff
  Chappell in "WM_COPYDATA for Taskbar Interface", at
  http://www.geoffchappell.com/studies/windows/shell/shell32/api/shlnot/copydata.htm
  that is the data structure passed between shell32 and explorer for
  communicating shell notify icon information.

- In Shell_NotifyIcon(), correctly capture the (ANSI and) UNICODE
  structures provided by the caller, properly taking into account for
  the different NOTIFYICONDATA structure sizes existing out there.
  The different strings are now properly null-terminated (especially
  szTip if it needs to be truncated out), and the flags validated.

- Remove the now unneeded "SHELL_NotifyIcon()" helper function.

[EXPLORER] Use TRAYNOTIFYDATAW and adjust the callers.

base/shell/explorer/syspager.cpp
dll/win32/shell32/precomp.h
dll/win32/shell32/systray.cpp
sdk/include/reactos/undocshell.h

index 9ba0f2f..32d4b45 100644 (file)
 
 #include "precomp.h"
 
-// Data comes from shell32/systray.cpp -> TrayNotifyCDS_Dummy
-typedef struct _SYS_PAGER_COPY_DATA
-{
-    DWORD           cookie;
-    DWORD           notify_code;
-    NOTIFYICONDATA  nicon_data;
-} SYS_PAGER_COPY_DATA, *PSYS_PAGER_COPY_DATA;
-
 struct InternalIconData : NOTIFYICONDATA
 {
     // Must keep a separate copy since the original is unioned with uTimeout.
@@ -236,7 +228,7 @@ public:
         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
     END_COM_MAP()
 
-    BOOL NotifyIcon(DWORD notify_code, _In_ CONST NOTIFYICONDATA *iconData);
+    BOOL NotifyIcon(DWORD dwMessage, _In_ CONST NOTIFYICONDATA *iconData);
     void GetSize(IN BOOL IsHorizontal, IN PSIZE size);
 
     DECLARE_WND_CLASS_EX(szSysPagerWndClass, CS_DBLCLKS, COLOR_3DFACE)
@@ -454,22 +446,18 @@ UINT WINAPI CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam)
 
             TRACE("Pid %lu owns a notification icon and has stopped without deleting it. We'll cleanup on its behalf", Icon->ProcessId);
 
-            int len = FIELD_OFFSET(SYS_PAGER_COPY_DATA, nicon_data) + Icon->IconData.cbSize;
-            PSYS_PAGER_COPY_DATA pnotify_data = (PSYS_PAGER_COPY_DATA)new BYTE[len];
-            pnotify_data->cookie = 1;
-            pnotify_data->notify_code = NIM_DELETE;
-            memcpy(&pnotify_data->nicon_data, &Icon->IconData, Icon->IconData.cbSize);
+            TRAYNOTIFYDATAW tnid = {0};
+            tnid.dwSignature = NI_NOTIFY_SIG;
+            tnid.dwMessage   = NIM_DELETE;
+            CopyMemory(&tnid.nid, &Icon->IconData, Icon->IconData.cbSize);
 
             COPYDATASTRUCT data;
             data.dwData = 1;
-            data.cbData = len;
-            data.lpData = pnotify_data;
-
-            BOOL Success = FALSE;
-            ::SendMessage(This->m_hwndSysTray, WM_COPYDATA, (WPARAM)&Icon->IconData, (LPARAM)&data);
-
-            delete pnotify_data;
+            data.cbData = sizeof(tnid);
+            data.lpData = &tnid;
 
+            BOOL Success = ::SendMessage(This->m_hwndSysTray, WM_COPYDATA,
+                                         (WPARAM)&Icon->IconData, (LPARAM)&data);
             if (!Success)
             {
                 // If we failed to handle the delete message, forcibly remove it
@@ -1263,14 +1251,14 @@ LRESULT CSysPagerWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& b
     return TRUE;
 }
 
-BOOL CSysPagerWnd::NotifyIcon(DWORD notify_code, _In_ CONST NOTIFYICONDATA *iconData)
+BOOL CSysPagerWnd::NotifyIcon(DWORD dwMessage, _In_ CONST NOTIFYICONDATA *iconData)
 {
     BOOL ret = FALSE;
 
     int VisibleButtonCount = Toolbar.GetVisibleButtonCount();
 
-    TRACE("NotifyIcon received. Code=%d\n", notify_code);
-    switch (notify_code)
+    TRACE("NotifyIcon received. Code=%d\n", dwMessage);
+    switch (dwMessage)
     {
     case NIM_ADD:
         ret = Toolbar.AddButton(iconData);
@@ -1295,7 +1283,7 @@ BOOL CSysPagerWnd::NotifyIcon(DWORD notify_code, _In_ CONST NOTIFYICONDATA *icon
     case NIM_SETVERSION:
         ret = Toolbar.SwitchVersion(iconData);
     default:
-        TRACE("NotifyIcon received with unknown code %d.\n", notify_code);
+        TRACE("NotifyIcon received with unknown code %d.\n", dwMessage);
         return FALSE;
     }
 
@@ -1420,9 +1408,12 @@ LRESULT CSysPagerWnd::OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&
     PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;
     if (cpData->dwData == 1)
     {
-        PSYS_PAGER_COPY_DATA pData = (PSYS_PAGER_COPY_DATA)cpData->lpData;
-        return NotifyIcon(pData->notify_code, &pData->nicon_data);
+        /* A taskbar NotifyIcon notification */
+        PTRAYNOTIFYDATAW pData = (PTRAYNOTIFYDATAW)cpData->lpData;
+        if (pData->dwSignature == NI_NOTIFY_SIG)
+            return NotifyIcon(pData->dwMessage, &pData->nid);
     }
+    // TODO: Handle other types of taskbar notifications
 
     return FALSE;
 }
index 03166e3..f059e5b 100644 (file)
@@ -17,6 +17,7 @@
 #include <wincon.h>
 #include <commdlg.h>
 #include <ddeml.h>
+
 #include <shlwapi.h>
 #include <shlobj.h>
 #include <shobjidl.h>
 #include <shlguid_undoc.h>
 #include <shlobj_undoc.h>
 #include <shlwapi_undoc.h>
+
+#include <shellapi.h>
+#undef ShellExecute
 #include <undocshell.h>
+
 #include <browseui_undoc.h>
 
 #include <shellutils.h>
index aa07708..482abf0 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright 2004 Martin Fuchs
+ * Copyright 2018 Hermes Belusca-Maito
  *
  * Pass on icon notification messages to the systray implementation
  * in the currently running shell.
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
-/* copy data structure for tray notifications */
-typedef struct TrayNotifyCDS_Dummy {
-    DWORD    cookie;
-    DWORD    notify_code;
-    DWORD    nicon_data[1];    // placeholder for NOTIFYICONDATA structure
-} TrayNotifyCDS_Dummy;
-
-/* The only difference between Shell_NotifyIconA and Shell_NotifyIconW is the call to SendMessageA/W. */
-static BOOL SHELL_NotifyIcon(DWORD dwMessage, void* pnid, HWND nid_hwnd, DWORD nid_size, BOOL unicode)
-{
-    HWND hwnd;
-    COPYDATASTRUCT data;
-
-    BOOL ret = FALSE;
-    int len = FIELD_OFFSET(TrayNotifyCDS_Dummy, nicon_data) + nid_size;
-
-    TrayNotifyCDS_Dummy* pnotify_data = (TrayNotifyCDS_Dummy*) alloca(len);
-
-    pnotify_data->cookie = 1;
-    pnotify_data->notify_code = dwMessage;
-    memcpy(&pnotify_data->nicon_data, pnid, nid_size);
-
-    data.dwData = 1;
-    data.cbData = len;
-    data.lpData = pnotify_data;
-
-    for(hwnd = 0; (hwnd = FindWindowExW(0, hwnd, L"Shell_TrayWnd", NULL)); )
-        if ((unicode ? SendMessageW : SendMessageA)(hwnd, WM_COPYDATA, (WPARAM)nid_hwnd, (LPARAM)&data))
-            ret = TRUE;
-
-    return ret;
-}
-
-
 /*************************************************************************
- * Shell_NotifyIcon            [SHELL32.296]
+ * Shell_NotifyIcon             [SHELL32.296]
  * Shell_NotifyIconA            [SHELL32.297]
  */
 BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid)
 {
     NOTIFYICONDATAW nidW;
-    DWORD cbSize;
-
-    /* Validate the cbSize as Windows XP does */
-    if (pnid->cbSize != NOTIFYICONDATAA_V1_SIZE &&
-        pnid->cbSize != NOTIFYICONDATAA_V2_SIZE &&
-        pnid->cbSize != sizeof(NOTIFYICONDATAA))
-    {
-        WARN("Invalid cbSize (%d) - using only Win95 fields (size=%d)\n",
-            pnid->cbSize, NOTIFYICONDATAA_V1_SIZE);
-        cbSize = NOTIFYICONDATAA_V1_SIZE;
-    }
-    else
-        cbSize = pnid->cbSize;
+    DWORD cbSize, dwValidFlags;
 
+    /* Initialize and capture the basic data fields */
     ZeroMemory(&nidW, sizeof(nidW));
-    nidW.cbSize = sizeof(nidW);
+    nidW.cbSize = sizeof(nidW); // Use a default size for the moment
     nidW.hWnd   = pnid->hWnd;
     nidW.uID    = pnid->uID;
     nidW.uFlags = pnid->uFlags;
     nidW.uCallbackMessage = pnid->uCallbackMessage;
     nidW.hIcon  = pnid->hIcon;
 
-    /* szTip */
-    if (pnid->uFlags & NIF_TIP)
-        MultiByteToWideChar(CP_ACP, 0, pnid->szTip, -1, nidW.szTip, _countof(nidW.szTip));
-
-    if (cbSize >= NOTIFYICONDATAA_V2_SIZE)
+    /* Validate the structure size and the flags */
+    cbSize = pnid->cbSize;
+    dwValidFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
+    if (cbSize == sizeof(NOTIFYICONDATAA))
+    {
+        nidW.cbSize = sizeof(nidW);
+        dwValidFlags |= NIF_STATE | NIF_INFO | NIF_GUID /* | NIF_REALTIME | NIF_SHOWTIP */;
+    }
+    else if (cbSize == NOTIFYICONDATAA_V3_SIZE)
+    {
+        nidW.cbSize = NOTIFYICONDATAW_V3_SIZE;
+        dwValidFlags |= NIF_STATE | NIF_INFO | NIF_GUID;
+    }
+    else if (cbSize == NOTIFYICONDATAA_V2_SIZE)
+    {
+        nidW.cbSize = NOTIFYICONDATAW_V2_SIZE;
+        dwValidFlags |= NIF_STATE | NIF_INFO;
+    }
+    else // if cbSize == NOTIFYICONDATAA_V1_SIZE or something else
     {
-        nidW.dwState      = pnid->dwState;
-        nidW.dwStateMask  = pnid->dwStateMask;
+        if (cbSize != NOTIFYICONDATAA_V1_SIZE)
+        {
+            WARN("Invalid cbSize (%d) - using only Win95 fields (size=%d)\n",
+                cbSize, NOTIFYICONDATAA_V1_SIZE);
+            cbSize = NOTIFYICONDATAA_V1_SIZE;
+        }
+        nidW.cbSize = NOTIFYICONDATAW_V1_SIZE;
+    }
+    nidW.uFlags &= dwValidFlags;
+
+    /* Capture the other data fields */
 
-        /* szInfo, szInfoTitle */
-        if (pnid->uFlags & NIF_INFO)
+    if (nidW.uFlags & NIF_TIP)
+    {
+        /*
+         * Depending on the size of the NOTIFYICONDATA structure
+         * we should convert part of, or all the szTip string.
+         */
+        if (cbSize <= NOTIFYICONDATAA_V1_SIZE)
+        {
+#define NIDV1_TIP_SIZE_A  (NOTIFYICONDATAA_V1_SIZE - FIELD_OFFSET(NOTIFYICONDATAA, szTip))/sizeof(CHAR)
+            MultiByteToWideChar(CP_ACP, 0, pnid->szTip, NIDV1_TIP_SIZE_A,
+                                nidW.szTip, _countof(nidW.szTip));
+            /* Truncate the string */
+            nidW.szTip[NIDV1_TIP_SIZE_A - 1] = 0;
+#undef NIDV1_TIP_SIZE_A
+        }
+        else
         {
-            MultiByteToWideChar(CP_ACP, 0, pnid->szInfo, -1,  nidW.szInfo, _countof(nidW.szInfo));
-            MultiByteToWideChar(CP_ACP, 0, pnid->szInfoTitle, -1, nidW.szInfoTitle, _countof(nidW.szInfoTitle));
+            MultiByteToWideChar(CP_ACP, 0, pnid->szTip, -1,
+                                nidW.szTip, _countof(nidW.szTip));
         }
+    }
 
-        nidW.uTimeout = pnid->uTimeout;
+    if (cbSize >= NOTIFYICONDATAA_V2_SIZE)
+    {
+        nidW.dwState     = pnid->dwState;
+        nidW.dwStateMask = pnid->dwStateMask;
+        nidW.uTimeout    = pnid->uTimeout;
         nidW.dwInfoFlags = pnid->dwInfoFlags;
+
+        if (nidW.uFlags & NIF_INFO)
+        {
+            MultiByteToWideChar(CP_ACP, 0, pnid->szInfo, -1,
+                                nidW.szInfo, _countof(nidW.szInfo));
+            MultiByteToWideChar(CP_ACP, 0, pnid->szInfoTitle, -1,
+                                nidW.szInfoTitle, _countof(nidW.szInfoTitle));
+        }
     }
 
+    if ((cbSize >= NOTIFYICONDATAA_V3_SIZE) && (nidW.uFlags & NIF_GUID))
+        nidW.guidItem = pnid->guidItem;
+
     if (cbSize >= sizeof(NOTIFYICONDATAA))
         nidW.hBalloonIcon = pnid->hBalloonIcon;
+
+    /* Call the unicode function */
     return Shell_NotifyIconW(dwMessage, &nidW);
 }
 
@@ -116,19 +127,81 @@ BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid)
  */
 BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW pnid)
 {
-    DWORD cbSize;
+    BOOL ret = FALSE;
+    HWND hShellTrayWnd;
+    DWORD cbSize, dwValidFlags;
+    TRAYNOTIFYDATAW tnid;
+    COPYDATASTRUCT data;
 
-    /* Validate the cbSize so that WM_COPYDATA doesn't crash the application */
-    if (pnid->cbSize != NOTIFYICONDATAW_V1_SIZE &&
-        pnid->cbSize != NOTIFYICONDATAW_V2_SIZE &&
-        pnid->cbSize != sizeof(NOTIFYICONDATAW))
+    /* Find a handle to the shell tray window */
+    hShellTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
+    if (!hShellTrayWnd)
+        return FALSE; // None found, bail out
+
+    /* Validate the structure size and the flags */
+    cbSize = pnid->cbSize;
+    dwValidFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
+    if (cbSize == sizeof(NOTIFYICONDATAW))
+    {
+        dwValidFlags |= NIF_STATE | NIF_INFO | NIF_GUID /* | NIF_REALTIME | NIF_SHOWTIP */;
+    }
+    else if (cbSize == NOTIFYICONDATAW_V3_SIZE)
+    {
+        dwValidFlags |= NIF_STATE | NIF_INFO | NIF_GUID;
+    }
+    else if (cbSize == NOTIFYICONDATAW_V2_SIZE)
     {
-        WARN("Invalid cbSize (%d) - using only Win95 fields (size=%d)\n",
-            pnid->cbSize, NOTIFYICONDATAW_V1_SIZE);
-        cbSize = NOTIFYICONDATAW_V1_SIZE;
+        dwValidFlags |= NIF_STATE | NIF_INFO;
     }
-    else
-        cbSize = pnid->cbSize;
+    else // if cbSize == NOTIFYICONDATAW_V1_SIZE or something else
+    {
+        if (cbSize != NOTIFYICONDATAW_V1_SIZE)
+        {
+            WARN("Invalid cbSize (%d) - using only Win95 fields (size=%d)\n",
+                cbSize, NOTIFYICONDATAW_V1_SIZE);
+            cbSize = NOTIFYICONDATAW_V1_SIZE;
+        }
+    }
+
+    /* Build the data structure */
+    ZeroMemory(&tnid, sizeof(tnid));
+    tnid.dwSignature = NI_NOTIFY_SIG;
+    tnid.dwMessage   = dwMessage;
+
+    /* Copy only the needed data, everything else is zeroed out */
+    CopyMemory(&tnid.nid, pnid, cbSize);
+    /* Adjust the size (the NOTIFYICONDATA structure is the full-fledged one) and the flags */
+    tnid.nid.cbSize = sizeof(tnid.nid);
+    tnid.nid.uFlags &= dwValidFlags;
+
+    /* Be sure the szTip member (that could be cut-off) is correctly NULL-terminated */
+    if (tnid.nid.uFlags & NIF_TIP)
+    {
+        if (cbSize <= NOTIFYICONDATAW_V1_SIZE)
+        {
+#define NIDV1_TIP_SIZE_W  (NOTIFYICONDATAW_V1_SIZE - FIELD_OFFSET(NOTIFYICONDATAW, szTip))/sizeof(WCHAR)
+            tnid.nid.szTip[NIDV1_TIP_SIZE_W - 1] = 0;
+#undef NIDV1_TIP_SIZE_W
+        }
+        else
+        {
+            tnid.nid.szTip[_countof(tnid.nid.szTip) - 1] = 0;
+        }
+    }
+
+    /* Be sure the info strings are correctly NULL-terminated */
+    if (tnid.nid.uFlags & NIF_INFO)
+    {
+        tnid.nid.szInfo[_countof(tnid.nid.szInfo) - 1] = 0;
+        tnid.nid.szInfoTitle[_countof(tnid.nid.szInfoTitle) - 1] = 0;
+    }
+
+    /* Send the data */
+    data.dwData = 1;
+    data.cbData = sizeof(tnid);
+    data.lpData = &tnid;
+    if (SendMessageW(hShellTrayWnd, WM_COPYDATA, (WPARAM)pnid->hWnd, (LPARAM)&data))
+        ret = TRUE;
 
-    return SHELL_NotifyIcon(dwMessage, pnid, pnid->hWnd, cbSize, TRUE);
+    return ret;
 }
index 4a68feb..e74f201 100644 (file)
@@ -30,10 +30,29 @@ extern "C" {
 #define DBIMF_NOMARGINS         0x2000
 #endif  // NTDDI_LONGHORN
 
+#if defined (_SHELLAPI_H) || defined (_INC_SHELLAPI)
+
 /****************************************************************************
- * Taskbar WM_COMMAND identifiers
+ * Taskbar interface WM_COPYDATA structures
+ * See http://www.geoffchappell.com/studies/windows/shell/shell32/api/shlnot/copydata.htm
  */
+/* Data structure for Shell_NotifyIcon messages */
+typedef struct _TRAYNOTIFYDATAW
+{
+    DWORD dwSignature;
+    DWORD dwMessage;
+    NOTIFYICONDATAW nid; // Always use the latest NOTIFYICONDATAW structure version.
+} TRAYNOTIFYDATAW, *PTRAYNOTIFYDATAW;
+// Note: One could also introduce TRAYNOTIFYDATAA
+
+#define NI_NOTIFY_SIG 0x34753423 /* TRAYNOTIFYDATA */
+
+#endif /* defined (_SHELLAPI_H) || defined (_INC_SHELLAPI) */
+
 
+/****************************************************************************
+ * Taskbar WM_COMMAND identifiers
+ */
 #define TWM_DOEXITWINDOWS (WM_USER + 342)
 #define TWM_CYCLEFOCUS (WM_USER + 348)
 
@@ -79,8 +98,8 @@ BOOL WINAPI StrRetToStrNW(LPWSTR,DWORD,LPSTRRET,const ITEMIDLIST*);
 
 
 /****************************************************************************
-* SHChangeNotifyRegister API
-*/
+ * SHChangeNotifyRegister API
+ */
 #define SHCNRF_InterruptLevel       0x0001
 #define SHCNRF_ShellLevel           0x0002
 #define SHCNRF_RecursiveInterrupt   0x1000  /* Must be combined with SHCNRF_InterruptLevel */
@@ -580,7 +599,7 @@ BOOL WINAPI GUIDFromStringW(
     _In_   PCWSTR psz,
     _Out_  LPGUID pguid
     );
-    
+
 static inline ULONG
 Win32DbgPrint(const char *filename, int line, const char *lpFormat, ...)
 {
@@ -838,7 +857,7 @@ typedef struct tagSHELL_LINK_INFOW
 
 /*****************************************************************************
  * SHELL_LINK_INFO_VOLUME_IDA/W
- * If cbVolumeLabelOffset != 0x00000014 (should be 0x00000010) then use 
+ * If cbVolumeLabelOffset != 0x00000014 (should be 0x00000010) then use
  * SHELL_LINK_INFO_VOLUME_IDA
  * If cbVolumeLabelOffset == 0x00000014 then use SHELL_LINK_INFO_VOLUME_IDW
  */
@@ -958,7 +977,7 @@ typedef struct tagEXP_VISTA_ID_LIST
 {
     /* .cbSize >= 0x0000000a, .dwSignature = 0xa000000c */
     DATABLOCK_HEADER dbh;
-    /* Specifies an alternate IDList that can be used instead 
+    /* Specifies an alternate IDList that can be used instead
        of the "normal" IDList (SLDF_HAS_ID_LIST) */
     /* LPITEMIDLIST pIDList; (variable) */
 } EXP_VISTA_ID_LIST, *LPEXP_VISTA_ID_LIST;