#include "precomp.h" #include "mcinc.h" #include "util.h" #include "intshcut.h" #include "optary.h" #define COMPILE_MULTIMON_STUBS #include "multimon.h" #undef COMPILE_MULTIMON_STUBS BOOL IsSysKeyMessage(MSG *pMsg) { switch(pMsg->message) { case WM_SYSKEYDOWN: case WM_SYSKEYUP : case WM_SYSCHAR : if(pMsg->wParam == VK_MENU) break; // Alt key alone. if(pMsg->wParam >= L'0' && pMsg->wParam <= L'9' ) break; // ALT+ should pass through. if(pMsg->wParam >= VK_NUMPAD0 && pMsg->wParam <= VK_NUMPAD9) break; // ALT+ should pass through. case WM_SYSDEADCHAR: return TRUE; } return FALSE; } BOOL IsGlobalKeyMessage(MSG *pMsg) { BOOL fRet = IsSysKeyMessage( pMsg ); if(!fRet) { switch(pMsg->message) { case WM_KEYDOWN: case WM_KEYUP: // Allow ESC and CTRL-E as well... fRet = ((pMsg->wParam == VK_ESCAPE ) || (pMsg->wParam == L'E' && GetAsyncKeyState( VK_CONTROL ) < 0) ); } } return fRet; } int IsVK_TABCycler(MSG *pMsg) { int result; if (pMsg && (pMsg->message == WM_KEYDOWN) && ((pMsg->wParam == VK_TAB) || (pMsg->wParam == VK_F6))) { result = (GetKeyState(VK_SHIFT) < 0) ? -1 : 1; } else { result = 0; } return result; } DWORD CThreadData::s_dwTlsIndex = 0xffffffff; CThreadData::CThreadData() { } CThreadData::~CThreadData() { } BOOL CThreadData::TlsSetValue(CThreadData *ptd) { ATLASSERT(s_dwTlsIndex != 0xffffffff); // Don't call set twice except to clear ATLASSERT((NULL == ptd) || (NULL == ::TlsGetValue(s_dwTlsIndex))); return ::TlsSetValue(s_dwTlsIndex, ptd); } BOOL CThreadData::HaveData() { ATLASSERT(s_dwTlsIndex != 0xffffffff); return NULL != ::TlsGetValue(s_dwTlsIndex); } CThreadData *CThreadData::TlsGetValue() { ATLASSERT(s_dwTlsIndex != 0xffffffff); CThreadData *ptd = (CThreadData *)::TlsGetValue(s_dwTlsIndex); ATLASSERT(NULL != ptd); return ptd; } BOOL CThreadData::TlsAlloc() { ATLASSERT(s_dwTlsIndex == 0xffffffff); // Don't call this twice s_dwTlsIndex = ::TlsAlloc(); return (s_dwTlsIndex != 0xffffffff) ? TRUE : FALSE; } BOOL CThreadData::TlsFree() { BOOL bResult; if (s_dwTlsIndex != 0xffffffff) { bResult = ::TlsFree(s_dwTlsIndex); s_dwTlsIndex = 0xffffffff; } else { bResult = FALSE; } return bResult; } HRESULT GetMarsTypeLib(ITypeLib **ppTypeLib) { ATLASSERT(NULL != ppTypeLib); CThreadData *pThreadData = CThreadData::TlsGetValue(); if (!pThreadData->m_spTypeLib) { // Load our typelib, to be used for our automation interfaces WCHAR wszModule[_MAX_PATH+10]; GetModuleFileNameW(_Module.GetModuleInstance(), wszModule, _MAX_PATH); LoadTypeLib(wszModule, &pThreadData->m_spTypeLib); } pThreadData->m_spTypeLib.CopyTo(ppTypeLib); return ((NULL != ppTypeLib) && (NULL != *ppTypeLib)) ? S_OK : E_FAIL; } UINT HashKey(LPCWSTR pwszName) { int hash = 0; while (*pwszName) { hash += (hash << 5) + *pwszName++; } return hash; } void AsciiToLower(LPWSTR pwsz) { while (*pwsz) { if ((*pwsz >= L'A') && (*pwsz <= L'Z')) { *pwsz += L'a' - L'A'; } pwsz++; } } HRESULT PIDLToVariant(LPCITEMIDLIST pidl, CComVariant& v) { // the variant must be empty since we don't clear or initialize it HRESULT hr = S_OK; // NULL pidls are valid, so we just leave the variant empty and return S_OK if (pidl) { v.bstrVal = SysAllocStringLen(NULL, MAX_PATH); if (v.bstrVal) { // make this an official BSTR since the alloc succeeded v.vt = VT_BSTR; if (!SHGetPathFromIDListW(pidl, v.bstrVal)) { // CComVariant will handle cleanup hr = E_FAIL; } } else { hr = E_OUTOFMEMORY; } } return hr; } // Checks if global state is offline BOOL IsGlobalOffline(void) { DWORD dwState = 0, dwSize = sizeof(DWORD); BOOL fRet = FALSE; HANDLE hModuleHandle = LoadLibraryA("wininet.dll"); if (!hModuleHandle) { return FALSE; } if (InternetQueryOption(NULL, INTERNET_OPTION_CONNECTED_STATE, &dwState, &dwSize)) { if(dwState & INTERNET_STATE_DISCONNECTED_BY_USER) fRet = TRUE; } return fRet; } void SetGlobalOffline(BOOL fOffline) { INTERNET_CONNECTED_INFO ci; memset(&ci, 0, sizeof(ci)); if (fOffline) { ci.dwConnectedState = INTERNET_STATE_DISCONNECTED_BY_USER; ci.dwFlags = ISO_FORCE_DISCONNECTED; } else { ci.dwConnectedState = INTERNET_STATE_CONNECTED; } InternetSetOption(NULL, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci)); } HRESULT _WriteDIBToFile(HBITMAP hDib, HANDLE hFile) { if (!hDib) { return E_INVALIDARG; } // Make sure this is a valid DIB and get this useful info. DIBSECTION ds; if (!GetObject( hDib, sizeof(DIBSECTION), &ds )) { return E_INVALIDARG; } // We only deal with DIBs if (ds.dsBm.bmPlanes != 1) { return E_INVALIDARG; } // Calculate some color table sizes int nColors = ds.dsBmih.biBitCount <= 8 ? 1 << ds.dsBmih.biBitCount : 0; int nBitfields = ds.dsBmih.biCompression == BI_BITFIELDS ? 3 : 0; // Calculate the data size int nImageDataSize = ds.dsBmih.biSizeImage ? ds.dsBmih.biSizeImage : ds.dsBm.bmWidthBytes * ds.dsBm.bmHeight; // Get the color table (if needed) RGBQUAD rgbqaColorTable[256] = {0}; if (nColors) { HDC hDC = CreateCompatibleDC(NULL); if (hDC) { HBITMAP hOldBitmap = reinterpret_cast(SelectObject(hDC,hDib)); GetDIBColorTable( hDC, 0, nColors, rgbqaColorTable ); SelectObject(hDC,hOldBitmap); DeleteDC( hDC ); } } // Create the file header BITMAPFILEHEADER bmfh; bmfh.bfType = 'MB'; bmfh.bfSize = 0; bmfh.bfReserved1 = 0; bmfh.bfReserved2 = 0; bmfh.bfOffBits = sizeof(bmfh) + sizeof(ds.dsBmih) + nBitfields*sizeof(DWORD) + nColors*sizeof(RGBQUAD); // Start writing! Note that we write out the bitfields and the color table. Only one, // at most, will actually result in data being written DWORD dwBytesWritten; if (!WriteFile( hFile, &bmfh, sizeof(bmfh), &dwBytesWritten, NULL )) return HRESULT_FROM_WIN32(GetLastError()); if (!WriteFile( hFile, &ds.dsBmih, sizeof(ds.dsBmih), &dwBytesWritten, NULL )) return HRESULT_FROM_WIN32(GetLastError()); if (!WriteFile( hFile, &ds.dsBitfields, nBitfields*sizeof(DWORD), &dwBytesWritten, NULL )) return HRESULT_FROM_WIN32(GetLastError()); if (!WriteFile( hFile, rgbqaColorTable, nColors*sizeof(RGBQUAD), &dwBytesWritten, NULL )) return HRESULT_FROM_WIN32(GetLastError()); if (!WriteFile( hFile, ds.dsBm.bmBits, nImageDataSize, &dwBytesWritten, NULL )) return HRESULT_FROM_WIN32(GetLastError()); return S_OK; } HRESULT SaveDIBToFile(HBITMAP hbm, WCHAR *pszPath) { HRESULT hr = E_INVALIDARG; if (hbm != NULL && hbm != INVALID_HANDLE_VALUE) { HANDLE hFile; hr = E_FAIL; hFile = CreateFileWrapW(pszPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { hr = _WriteDIBToFile(hbm, hFile); CloseHandle(hFile); } } return hr; } // BoundWindowRect will nudge a rectangle so that it stays fully on its current monitor. // pRect must be in workspace coordinates void BoundWindowRectToMonitor(HWND hwnd, RECT *pRect) { MONITORINFO mi; mi.cbSize = sizeof(mi); GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), &mi); OffsetRect(&mi.rcWork, mi.rcMonitor.left - mi.rcWork.left, mi.rcMonitor.top - mi.rcWork.top); LONG lDeltaX = 0, lDeltaY = 0; if (pRect->left < mi.rcWork.left) lDeltaX = mi.rcWork.left - pRect->left; if (pRect->top < mi.rcWork.top) lDeltaY = mi.rcWork.top - pRect->top; if (pRect->right > mi.rcWork.right) lDeltaX = mi.rcWork.right - pRect->right; if (pRect->bottom > mi.rcWork.bottom) lDeltaY = mi.rcWork.bottom - pRect->bottom; RECT rc = *pRect; OffsetRect(&rc, lDeltaX, lDeltaY); IntersectRect(pRect, &rc, &mi.rcWork); } // Moves a rectangle down and to the right, by the same amount Windows would use // to cascade. If the new position is partially off-screen, then the rect is either // moved up to the top, or back to the origin. void CascadeWindowRectOnMonitor(HWND hwnd, RECT *pRect) { int delta = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYSIZEFRAME) - 1; OffsetRect(pRect, delta, delta); // test if the new rect will end up getting moved later on RECT rc = *pRect; BoundWindowRectToMonitor(hwnd, &rc); if (!EqualRect(pRect, &rc)) { // rc had to be moved, so we'll restart the cascade using the best monitor MONITORINFO mi; mi.cbSize = sizeof(mi); GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), &mi); if (rc.bottom < pRect->bottom && rc.left == pRect->left) { // Too tall to cascade further down, but we can keep the X and just // reset the Y. This fixes the bug of having a tall windows piling up // on the top left corner -- instead they will be offset to the right OffsetRect(pRect, 0, mi.rcMonitor.top - pRect->top); } else { // we've really run out of room, so restart cascade at top left OffsetRect(pRect, mi.rcMonitor.left - pRect->left, mi.rcMonitor.top - pRect->top); } } } struct WINDOWSEARCHSTRUCT { LONG x; LONG y; ATOM atomClass; BOOL fFoundWindow; }; BOOL CALLBACK EnumWindowSearchProc(HWND hwnd, LPARAM lParam) { WINDOWSEARCHSTRUCT *pSearch = (WINDOWSEARCHSTRUCT *) lParam; if ((ATOM) GetClassLong(hwnd, GCW_ATOM) == pSearch->atomClass) { // Only check the rest if we find a window that matches our class WINDOWPLACEMENT wp; wp.length = sizeof(wp); GetWindowPlacement(hwnd, &wp); pSearch->fFoundWindow = pSearch->x == wp.rcNormalPosition.left && pSearch->y == wp.rcNormalPosition.top && IsWindowVisible(hwnd); } // return TRUE if we want to continue the enumeration return !pSearch->fFoundWindow; } // Checks whether there is a window of the same class at some location on screen. // x and y are in workspace coords because we need to use GetWindowPlacement to // retrieve the rect of the restored window. BOOL IsWindowOverlayed(HWND hwndMatch, LONG x, LONG y) { WINDOWSEARCHSTRUCT search = { x, y, (ATOM) GetClassLong(hwndMatch, GCW_ATOM), FALSE }; EnumWindows(EnumWindowSearchProc, (LPARAM) &search); return search.fFoundWindow; } BOOL CInterfaceMarshal::Init() { m_hresMarshal = E_FAIL; m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); return m_hEvent != NULL; } CInterfaceMarshal::~CInterfaceMarshal() { if (m_hEvent) CloseHandle(m_hEvent); SAFERELEASE2(m_pStream); } HRESULT CInterfaceMarshal::Marshal(REFIID riid, IUnknown *pUnk) { ATLASSERT(pUnk); m_hresMarshal = CoMarshalInterThreadInterfaceInStream(riid, pUnk, &m_pStream); // We must signal the other thread regardless of whether the marshal was // successful, otherwise it will be blocked for a very long time. Signal(); return m_hresMarshal; } HRESULT CInterfaceMarshal::UnMarshal(REFIID riid, void ** ppv) { HRESULT hr; ATLASSERT(ppv); if (S_OK == m_hresMarshal) { hr = CoGetInterfaceAndReleaseStream(m_pStream, riid, ppv); m_pStream = NULL; } else { hr = m_hresMarshal; } return hr; } void CInterfaceMarshal::Signal() { ATLASSERT(m_hEvent); SetEvent(m_hEvent); } // This waiting code was copied from Shdocvw iedisp.cpp // // hSignallingThread is the handle of the thread that will be setting the m_hEvent. // If that thread terminates before marshalling an interface, we can detect this // condition and not hang around pointlessly. HRESULT CInterfaceMarshal::WaitForSignal(HANDLE hSignallingThread, DWORD dwSecondsTimeout) { ATLASSERT(m_hEvent); HANDLE ah[] = { m_hEvent, hSignallingThread }; DWORD dwStart = GetTickCount(); DWORD dwMaxWait = 1000 * dwSecondsTimeout; DWORD dwWait = dwMaxWait; DWORD dwWaitResult; do { // dwWait is the number of millseconds we still need to wait for dwWaitResult = MsgWaitForMultipleObjects( ARRAYSIZE(ah), ah, FALSE, dwWait, QS_SENDMESSAGE); if (dwWaitResult == WAIT_OBJECT_0 + ARRAYSIZE(ah)) { // Msg input. We allow the pending SendMessage() to go through MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); } else { // signaled or timed out, so we exit the loop break; } // Update dwWait. It will become larger than dwMaxWait if we // wait more than that dwWait = dwStart + dwMaxWait - GetTickCount(); } while (dwWait <= dwMaxWait); HRESULT hr = E_FAIL; switch (dwWaitResult) { case WAIT_OBJECT_0: // Event signaled -- this is what should happen every time hr = m_hresMarshal; break; case WAIT_OBJECT_0 + 1: // Thread terminated before signalling break; case WAIT_OBJECT_0 + ARRAYSIZE(ah): // msg input -- fall through case WAIT_TIMEOUT: // Timed out while waiting for signal break; } return hr; } //////////////////////////////////////////////////////////////////////////////// HRESULT MarsNavigateShortcut(IUnknown *pBrowser, IUniformResourceLocator* pUrl, LPCWSTR pszPath) { HRESULT hr; if (pBrowser ) { CComPtr spMkUrl; CComPtr spBindCtx; // Create moniker LPWSTR pszURL = NULL; hr = pUrl->GetURL(&pszURL); if (pszURL) { hr = CreateURLMoniker(NULL, pszURL, &spMkUrl); SHFree(pszURL); } if (SUCCEEDED(hr) && spMkUrl) { // create bind context and register load options // NOTE: errors here are not fatal, as the bind context is optional, so hr is not set CreateBindCtx(0, &spBindCtx); if (spBindCtx) { CComPtr spLoadOpt; if (SUCCEEDED(CoCreateInstance(CLSID_HTMLLoadOptions, NULL, CLSCTX_INPROC_SERVER, IID_IHtmlLoadOptions, (void**)&spLoadOpt))) { if (pszPath) { spLoadOpt->SetOption(HTMLLOADOPTION_INETSHORTCUTPATH, (void*)pszPath, (lstrlen(pszPath) + 1) * sizeof(WCHAR)); } spBindCtx->RegisterObjectParam(L"__HTMLLOADOPTIONS", spLoadOpt); } } // create hyperlink using URL moniker CComPtr spHlink; hr = HlinkCreateFromMoniker(spMkUrl, NULL, NULL, NULL, 0, NULL, IID_IHlink, (void **)&spHlink); if (spHlink) { // navigate frame using hyperlink and bind context CComQIPtr spFrame(pBrowser); if (spFrame) { hr = spFrame->Navigate(0, spBindCtx, NULL, spHlink); } else { hr = E_NOINTERFACE; } } } } else { // L"MarsNavigateShortcut: target or path is NULL"; hr = E_INVALIDARG; } return hr; } HRESULT MarsNavigateShortcut(IUnknown *pBrowser, LPCWSTR lpszPath) { HRESULT hr; if (pBrowser && lpszPath) { // create internet shortcut object CComPtr spPersistFile; hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IPersistFile, (void **)&spPersistFile); if (SUCCEEDED(hr)) { // persist from file hr = spPersistFile->Load(lpszPath, STGM_READ); if (SUCCEEDED(hr)) { CComQIPtr spURL(spPersistFile); if (spURL) { hr = MarsNavigateShortcut(pBrowser, spURL, lpszPath); } else { hr = E_NOINTERFACE; } } } } else { hr = E_INVALIDARG; } return hr; } HRESULT MarsVariantToPath(VARIANT &varItem, CComBSTR &strPath) { HRESULT hr = E_INVALIDARG; if (API_IsValidVariant(varItem)) { switch (varItem.vt) { case VT_EMPTY: case VT_NULL: case VT_ERROR: // return path empty when undefined, null, or omitted strPath.Empty(); hr = S_OK; break; case VT_BSTR: // make a copy of the supplied path strPath = varItem.bstrVal; hr = S_OK; break; case VT_DISPATCH: { // query for FolderItem interface CComQIPtr spFolderItem(varItem.pdispVal); // if we don't have a FolderItem, try to get one if (!spFolderItem) { // if we got a Folder2 object instead of a FolderItem object CComQIPtr spFolder2(varItem.pdispVal); if (spFolder2) { // get FolderItem object from Folder2 interface spFolder2->get_Self(&spFolderItem); } } // if we managed to get a folder item if (spFolderItem) { // get the path from it CComBSTR bstr; hr = spFolderItem->get_Path(&bstr); strPath = bstr; } } break; } } return hr; } BOOL PathIsURLFileW(LPCWSTR lpszPath) { BOOL fDoesMatch = FALSE; if (lpszPath) { LPCWSTR lpszExt = PathFindExtensionW(lpszPath); if (lpszExt && (StrCmpIW(lpszExt, L".url") == 0)) { fDoesMatch = TRUE; } } return fDoesMatch; } //////////////////////////////////////////////////////////////////////////////// #define GLOBAL_SETTINGS_PATH L"Software\\Microsoft\\PCHealth\\Global" //================================================================== // Registry helpers //================================================================== LONG CRegistryKey::QueryLongValue(LONG& lValue, LPCWSTR pwszValueName) { DWORD dwValue; LONG lResult = QueryValue(dwValue, pwszValueName); if (lResult == ERROR_SUCCESS) { lValue = (LONG) dwValue; } return lResult; } LONG CRegistryKey::SetLongValue(LONG lValue, LPCWSTR pwszValueName) { return SetValue((DWORD) lValue, pwszValueName); } LONG CRegistryKey::QueryBoolValue(BOOL& bValue, LPCWSTR pwszValueName) { DWORD dwValue; LONG lResult = QueryValue(dwValue, pwszValueName); if (lResult == ERROR_SUCCESS) { bValue = (BOOL) dwValue; } return lResult; } LONG CRegistryKey::SetBoolValue(BOOL bValue, LPCWSTR pwszValueName) { return SetValue((DWORD) bValue, pwszValueName); } LONG CRegistryKey::QueryBinaryValue(LPVOID pData, DWORD cbData, LPCWSTR pwszValueName) { DWORD dwType; DWORD lResult = RegQueryValueEx(m_hKey, pwszValueName, NULL, &dwType, (BYTE *) pData, &cbData); return (lResult == ERROR_SUCCESS) && (dwType != REG_BINARY) ? ERROR_INVALID_DATA : lResult; } LONG CRegistryKey::SetBinaryValue(LPVOID pData, DWORD cbData, LPCWSTR pwszValueName) { return RegSetValueEx(m_hKey, pwszValueName, NULL, REG_BINARY, (BYTE *) pData, cbData); } LONG CGlobalSettingsRegKey::CreateGlobalSubkey(LPCWSTR pwszSubkey) { CComBSTR strPath = GLOBAL_SETTINGS_PATH; if (pwszSubkey) { strPath += L"\\"; strPath += pwszSubkey; } return Create(HKEY_CURRENT_USER, strPath); } LONG CGlobalSettingsRegKey::OpenGlobalSubkey(LPCWSTR pwszSubkey) { CComBSTR strPath = GLOBAL_SETTINGS_PATH; if (pwszSubkey) { strPath += L"\\"; strPath += pwszSubkey; } return Open(HKEY_CURRENT_USER, strPath); }