windows-nt/Source/XPSP1/NT/shell/shell32/shlnot.cpp
2020-09-26 16:20:57 +08:00

372 lines
11 KiB
C++

#include "shellprv.h"
#pragma hdrstop
#include <trayp.h>
TCHAR const c_szTrayClass[] = TEXT(WNDCLASS_TRAYNOTIFY);
STDAPI_(BOOL) Shell_NotifyIcon(DWORD dwMessage, NOTIFYICONDATA *pnid)
{
HWND hwndTray;
SetLastError(0); // Clean any previous last error (code to help catch another bug)
hwndTray = FindWindow(c_szTrayClass, NULL);
if (hwndTray)
{
COPYDATASTRUCT cds;
TRAYNOTIFYDATA tnd = {0};
DWORD_PTR dwRes = FALSE;
DWORD dwValidFlags;
int cbSize = pnid->cbSize;
if (cbSize == sizeof(*pnid))
{
dwValidFlags = NIF_VALID;
}
// Win2K checked for size of this struct
else if (cbSize == NOTIFYICONDATA_V2_SIZE)
{
dwValidFlags = NIF_VALID_V2;
}
else
{
// This will RIP if the app was buggy and passed stack
// garbage as cbSize. Apps got away with this on Win95
// and NT4 because those versions didn't validate cbSize.
// So if we see a strange cbSize, assume it's the V1 size.
RIP(cbSize == NOTIFYICONDATA_V1_SIZE);
cbSize = NOTIFYICONDATA_V1_SIZE;
dwValidFlags = NIF_VALID_V1;
}
#ifdef _WIN64
// Thunking NOTIFYICONDATA to NOTIFYICONDATA32 is annoying
// on Win64 due to variations in the size of HWND and HICON
// We have to copy each field individually.
tnd.nid.dwWnd = PtrToUlong(pnid->hWnd);
tnd.nid.uID = pnid->uID;
tnd.nid.uFlags = pnid->uFlags;
tnd.nid.uCallbackMessage = pnid->uCallbackMessage;
tnd.nid.dwIcon = PtrToUlong(pnid->hIcon);
// The rest of the fields don't change size between Win32 and
// Win64, so just block copy them over
// Toss in an assertion to make sure
COMPILETIME_ASSERT(
sizeof(NOTIFYICONDATA ) - FIELD_OFFSET(NOTIFYICONDATA , szTip) ==
sizeof(NOTIFYICONDATA32) - FIELD_OFFSET(NOTIFYICONDATA32, szTip));
memcpy(&tnd.nid.szTip, &pnid->szTip, cbSize - FIELD_OFFSET(NOTIFYICONDATA, szTip));
#else
// On Win32, the two structures are the same
COMPILETIME_ASSERT(sizeof(NOTIFYICONDATA) == sizeof(NOTIFYICONDATA32));
memcpy(&tnd.nid, pnid, cbSize);
#endif
tnd.nid.cbSize = sizeof(NOTIFYICONDATA32);
// This will RIP if the app was really buggy and passed stack
// garbage as uFlags.
RIP(!(pnid->uFlags & ~dwValidFlags));
tnd.nid.uFlags &= dwValidFlags;
// Toss in an extra NULL to ensure that the tip is NULL terminated...
if (tnd.nid.uFlags & NIF_TIP)
{
tnd.nid.szTip[ARRAYSIZE(tnd.nid.szTip)-1] = TEXT('\0');
}
if ( (cbSize == sizeof(*pnid)) || (cbSize == NOTIFYICONDATA_V2_SIZE) )
{
if (tnd.nid.uFlags & NIF_INFO)
{
tnd.nid.szInfo[ARRAYSIZE(tnd.nid.szInfo)-1] = TEXT('\0');
tnd.nid.szInfoTitle[ARRAYSIZE(tnd.nid.szInfoTitle)-1] = TEXT('\0');
}
}
if (dwMessage == NIM_SETFOCUS)
{
DWORD dwProcId;
GetWindowThreadProcessId(hwndTray, &dwProcId);
AllowSetForegroundWindow(dwProcId);
}
tnd.dwSignature = NI_SIGNATURE;
tnd.dwMessage = dwMessage;
cds.dwData = TCDM_NOTIFY;
cds.cbData = sizeof(tnd);
cds.lpData = &tnd;
if (SendMessageTimeout(hwndTray, WM_COPYDATA, (WPARAM)pnid->hWnd, (LPARAM)&cds,
SMTO_ABORTIFHUNG | SMTO_BLOCK, 4000, &dwRes))
{
return (BOOL) dwRes;
}
}
return FALSE;
}
#ifdef UNICODE
STDAPI_(BOOL) Shell_NotifyIconA(DWORD dwMessage, NOTIFYICONDATAA *pnid)
{
NOTIFYICONDATAW tndw = {0};
tndw.cbSize = sizeof(tndw);
tndw.hWnd = pnid->hWnd;
tndw.uID = pnid->uID;
tndw.uFlags = pnid->uFlags;
tndw.uCallbackMessage = pnid->uCallbackMessage;
tndw.hIcon = pnid->hIcon;
if (pnid->cbSize == sizeof(*pnid))
{
tndw.dwState = pnid->dwState;
tndw.dwStateMask = pnid->dwStateMask;
tndw.uTimeout = pnid->uTimeout;
tndw.dwInfoFlags = pnid->dwInfoFlags;
}
// Transfer those fields we are aware of as of this writing
else if (pnid->cbSize == NOTIFYICONDATAA_V2_SIZE)
{
tndw.cbSize = NOTIFYICONDATAW_V2_SIZE;
tndw.dwState = pnid->dwState;
tndw.dwStateMask = pnid->dwStateMask;
tndw.uTimeout = pnid->uTimeout;
tndw.dwInfoFlags = pnid->dwInfoFlags;
// This will RIP if the app was really buggy and passed stack
// garbage as uFlags. We have to clear out bogus flags to
// avoid accidentally trying to read from invalid data.
RIP(!(pnid->uFlags & ~NIF_VALID_V2));
tndw.uFlags &= NIF_VALID_V2;
}
else
{
// This will RIP if the app was buggy and passed stack
// garbage as cbSize. Apps got away with this on Win95
// and NT4 because those versions didn't validate cbSize.
// So if we see a strange cbSize, assume it's the V1 size.
RIP(pnid->cbSize == (DWORD)NOTIFYICONDATAA_V1_SIZE);
tndw.cbSize = NOTIFYICONDATAW_V1_SIZE;
// This will RIP if the app was really buggy and passed stack
// garbage as uFlags. We have to clear out bogus flags to
// avoid accidentally trying to read from invalid data.
RIP(!(pnid->uFlags & ~NIF_VALID_V1));
tndw.uFlags &= NIF_VALID_V1;
}
if (tndw.uFlags & NIF_TIP)
SHAnsiToUnicode(pnid->szTip, tndw.szTip, ARRAYSIZE(tndw.szTip));
if (tndw.uFlags & NIF_INFO)
{
SHAnsiToUnicode(pnid->szInfo, tndw.szInfo, ARRAYSIZE(tndw.szInfo));
SHAnsiToUnicode(pnid->szInfoTitle, tndw.szInfoTitle, ARRAYSIZE(tndw.szInfoTitle));
}
if (tndw.uFlags & NIF_GUID)
{
memcpy(&(tndw.guidItem), &(pnid->guidItem), sizeof(pnid->guidItem));
}
return Shell_NotifyIconW(dwMessage, &tndw);
}
#else
STDAPI_(BOOL) Shell_NotifyIconW(DWORD dwMessage, NOTIFYICONDATAW *pnid)
{
return FALSE;
}
#endif
//*** CopyIn -- copy app data in to shared region (and create shared)
// ENTRY/EXIT
// return handle on success, NULL on failure
// pvData app buffer
// cbData count
// dwProcId ...
// NOTES
// should make it handle pvData=NULL for cases where param is OUT not INOUT.
//
HANDLE CopyIn(void *pvData, int cbData, DWORD dwProcId)
{
HANDLE hShared = SHAllocShared(NULL, cbData, dwProcId);
if (hShared)
{
void *pvShared = SHLockShared(hShared, dwProcId);
if (pvShared == NULL)
{
SHFreeShared(hShared, dwProcId);
hShared = NULL;
}
else
{
memcpy(pvShared, pvData, cbData);
SHUnlockShared(pvShared);
}
}
return hShared;
}
// copy out to app data from shared region (and free shared)
// ENTRY/EXIT
// return TRUE on success, FALSE on failure.
// hShared shared data, freed when done
// pvData app buffer
// cbData count
BOOL CopyOut(HANDLE hShared, void *pvData, int cbData, DWORD dwProcId)
{
void *pvShared = SHLockShared(hShared, dwProcId);
if (pvShared)
{
memcpy(pvData, pvShared, cbData);
SHUnlockShared(pvShared);
}
SHFreeShared(hShared, dwProcId);
return (pvShared != 0);
}
STDAPI_(UINT_PTR) SHAppBarMessage(DWORD dwMessage, APPBARDATA *pabd)
{
TRAYAPPBARDATA tabd;
UINT_PTR fret = FALSE;
HWND hwndTray = FindWindow(c_szTrayClass, NULL);
if (hwndTray && (pabd->cbSize <= sizeof(*pabd)))
{
COPYDATASTRUCT cds;
RIP(pabd->cbSize == sizeof(*pabd));
#ifdef _WIN64
tabd.abd.dwWnd = PtrToUlong(pabd->hWnd);
tabd.abd.uCallbackMessage = pabd->uCallbackMessage;
tabd.abd.uEdge = pabd->uEdge;
tabd.abd.rc = pabd->rc;
#else
// Sadly, the Win32 compiler doesn't realize that the code
// sequence above can be optimized into a single memcpy, so
// we need to spoon-feed it...
memcpy(&tabd.abd.dwWnd, &pabd->hWnd,
FIELD_OFFSET(APPBARDATA, lParam) - FIELD_OFFSET(APPBARDATA, hWnd));
#endif
tabd.abd.cbSize = sizeof(tabd.abd);
tabd.abd.lParam = pabd->lParam;
tabd.dwMessage = dwMessage;
tabd.hSharedABD = PtrToUlong(NULL);
tabd.dwProcId = GetCurrentProcessId();
cds.dwData = TCDM_APPBAR;
cds.cbData = sizeof(tabd);
cds.lpData = &tabd;
//
// These are the messages that return data back to the caller.
//
switch (dwMessage)
{
case ABM_QUERYPOS:
case ABM_SETPOS:
case ABM_GETTASKBARPOS:
tabd.hSharedABD = PtrToUlong(CopyIn(&tabd.abd, sizeof(tabd.abd), tabd.dwProcId));
if (tabd.hSharedABD == PtrToUlong(NULL))
return FALSE;
break;
}
fret = SendMessage(hwndTray, WM_COPYDATA, (WPARAM)pabd->hWnd, (LPARAM)&cds);
if (tabd.hSharedABD)
{
if (CopyOut(UlongToPtr(tabd.hSharedABD), &tabd.abd, sizeof(tabd.abd), tabd.dwProcId))
{
#ifdef _WIN64
pabd->hWnd = (HWND)UIntToPtr(tabd.abd.dwWnd);
pabd->uCallbackMessage = tabd.abd.uCallbackMessage;
pabd->uEdge = tabd.abd.uEdge;
pabd->rc = tabd.abd.rc;
#else
// Sadly, the Win32 compiler doesn't realize that the code
// sequence above can be optimized into a single memcpy, so
// we need to spoon-feed it...
memcpy(&pabd->hWnd, &tabd.abd.dwWnd,
FIELD_OFFSET(APPBARDATA, lParam) - FIELD_OFFSET(APPBARDATA, hWnd));
#endif
pabd->lParam = (LPARAM)tabd.abd.lParam;
}
else
fret = FALSE;
}
}
return fret;
}
HRESULT _TrayLoadInProc(REFCLSID rclsid, DWORD dwFlags)
{
HWND hwndTray = FindWindow(c_szTrayClass, NULL);
if (hwndTray)
{
COPYDATASTRUCT cds;
LOADINPROCDATA lipd;
lipd.clsid = rclsid;
lipd.dwFlags = dwFlags;
cds.dwData = TCDM_LOADINPROC;
cds.cbData = sizeof(lipd);
cds.lpData = &lipd;
return (HRESULT)SendMessage(hwndTray, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
}
else
{
return E_FAIL;
}
}
STDAPI SHLoadInProc(REFCLSID rclsid)
{
return _TrayLoadInProc(rclsid, LIPF_ENABLE);
}
STDAPI SHEnableServiceObject(REFCLSID rclsid, BOOL fEnable)
{
DWORD dwFlags = fEnable ? LIPF_ENABLE | LIPF_HOLDREF : LIPF_HOLDREF;
return _TrayLoadInProc(rclsid, dwFlags);
}
// used to implement a per process reference count for the main thread
// the browser msg loop and the proxy desktop use this to let other threads
// extend their lifetime.
// there is a thread level equivelent of this, shlwapi SHGetThreadRef()/SHSetThreadRef()
IUnknown *g_punkProcessRef = NULL;
STDAPI_(void) SHSetInstanceExplorer(IUnknown *punk)
{
g_punkProcessRef = punk;
}
// This should be thread safe since we grab the punk locally before
// checking/using it, plus it never gets freed since it is not actually
// alloced in Explorer so we can always use it
STDAPI SHGetInstanceExplorer(IUnknown **ppunk)
{
*ppunk = g_punkProcessRef;
if (*ppunk)
{
(*ppunk)->AddRef();
return NOERROR;
}
return E_FAIL;
}