372 lines
11 KiB
C++
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;
|
|
}
|