[SHELL32] Rewrite the wrapping code for shell taskbar notifications.
[reactos.git] / dll / win32 / shell32 / systray.cpp
index 6fc578f..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)
+/*************************************************************************
+ * Shell_NotifyIcon             [SHELL32.296]
+ * Shell_NotifyIconA            [SHELL32.297]
+ */
+BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid)
 {
-    HWND hwnd;
-    COPYDATASTRUCT data;
+    NOTIFYICONDATAW nidW;
+    DWORD cbSize, dwValidFlags;
+
+    /* Initialize and capture the basic data fields */
+    ZeroMemory(&nidW, 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;
+
+    /* 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
+    {
+        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;
 
-    BOOL ret = FALSE;
-    int len = FIELD_OFFSET(TrayNotifyCDS_Dummy, nicon_data) + nid_size;
+    /* Capture the other data fields */
 
-    TrayNotifyCDS_Dummy* pnotify_data = (TrayNotifyCDS_Dummy*) alloca(len);
+    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->szTip, -1,
+                                nidW.szTip, _countof(nidW.szTip));
+        }
+    }
 
-    pnotify_data->cookie = 1;
-    pnotify_data->notify_code = dwMessage;
-    memcpy(&pnotify_data->nicon_data, pnid, nid_size);
+    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));
+        }
+    }
 
-    data.dwData = 1;
-    data.cbData = len;
-    data.lpData = pnotify_data;
+    if ((cbSize >= NOTIFYICONDATAA_V3_SIZE) && (nidW.uFlags & NIF_GUID))
+        nidW.guidItem = pnid->guidItem;
 
-    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;
+    if (cbSize >= sizeof(NOTIFYICONDATAA))
+        nidW.hBalloonIcon = pnid->hBalloonIcon;
 
-    return ret;
+    /* Call the unicode function */
+    return Shell_NotifyIconW(dwMessage, &nidW);
 }
 
-
 /*************************************************************************
- * Shell_NotifyIcon            [SHELL32.296]
- * Shell_NotifyIconA            [SHELL32.297]
+ * Shell_NotifyIconW            [SHELL32.298]
  */
-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;
+
+    /* Find a handle to the shell tray window */
+    hShellTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
+    if (!hShellTrayWnd)
+        return FALSE; // None found, bail out
 
-    /* Validate the cbSize as Windows XP does */
-    if (pnid->cbSize != NOTIFYICONDATAA_V1_SIZE &&
-        pnid->cbSize != NOTIFYICONDATAA_V2_SIZE &&
-        pnid->cbSize != sizeof(NOTIFYICONDATAA))
+    /* 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, NOTIFYICONDATAA_V1_SIZE);
-        cbSize = NOTIFYICONDATAA_V1_SIZE;
+        dwValidFlags |= NIF_STATE | NIF_INFO;
+    }
+    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;
+        }
     }
-    else
-        cbSize = pnid->cbSize;
 
-    return SHELL_NotifyIcon(dwMessage, pnid, pnid->hWnd, cbSize, FALSE);
-}
+    /* Build the data structure */
+    ZeroMemory(&tnid, sizeof(tnid));
+    tnid.dwSignature = NI_NOTIFY_SIG;
+    tnid.dwMessage   = dwMessage;
 
-/*************************************************************************
- * Shell_NotifyIconW            [SHELL32.298]
- */
-BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW pnid)
-{
-    DWORD cbSize;
+    /* 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;
 
-    /* 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))
+    /* Be sure the szTip member (that could be cut-off) is correctly NULL-terminated */
+    if (tnid.nid.uFlags & NIF_TIP)
     {
-        WARN("Invalid cbSize (%d) - using only Win95 fields (size=%d)\n",
-            pnid->cbSize, NOTIFYICONDATAW_V1_SIZE);
-        cbSize = NOTIFYICONDATAA_V1_SIZE;
+        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;
+        }
     }
-    else
-        cbSize = pnid->cbSize;
 
-    return SHELL_NotifyIcon(dwMessage, pnid, pnid->hWnd, cbSize, TRUE);
+    /* 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 ret;
 }