#include "shellprv.h" #pragma hdrstop #include 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; }