|
|
#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; }
|