[SHELL32] Rewrite the wrapping code for shell taskbar notifications.

- 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.
This commit is contained in:
Hermès Bélusca-Maïto 2018-02-04 18:02:41 +01:00
parent 4207397963
commit 131678a025
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
4 changed files with 195 additions and 107 deletions

View file

@ -21,14 +21,6 @@
#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;
}

View file

@ -17,6 +17,7 @@
#include <wincon.h>
#include <commdlg.h>
#include <ddeml.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <shobjidl.h>
@ -36,7 +37,11 @@
#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>

View 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.
@ -23,91 +24,101 @@
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));
/* 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;
/* Capture the other data fields */
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));
}
}
if (cbSize >= NOTIFYICONDATAA_V2_SIZE)
{
nidW.dwState = pnid->dwState;
nidW.dwStateMask = pnid->dwStateMask;
/* szInfo, szInfoTitle */
if (pnid->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));
}
nidW.uTimeout = pnid->uTimeout;
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))
{
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 | 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)
{
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, TRUE);
/* 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 ret;
}

View file

@ -30,10 +30,29 @@ extern "C" {
#define DBIMF_NOMARGINS 0x2000
#endif // NTDDI_LONGHORN
#if defined (_SHELLAPI_H) || defined (_INC_SHELLAPI)
/****************************************************************************
* 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;