#include "priv.h" #include "varutil.h" // static context menu for the start.search menu. note, this gets invoked directly // by a number of clients (in shell32 for example) class CShellSearchExt : public IContextMenu, public IObjectWithSite { public: CShellSearchExt(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IContextMenu STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici); STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT wFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax); // IObjectWithSite STDMETHODIMP SetSite(IUnknown *pUnkSite); STDMETHODIMP GetSite(REFIID riid, void **ppvSite); protected: virtual ~CShellSearchExt(); // for a derived class private: virtual BOOL _GetSearchUrls(GUID *pguid, LPTSTR psz, DWORD cch, LPTSTR pszUrlNavNew, DWORD cchNavNew, BOOL *pfRunInProcess); HRESULT _IsShellSearchBand(REFGUID guidSearch); HRESULT _ShowShellSearchResults(IWebBrowser2* pwb2, BOOL fNewFrame, REFGUID guidSearch); LONG _cRef; IUnknown *_pSite; }; STDAPI CShellSearchExt_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CShellSearchExt* psse = new CShellSearchExt(); if (psse) { *ppunk = SAFECAST(psse, IContextMenu*); return S_OK; } else { *ppunk = NULL; return E_OUTOFMEMORY; } } STDMETHODIMP CShellSearchExt::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CShellSearchExt, IContextMenu), QITABENT(CShellSearchExt, IObjectWithSite), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CShellSearchExt::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CShellSearchExt::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDMETHODIMP CShellSearchExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { return E_NOTIMPL; } #define SZ_SHELL_SEARCH TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FindExtensions\\Static\\ShellSearch") BOOL CShellSearchExt::_GetSearchUrls(GUID *pguidSearch, LPTSTR pszUrl, DWORD cch, LPTSTR pszUrlNavNew, DWORD cchNavNew, BOOL *pfRunInProcess) { BOOL bRet = FALSE; *pfRunInProcess = FALSE; // Assume that we are not forcing it to run in process. if (pszUrl == NULL || IsEqualGUID(*pguidSearch, GUID_NULL) || pszUrlNavNew == NULL) return bRet; *pszUrlNavNew = 0; HKEY hkey; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_SHELL_SEARCH, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { TCHAR szSubKey[32]; HKEY hkeySub; for (int i = 0; SUCCEEDED(StringCchPrintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("%d"), i)) && (RegOpenKeyEx(hkey, szSubKey, NULL, KEY_QUERY_VALUE, &hkeySub) == ERROR_SUCCESS); i++) { TCHAR szSearchGuid[MAX_PATH]; DWORD dwType, cb = sizeof(szSearchGuid); if (SHGetValue(hkeySub, TEXT("SearchGUID"), NULL, &dwType, (BYTE*)szSearchGuid, &cb) == ERROR_SUCCESS) { GUID guid; if (GUIDFromString(szSearchGuid, &guid) && IsEqualGUID(guid, *pguidSearch)) { cb = cch * sizeof(TCHAR); bRet = (SHGetValue(hkeySub, TEXT("SearchGUID\\Url"), NULL, &dwType, (BYTE*)pszUrl, &cb) == ERROR_SUCCESS); if (bRet || IsEqualGUID(*pguidSearch, SRCID_SFileSearch)) { if (!bRet) { *pszUrl = 0; // in file search case we don't need url but we still succeed bRet = TRUE; } // See if there is a URL that we should navigate to if we // are navigating to a new cb = cchNavNew * sizeof(TCHAR); SHGetValue(hkeySub, TEXT("SearchGUID\\UrlNavNew"), NULL, &dwType, (BYTE*)pszUrlNavNew, &cb); // likewise try to grab the RunInProcess flag, if not there or zero then off, else on // reuse szSearchGuid for now... *pfRunInProcess = (BOOL)SHRegGetIntW(hkeySub, L"RunInProcess", 0); } RegCloseKey(hkeySub); break; } } RegCloseKey(hkeySub); } RegCloseKey(hkey); } if (!bRet) pszUrl[0] = 0; return bRet; } STDMETHODIMP CShellSearchExt::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { TCHAR szUrl[MAX_URL_STRING], szUrlNavNew[MAX_URL_STRING]; BOOL bNewFrame = FALSE; // First get the Urls such that we can see which class we should create... GUID guidSearch = GUID_NULL; BOOL fRunInProcess = FALSE; CLSID clsidBand; // deskband object for search // Retrieve search ID from invoke params if (pici->lpParameters) GUIDFromStringA(pici->lpParameters, &guidSearch); HRESULT hr = S_OK; BOOL fShellSearchBand = (S_OK == _IsShellSearchBand(guidSearch)); if (fShellSearchBand) { clsidBand = CLSID_FileSearchBand; if (SHRestricted(REST_NOFIND) && IsEqualGUID(guidSearch, SRCID_SFileSearch)) hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // user saw the error } else { clsidBand = CLSID_SearchBand; // retrieve search URLs from registry if (!_GetSearchUrls(&guidSearch, szUrl, ARRAYSIZE(szUrl), szUrlNavNew, ARRAYSIZE(szUrlNavNew), &fRunInProcess)) hr = E_FAIL; } if (SUCCEEDED(hr)) { // if invoked from within a browser reuse it, else open a new browser IWebBrowser2 *pwb2; hr = IUnknown_QueryServiceForWebBrowserApp(_pSite, IID_PPV_ARG(IWebBrowser2, &pwb2)); if (FAILED(hr)) { // Note: we want the frame to display shell characteristics (CLSID_ShellBrowserWindow), // including persistence behavior, if we're loading shell search (CLSID_FileSearchBand). if (fRunInProcess || IsEqualGUID(clsidBand, CLSID_FileSearchBand)) hr = CoCreateInstance(CLSID_ShellBrowserWindow, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pwb2)); else hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pwb2)); bNewFrame = TRUE; } if (SUCCEEDED(hr)) { // show html-hosting band VARIANT var, varEmpty = {0}; hr = InitBSTRVariantFromGUID(&var, clsidBand); if (SUCCEEDED(hr)) { hr = pwb2->ShowBrowserBar(&var, &varEmpty, &varEmpty); VariantClear(&var); } if (SUCCEEDED(hr)) { if (fShellSearchBand) { hr = _ShowShellSearchResults(pwb2, bNewFrame, guidSearch); } else { LBSTR::CString strUrl; VARIANT varFlags; varFlags.vt = VT_I4; varFlags.lVal = navBrowserBar; LPTSTR pstrUrl = strUrl.GetBuffer( MAX_URL_STRING ); if ( strUrl.GetAllocLength() < MAX_URL_STRING ) { TraceMsg( TF_WARNING, "CShellSearchExt::InvokeCommand() - strUrl Allocation Failed!" ); strUrl.Empty(); } else { SHTCharToUnicode( szUrl, pstrUrl, MAX_URL_STRING ); // Let CString class own the buffer again. strUrl.ReleaseBuffer(); } var.vt = VT_BSTR; var.bstrVal = strUrl; // if we opened a new window, navigate the right side to about.blank if (bNewFrame) { LBSTR::CString strNavNew; if ( szUrlNavNew[0] ) { LPTSTR pstrNavNew = strNavNew.GetBuffer( MAX_URL_STRING ); if ( strNavNew.GetAllocLength() < MAX_URL_STRING ) { TraceMsg( TF_WARNING, "CShellSearchExt::InvokeCommand() - strNavNew Allocation Failed!" ); strNavNew.Empty(); } else { SHTCharToUnicode( szUrlNavNew, pstrNavNew, MAX_URL_STRING ); // Let CString class own the buffer again. strNavNew.ReleaseBuffer(); } } else { strNavNew = L"about:blank"; } // we don't care about the error here pwb2->Navigate( strNavNew, &varEmpty, &varEmpty, &varEmpty, &varEmpty ); } // navigate the search bar to the correct url hr = pwb2->Navigate2( &var, &varFlags, &varEmpty, &varEmpty, &varEmpty ); } } if (SUCCEEDED(hr) && bNewFrame) hr = pwb2->put_Visible(TRUE); pwb2->Release(); } } return hr; } HRESULT CShellSearchExt::_IsShellSearchBand(REFGUID guidSearch) { if (IsEqualGUID(guidSearch, SRCID_SFileSearch) || IsEqualGUID(guidSearch, SRCID_SFindComputer) || IsEqualGUID(guidSearch, SRCID_SFindPrinter)) return S_OK; return S_FALSE; } HRESULT CShellSearchExt::_ShowShellSearchResults(IWebBrowser2* pwb2, BOOL bNewFrame, REFGUID guidSearch) { VARIANT varBand; HRESULT hr = InitBSTRVariantFromGUID(&varBand, CLSID_FileSearchBand); if (SUCCEEDED(hr)) { // Retrieve the FileSearchBand's unknown from the browser frame as a VT_UNKNOWN property; // (FileSearchBand initialized and this when he was created and hosted.) VARIANT varFsb; hr = pwb2->GetProperty(varBand.bstrVal, &varFsb); if (SUCCEEDED(hr)) { IFileSearchBand* pfsb; if (SUCCEEDED(QueryInterfaceVariant(varFsb, IID_PPV_ARG(IFileSearchBand, &pfsb)))) { // Assign the correct search type to the band VARIANT varSearchID; if (SUCCEEDED(InitBSTRVariantFromGUID(&varSearchID, guidSearch))) { VARIANT varNil = {0}; VARIANT_BOOL bNavToResults = bNewFrame ? VARIANT_TRUE : VARIANT_FALSE ; pfsb->SetSearchParameters(&varSearchID.bstrVal, bNavToResults, &varNil, &varNil); VariantClear(&varSearchID); } pfsb->Release(); } VariantClear(&varFsb); } VariantClear(&varBand); } return hr; } STDMETHODIMP CShellSearchExt::GetCommandString(UINT_PTR idCmd, UINT wFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax) { return E_NOTIMPL; } STDMETHODIMP CShellSearchExt::SetSite(IUnknown *pUnkSite) { IUnknown_Set(&_pSite, pUnkSite); return S_OK; } STDMETHODIMP CShellSearchExt::GetSite(REFIID riid, void **ppvSite) { if (_pSite) return _pSite->QueryInterface(riid, ppvSite); *ppvSite = NULL; return E_NOINTERFACE; } CShellSearchExt::CShellSearchExt() : _cRef(1), _pSite(NULL) { } CShellSearchExt::~CShellSearchExt() { ATOMICRELEASE(_pSite); } class CWebSearchExt : public CShellSearchExt { public: CWebSearchExt(); private: virtual BOOL _GetSearchUrls(GUID *pguidSearch, LPTSTR pszUrl, DWORD cch, LPTSTR pszUrlNavNew, DWORD cchNavNew, BOOL *pfRunInProcess); }; STDAPI CWebSearchExt_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CWebSearchExt* pwse; pwse = new CWebSearchExt(); if (pwse) { *ppunk = SAFECAST(pwse, IContextMenu*); return S_OK; } else { *ppunk = NULL; return E_OUTOFMEMORY; } } CWebSearchExt::CWebSearchExt() : CShellSearchExt() { } BOOL CWebSearchExt::_GetSearchUrls(GUID *pguidSearch, LPTSTR pszUrl, DWORD cch, LPTSTR pszUrlNavNew, DWORD cchNavNew, BOOL *pfRunInProcess) { // Currently does not support NavNew, can be extended later if desired, likewise for RunInProcess... *pfRunInProcess = FALSE; if (pszUrlNavNew && cchNavNew) *pszUrlNavNew = 0; return GetDefaultInternetSearchUrl(pszUrl, cch, TRUE); }