#include "priv.h" #include "resource.h" #include "hlframe.h" #include "bindcb.h" #include "winlist.h" #include "iface.h" #include "shdocfl.h" #include #include #define DM_SHELLEXECOBJECT 0x80000000 // flags for SHDVID_DOCFAMILYCHARSET communication #define DFC_URLCHARSET 1 #ifdef FEATURE_PICS #include "dochost.h" /* for IID_IsPicsBrowser */ #endif #ifdef DEBUG extern DWORD g_dwPerf; #endif #define DM_HLINKTRACE DM_TRACE #define DM_WEBCHECKDRT 0 #define JMPMSG(psz, psz2) TraceMsg(0, "shlf TR-CDOV::%s %s", psz, psz2) #define JMPMSG2(psz, x) TraceMsg(0, "shlf TR-CDOV::%s %x", psz, x) #define DOFMSG(psz) TraceMsg(0, "shlf TR-DOF::%s", psz) #define DOFMSG2(psz, x) TraceMsg(0, "shlf TR-DOF::%s %x", psz, x) #define URLMSG(psz) TraceMsg(0, "shlf TR-DOF::%s", psz) #define URLMSG2(psz, x) TraceMsg(0, "shlf TR-DOF::%s %x", psz, x) #define URLMSG3(psz, x, y) TraceMsg(0, "shlf TR-DOF::%s %x %x", psz, x, y) #define BSCMSG(psz, i, j) TraceMsg(0, "shlf TR-BSC::%s %x %x", psz, i, j) #define BSCMSG3(psz, i, j, k) TraceMsg(0, "shlf TR-BSC::%s %x %x %x", psz, i, j, k) #define BSCMSGS(psz, sz) TraceMsg(0, "shlf TR-BSC::%s %s", psz, sz) #define OIPSMSG(psz) TraceMsg(0, "shlf TR-OIPS::%s", psz) #define OIPSMSG3(psz, sz, p) TraceMsg(0, "shlf TR-OIPS::%s %s,%x", psz, sz,p) #define REFMSG0(psz) TraceMsg(0, "shlf TR-CDOV::%s", psz) #define REFMSG(psz, cRef) TraceMsg(0, "shlf TR-CDOV::%s new _cRef==%d", psz, cRef) #define REFMSG2(psz, if, cRef) TraceMsg(0, "shlf TR-CDOV::%s(%s) new _cRef==%d", psz, if, cRef) #define VIEWMSG(psz) TraceMsg(0, "shlf TR CDOV::%s", psz) #define VIEWMSG2(psz,xx) TraceMsg(0, "shlf TR CDOV::%s %x", psz,xx) #define CACHEMSG(psz, d) TraceMsg(0, "shlf TR CDocObjectCtx::%s %d", psz, d) #define HFRMMSG(psz) TraceMsg(TF_SHDNAVIGATE, "shlf HFRM::%s", psz) #define HFRMMSG2(psz, x, y) TraceMsg(TF_SHDNAVIGATE, "shlf HFRM::%s %x %x", psz, x, y) #define MNKMSG(psz, psz2) TraceMsg(0, "shlf MNK::%s (%s)", psz, psz2) #define SERVMSG(psz, x, y) TraceMsg(0, "shlf SERV::%s %x %x", psz, x, y) #define KEY_BINDCONTEXTPARAM _T("BIND_CONTEXT_PARAM") #define SZ_DWNBINDINFO_OBJECTPARAM _T("__DWNBINDINFO") BOOL g_fHlinkDLLLoaded = FALSE; // must be per-process STDAPI HlinkFrameNavigate(DWORD grfHLNF, IBindCtx *pbc, IBindStatusCallback *pibsc, IHlink* pihlNavigate, IHlinkBrowseContext *pihlbc); STDAPI HlinkFrameNavigateNHL(DWORD grfHLNF, IBindCtx *pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetFrame, LPCWSTR pszUrl, LPCWSTR pszLocation); // IHlinkFrame members HRESULT CIEFrameAuto::SetBrowseContext(IHlinkBrowseContext *pihlbc) { if (pihlbc) pihlbc->AddRef(); if (_phlbc) { if (_dwRegHLBC) { _phlbc->Revoke(_dwRegHLBC); _dwRegHLBC = 0; } _phlbc->Release(); } _phlbc = pihlbc; return NOERROR; } HRESULT CIEFrameAuto::GetBrowseContext(IHlinkBrowseContext **ppihlbc) { TraceMsg(0, "shlf TR ::GetBrowseContext called"); *ppihlbc = _phlbc; if (_phlbc) { _phlbc->AddRef(); return S_OK; } return E_FAIL; } void CIEFrameAuto::_SetPendingNavigateContext(IBindCtx *pbc, IBindStatusCallback *pibsc) { if (_pbscPending) { _pbscPending->Release(); _pbscPending = NULL; } if (_pbcPending) { _pbcPending->Release(); _pbcPending = NULL; } if (pibsc) { _pbscPending = pibsc; _pbscPending->AddRef(); } if (pbc) { // as long as we are cacheing the pending BindCtx, if it specifies // a shortcut URL we need to cache that too. (IE:98431) IUnknown * pUnk = NULL; IHtmlLoadOptions * pHtmlLoadOptions = NULL; _pbcPending = pbc; _pbcPending->AddRef(); pbc->GetObjectParam(_T("__HTMLLOADOPTIONS"), &pUnk); if (pUnk) { pUnk->QueryInterface(IID_IHtmlLoadOptions, (void **) &pHtmlLoadOptions); if (pHtmlLoadOptions) { TCHAR achCacheFile[MAX_PATH+1]; ULONG cchCacheFile = ARRAYSIZE(achCacheFile)-1; memset(&achCacheFile, 0, (cchCacheFile+1)*sizeof(TCHAR) ); // now determine if this is a shortcut-initiated load pHtmlLoadOptions->QueryOption(HTMLLOADOPTION_INETSHORTCUTPATH, &achCacheFile, &cchCacheFile); if (_pwszShortcutPathPending) LocalFree(_pwszShortcutPathPending); _pwszShortcutPathPending = StrDup(achCacheFile); pHtmlLoadOptions->Release(); } pUnk->Release(); } } } // // NavigateContext is a set of parameters passed from one CIEFrameAuto // to another. // void CIEFrameAuto::_ActivatePendingNavigateContext() { if (_pbsc) { _pbsc->Release(); _pbsc = NULL; } if (_pbc) { _pbc->Release(); _pbc = NULL; } if (_pwszShortcutPath) { LocalFree(_pwszShortcutPath); _pwszShortcutPath = NULL; } if (_pbscPending) { _pbsc = _pbscPending; _pbscPending = NULL; } if (_pbcPending) { _pbc = _pbcPending; _pbcPending = NULL; } if (_pwszShortcutPathPending) { _pwszShortcutPath = _pwszShortcutPathPending; _pwszShortcutPathPending = NULL; } } // Called to guarantee a newly created HLinkFrame's window is // visible after the navigate. HRESULT ShowHlinkFrameWindow(IUnknown *pUnkTargetHlinkFrame) { IWebBrowserApp* pdie; HRESULT hres = pUnkTargetHlinkFrame->QueryInterface(IID_PPV_ARG(IWebBrowserApp, &pdie)); if (SUCCEEDED(hres)) { pdie->put_Visible(TRUE); pdie->Release(); } return hres; } HRESULT CIEFrameAuto::_NavigateMagnum(DWORD grfHLNF, IBindCtx *pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetName, LPCWSTR pszUrl, LPCWSTR pszLocation, IHlink *pihlNavigate, IMoniker *pmkTarget) { HRESULT hres = NOERROR; HFRMMSG2("Navigate called", grfHLNF, pihlNavigate); BOOL fNavigateForReal = pszUrl || (pihlNavigate && (pihlNavigate != (IHlink*)-1)); _fSuppressHistory = _psbProxy != _psb; // no history for search band, etc _fSuppressSelect = _psbProxy != _psb; // no need to record select pidl if (grfHLNF != (DWORD)-1) { if (SHHLNF_WRITENOHISTORY & grfHLNF) { _fSuppressHistory = TRUE; } if (SHHLNF_NOAUTOSELECT & grfHLNF) { _fSuppressSelect = TRUE; } } if (pbc == NULL && pibsc == NULL && pihlNavigate == NULL && pszUrl == NULL) { // // This is a private interface so that mshtml can do navigation // if it is hosted by the shell. When IHlinkBrowseContext is implemented // in the shell this special code can be removed and the associated // code in mshtml that calls Navigate with these special parameters // can be removed so that it just goes through the // IHlinkBrowseContext->SetCurrentHlink interface. // // We also use this private mechanism to release the navigation // context with grfHLNF==0. // switch (grfHLNF&~(SHHLNF_WRITENOHISTORY|SHHLNF_NOAUTOSELECT)) { case HLNF_NAVIGATINGBACK: hres = _BrowseObject(PIDL_LOCALHISTORY, SBSP_SAMEBROWSER|SBSP_NAVIGATEBACK); break; case HLNF_NAVIGATINGFORWARD: hres = _BrowseObject(PIDL_LOCALHISTORY, SBSP_SAMEBROWSER|SBSP_NAVIGATEFORWARD); break; case 0: _ActivatePendingNavigateContext(); break; default: hres = E_INVALIDARG; break; } return hres; } #ifdef FEATURE_PICS /* As part of checking ratings, the PICS code will silently download the * root document of a site to look for rating labels in it. If that's a * frameset page, Trident will create OCXs for the subframes and try to * navigate them, which will invoke ratings checks for them and cause * infinite recursion. So here we check to see if our top-level browser * is really this PICS download, and if it is, we don't do any navigation. */ IUnknown *punkPics; if (SUCCEEDED(QueryService(SID_STopLevelBrowser, IID_IsPicsBrowser, (void **)&punkPics))) { punkPics->Release(); return S_OK; } #endif // // If we've got this call while we are busy (EnableModeless is FALSE), // we should just bail here (instead of doing somthing and let _JumpTo // call fail. // // This can happen if someone has a window.location="foobar.htm" in their unload // event handler. if (fNavigateForReal && !(grfHLNF & HLNF_OPENINNEWWINDOW)) { // If _pbs is NULL, it is bad news; we can't navigate. // An allowable reason for this condition is that someone has called CIEFrameAuto::Quit() // and we are in the process of shutting down. // if (_pbs == NULL) { if (_fQuitInProgress) { TraceMsg(TF_WARNING, "CIEFrameAuto::_NavigateMagnum quitting due to browser closing."); return S_OK; } TraceMsg(TF_WARNING, "CIEFrameAuto::_NavigateMagnum _pbs is NULL, but we are not shutting down."); return E_FAIL; } // If we have a _pbs but the browser says that it can't navigate now, then return S_FALSE. // else if (_pbs->CanNavigateNow() != S_OK) { TraceMsg(TF_WARNING, "CIEFrameAuto::Navigate CanNavigateNow returns non S_OK, bail out."); return S_FALSE; } } // // This Navigate method is not re-entrant (because of _SetPendingNavigateContext) // if (_fBusy) { TraceMsg(DM_WARNING, "CIEA::Navigate re-entered. Returning E_FAIL"); return E_FAIL; } _fBusy = TRUE; // // HACK: To let Webcheck DRT go. // if (fNavigateForReal && !(grfHLNF & HLNF_OPENINNEWWINDOW)) { TraceMsg(DM_WEBCHECKDRT, "CIFA::Navigate calling _CancelPendingNavigation"); VARIANT var = { 0 }; var.vt = VT_I4; var.lVal = TRUE; // synchronous _CancelPendingNavigation(&var); } if (pszUrl && SHRestricted2(REST_NOFILEURL, NULL, 0) && PathIsFilePath(pszUrl)) { TCHAR szPath[MAX_URL_STRING]; SHUnicodeToTChar(pszUrl, szPath, ARRAYSIZE(szPath)); MLShellMessageBox(NULL, MAKEINTRESOURCE(IDS_SHURL_ERR_PARSE_NOTALLOWED), szPath, MB_OK | MB_ICONERROR, szPath); _fBusy = FALSE; return E_ACCESSDENIED; } _SetPendingNavigateContext(pbc, pibsc); #ifdef DEBUG g_dwPerf = GetCurrentTime(); #endif if (pihlNavigate == (IHlink*)-1) { // // HACK: -1 means "release the navigation state". // CDocObjectHost::_CancelPendingNavigation is the only caller. // It Exec's SBCMDID_CANCELNAVIGATION which will asynchronously // cancel the pending navigation. Therefore, we no longer need // to call _CancelPendingNavigation here. (SatoNa) // // _CancelPendingNavigation(); } else if (pihlNavigate || pszUrl) { hres = S_OK; if (SUCCEEDED(hres)) { if ((grfHLNF & HLNF_EXTERNALNAVIGATE) && (grfHLNF & HLNF_NAVIGATINGBACK)) GoBack(); else if ((grfHLNF & HLNF_EXTERNALNAVIGATE) && (grfHLNF & HLNF_NAVIGATINGFORWARD)) GoForward(); else { hres = _JumpTo(pbc,(LPWSTR) pszLocation, grfHLNF, pibsc, pihlNavigate, pszTargetName, pszUrl); if (FAILED(hres)) { TraceMsg(DM_ERROR, "IEAuto::Navigate _JumpTo failed %x", hres); } } if (pihlNavigate) { // // Hopefully, we'll come up with a clean solution to // solve this problem nicely. I made a proposal to NatBro/SriniK // that CreateHlink will CoCreateInstance IHlink so that OLE // LoadLibrary it and maintains it as an InProc server. (SatoNa) // // HACK: If we AddRef to IHlink, we need to make it sure that // HLINK.DLL is stay loaded even though the DocObject InProc // server (that implicitly links to HLINK.DLL) is unloaded. // if (!g_fHlinkDLLLoaded) { LoadLibrary(TEXT("hlink.dll")); g_fHlinkDLLLoaded = TRUE; } } } else { TraceMsg(DM_ERROR, "CIEFA::Nav phl->GetMonRef failed %x", hres); } } _fBusy = FALSE; HFRMMSG2("Navigate returning", hres, 0); if (SUCCEEDED(hres) && (pihlNavigate != (IHlink*)-1)) { if (grfHLNF & HLNF_EXTERNALNAVIGATE) { HWND hwndFrame; _psb->GetWindow(&hwndFrame); if (_phlbc) { // if we have a browse context, then we're navigating from it and // we should size our window to match it. HLBWINFO hlbwi; hlbwi.cbSize = SIZEOF(hlbwi); if (SUCCEEDED(_phlbc->GetBrowseWindowInfo(&hlbwi)) && (hlbwi.grfHLBWIF & HLBWIF_HASFRAMEWNDINFO)) { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hwndFrame, &wp); wp.rcNormalPosition = hlbwi.rcFramePos; wp.showCmd = (hlbwi.grfHLBWIF & HLBWIF_FRAMEWNDMAXIMIZED) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL; // This is not broken in AOL because this // is an external navigate (word has cocreateinstance()d // Internet.Explorer and navigated it. // SetWindowPlacement(hwndFrame, &wp); } // Register the hlinkframe interface with the browse context, if it has not already // been registered if (_dwRegHLBC == 0) _phlbc->Register(0, (IHlinkFrame *) this, pmkTarget, &_dwRegHLBC); // add the link to browse context and // REVIEW: need to pass the proper friendly name _phlbc->OnNavigateHlink(grfHLNF, pmkTarget, pszLocation, NULL, NULL); } put_Visible(TRUE); SetForegroundWindow(hwndFrame); } // // According to SriniK, we need to call IHlinkSite::OnNavigationComplete // before returning from IHlinkFrame::Navigate with S_OK. (SatoNa) // if (pihlNavigate) { BOOL fExternal = FALSE; if (_phlbc && _pbs) { ITravelLog* ptl; if (SUCCEEDED(_pbs->GetTravelLog(&ptl))) { if (FAILED(ptl->GetTravelEntry(_pbs, 0, NULL))) { TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag this is external nav. Don't call OnNavigationComplete"); fExternal = TRUE; } else if (SUCCEEDED(ptl->GetTravelEntry(_pbs, TLOG_BACKEXTERNAL, NULL))) { TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag this is external for. Don't call OnNavigationComplete"); fExternal = TRUE; } ptl->Release(); } } // // Don't call OnNavigationComplete if this is an external navigation. // if (!fExternal) { IHlinkSite* pihlSite = NULL; DWORD dwSiteData; HRESULT hresT = pihlNavigate->GetHlinkSite(&pihlSite, &dwSiteData); if (SUCCEEDED(hresT) && pihlSite) { TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag calling OnNavigationComplete"); hresT = pihlSite->OnNavigationComplete(dwSiteData, 0, S_OK, L""); if (FAILED(hresT)) { TraceMsg(DM_ERROR, "CIEFA::Navigat OnNavComplete failed %x", hresT); } pihlSite->Release(); } } } } return hres; } // // HACK - what we really want is a good private marshalled interface - zekel 8-AUG-97 // to the Browser. but for now we will overload the NavigateHack method, // because it is simple and quick for ship. // #define HLNF_REFERRERHACK 0x40000000 HRESULT CIEFrameAuto::_ReferrerHack(LPCWSTR pszUrl) { if (_pbs == NULL) //Make sure we have a IBrowserService. return S_FALSE; LPITEMIDLIST pidl; if (SUCCEEDED(_pbs->IEParseDisplayName(CP_ACP, pszUrl, &pidl))) { ASSERT(pidl); _pbs->SetReferrer(pidl); ILFree(pidl); } return S_OK; } HRESULT CIEFrameAuto::NavigateHack(DWORD grfHLNF, IBindCtx *pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetName, LPCWSTR pszUrl, LPCWSTR pszLocation) { HRESULT hres = E_FAIL; IBindCtx * pBindCtx = pbc; IUnknown * pNotify = NULL; IUnknown * pBindCtxParam = NULL; BOOL fAsyncCalled = FALSE; // Check if we are actually a native frame build... if (pbc) { hres = pbc->GetObjectParam(KEY_BINDCONTEXTPARAM, &pBindCtxParam); } if (SUCCEEDED(hres) && pBindCtxParam) { // NavigateHack can be called multiple times, and we only want to create the // new bind context the first time. Since the ITargetNotify pointer is removed // after the first use, we can check that to make sure. // get and transfer the target notify pointer. hres = pbc->GetObjectParam(TARGET_NOTIFY_OBJECT_NAME, &pNotify); if (SUCCEEDED(hres) && pNotify) { // The call is coming from a native frame build of MSHTML. // We can not use their bind context, create a new one and transfer // parameters. // The old bind context is going to be released by the creator, so do not // make a release call on it. hres = CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, &pBindCtx, 0); if(FAILED(hres)) goto Exit; fAsyncCalled = TRUE; // carry over the ITargetNotify2 pointer. hres = pBindCtx->RegisterObjectParam( TARGET_NOTIFY_OBJECT_NAME, pNotify ); if (FAILED(hres)) goto Exit; pNotify->Release(); pNotify = NULL; // carry over the bind context parameter. hres = pBindCtx->RegisterObjectParam( KEY_BINDCONTEXTPARAM, pBindCtxParam ); if (FAILED(hres)) goto Exit; { IUnknown * pDwnBindInfo = NULL; if (SUCCEEDED(pbc->GetObjectParam(SZ_DWNBINDINFO_OBJECTPARAM, &pDwnBindInfo)) && pDwnBindInfo) { pBindCtx->RegisterObjectParam(SZ_DWNBINDINFO_OBJECTPARAM, pDwnBindInfo); pDwnBindInfo->Release(); } } } pBindCtxParam->Release(); pBindCtxParam = NULL; } if (IsFlagSet(grfHLNF, HLNF_REFERRERHACK)) hres = _ReferrerHack(pszUrl); else hres = _NavigateMagnum(grfHLNF, pBindCtx, pibsc, pszTargetName, pszUrl, pszLocation, NULL, NULL); Exit: SAFERELEASE(pNotify); SAFERELEASE(pBindCtxParam); // If the call failed anywhere, we can not be sure the new document // will free the object parameter that is in the bind context // we have created in this function. if (FAILED(hres) && pBindCtx) { // we don't want to change the return code here. pBindCtx->RevokeObjectParam(KEY_BINDCONTEXTPARAM); pBindCtx->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME); } if (fAsyncCalled) pBindCtx->Release(); return hres; } // passing NULL pibsc and pbc will make be like "ReleaseNavigationState" // passing -1 for pihlNavigate will cancel pending navigation HRESULT CIEFrameAuto::Navigate(DWORD grfHLNF, IBindCtx *pbc, IBindStatusCallback *pibsc, IHlink *pihlNavigate) { IMoniker* pmkTarget = NULL; LPOLESTR pwszDisplayName = NULL; LPOLESTR pwszLocation = NULL; LPOLESTR pwszFrameName = NULL; HRESULT hres = S_OK; if (pihlNavigate && ((IHlink *)-1) != pihlNavigate) { pihlNavigate->GetTargetFrameName(&pwszFrameName); // // Note that we are discarding "relative" portion. // hres = pihlNavigate->GetMonikerReference(HLINKGETREF_ABSOLUTE, &pmkTarget, &pwszLocation); HFRMMSG2("Navigate pihl->GetMonRef returned", hres, pmkTarget); if (SUCCEEDED(hres)) { IBindCtx* pbcLocal; if (pbc) { pbcLocal = pbc; pbcLocal->AddRef(); } else { hres = CreateBindCtx(0, &pbcLocal); } if (SUCCEEDED(hres)) { hres = pmkTarget->GetDisplayName(pbcLocal, NULL, &pwszDisplayName); pbcLocal->Release(); } } } if (SUCCEEDED(hres)) { hres = _NavigateMagnum(grfHLNF, pbc, pibsc, pwszFrameName, pwszDisplayName, pwszLocation, pihlNavigate, pmkTarget); } if (pwszFrameName) { OleFree(pwszFrameName); } if (pwszDisplayName) { OleFree(pwszDisplayName); } if (pwszLocation) { OleFree(pwszLocation); } if (pmkTarget) { pmkTarget->Release(); } return hres; } HRESULT CIEFrameAuto::OnNavigate(DWORD grfHLNF, /* [unique][in] */ IMoniker *pimkTarget, /* [unique][in] */ LPCWSTR pwzLocation, /* [unique][in] */ LPCWSTR pwzFriendlyName, /* [in] */ DWORD dwreserved) { TraceMsg(0, "shlf TR ::OnNavigate called"); return S_OK; } void CIEFrameAuto::_CancelPendingNavigation(VARIANTARG* pvar) { TraceMsg(0, "shd TR _CancelPendingNavigation called"); if (_pmsc) { TraceMsg(0, "shd TR _CancelPendingNavigation calling _pmsc->Exec"); _pmsc->Exec(&CGID_Explorer, SBCMDID_CANCELNAVIGATION, 0, pvar, NULL); } } // *** ITargetNotify *** void CIEFrameAuto::_HandleOpenOptions( IUnknown * pUnkDestination, ITargetNotify * ptgnNotify) { HRESULT hres = S_OK; ITargetNotify2 * ptgnNotify2 = NULL; if (!pUnkDestination || !ptgnNotify) return; if (SUCCEEDED(ptgnNotify->QueryInterface( IID_ITargetNotify2, (void **)&ptgnNotify2))) { BSTR bstrOptions = NULL; ASSERT(ptgnNotify2); // Apply the options only if the initator of the navigation // asks for it. if (S_OK == ptgnNotify2->GetOptionString(&bstrOptions)) { _omwin._OpenOptions.ReInitialize(); if (bstrOptions) { _omwin._ParseOptionString(bstrOptions, ptgnNotify2); // We are done with the options string, release it SysFreeString(bstrOptions); } // Apply the options now. // IWebBrowser2 * pNewIE; if (SUCCEEDED(pUnkDestination->QueryInterface(IID_PPV_ARG(IWebBrowser2, &pNewIE)))) { _omwin._ApplyOpenOptions(pNewIE); pNewIE->Release(); } } ptgnNotify2->Release(); } } HRESULT CIEFrameAuto::OnCreate(IUnknown *pUnkDestination, ULONG cbCookie) { HRESULT hres = S_OK; if (cbCookie == (ULONG)_cbCookie && _ptgnNotify) { _HandleOpenOptions( pUnkDestination, _ptgnNotify); hres = _ptgnNotify->OnCreate(pUnkDestination, cbCookie); SAFERELEASE(_ptgnNotify); } return hres; } HRESULT CIEFrameAuto::OnReuse(IUnknown *pUnkDestination) { return S_OK; } #define NOTIFY_WAIT_TIMEOUT (60000) // chrisfra 10/10/96: do we need EnableModeless(FALSE)/(TRUE) around // our little loop, or is the busy flag (which is set) sufficient? HRESULT CIEFrameAuto::_WaitForNotify() { if (_ptgnNotify && IsInternetExplorerApp()) { DWORD dwObject, msWait, msStart = GetTickCount(); goto DOPEEK; while (_ptgnNotify) { // NB We need to let the run dialog become active so we have to half handle sent // messages but we don't want to handle any input events or we'll swallow the // type-ahead. msWait = GetTickCount(); if (msWait - msStart > NOTIFY_WAIT_TIMEOUT) break; msWait = NOTIFY_WAIT_TIMEOUT - (msWait - msStart); dwObject = MsgWaitForMultipleObjects(0, NULL, FALSE, msWait, QS_ALLINPUT); // Are we done waiting? switch (dwObject) { case WAIT_FAILED: break; case WAIT_OBJECT_0: DOPEEK: // got a message, dispatch it and wait again MSG msg; while (PeekMessage(&msg, NULL,0, 0, PM_REMOVE)) { DispatchMessage(&msg); if (_ptgnNotify == NULL || ((GetTickCount() - msStart) > NOTIFY_WAIT_TIMEOUT)) break; } break; } } } return S_OK; } HRESULT CIEFrameAuto::_RegisterCallback(TCHAR *szFrameName, ITargetNotify *ptgnNotify) { HRESULT hr = S_OK; SAFERELEASE(_ptgnNotify); _fRegistered = 0; if (ptgnNotify) { IDispatch *pid; hr = QueryInterface(IID_PPV_ARG(IDispatch, &pid)); if (SUCCEEDED(hr)) { hr = E_FAIL; IShellWindows *psw = WinList_GetShellWindows(TRUE); if (psw != NULL) { long cbCookie; hr = psw->Register(pid, NULL, SWC_CALLBACK, &cbCookie); if (SUCCEEDED(hr)) { TCHAR szCookie[25]; // big enough for "_[cbCookie]" int slenCookie; int slenName; int slenMin; _cbCookie = cbCookie; _fRegistered = 1; _ptgnNotify = ptgnNotify; _ptgnNotify->AddRef(); // prepend unique id to target -- tells created WebBrowserOC to // register the remainder (if any) as frame name and to perform // callbacks on all registered callbacks wnsprintf(szCookie, ARRAYSIZE(szCookie), TEXT("_[%ld]"), cbCookie); slenCookie = lstrlen(szCookie); slenName = lstrlen(szFrameName); slenMin = min((int)MAX_URL_STRING-slenCookie,slenName); MoveMemory(&szFrameName[slenCookie], szFrameName, CbFromCch(slenMin)); szFrameName[slenCookie+slenMin] = 0; CopyMemory(szFrameName, szCookie, CbFromCch(slenCookie)); } psw->Release(); } pid->Release(); } } return hr; } HRESULT CIEFrameAuto::_RevokeCallback() { HRESULT hr = S_OK; if (_fRegistered) { IShellWindows *psw = WinList_GetShellWindows(TRUE); if (psw != NULL) { hr = psw->Revoke(_cbCookie); psw->Release(); } } SAFERELEASE(_ptgnNotify); _fRegistered = 0; return hr; } // // HACK - what we really want is a good private marshalled interface - zekel 8-AUG-97 // to the Browser. but for now we will overload the NavigateHack method, // because it is simple and quick for ship. // void CIEFrameAuto::_SetReferrer(ITargetFramePriv *ptgfp) { LPITEMIDLIST pidl; WCHAR szUrl[MAX_URL_STRING]; ASSERT(ptgfp); //Make sure we have a IBrowserService. if (_psb && SUCCEEDED(_pbs->GetPidl(&pidl))) { if (SUCCEEDED(_pbs->IEGetDisplayName(pidl, szUrl, SHGDN_FORPARSING))) ptgfp->NavigateHack(HLNF_REFERRERHACK, NULL, NULL, NULL, szUrl, NULL); ILFree(pidl); } } HRESULT CIEFrameAuto::_JumpTo(IBindCtx *pbc, LPWSTR pszLocation, DWORD grfHLNF, IBindStatusCallback *pibsc, IHlink *pihlNavigate, LPCWSTR pszFrameName, LPCWSTR pszUrl) { LPITEMIDLIST pidl = NULL; HRESULT hres; ITargetNotify *ptgnNotify = NULL; IUnknown *punkNotify = NULL; IUnknown *punkThis = NULL; UINT uiCP = CP_ACP; // Get the current document codepage from Trident and use it for url string conversion if necessary. if (!(grfHLNF & HLNF_ALLOW_AUTONAVIGATE) && _pmsc) { VARIANT varOut = { 0 }; VARIANT varIn = { 0 }; varIn.vt = VT_I4; varIn.lVal = DFC_URLCHARSET; // we want the doc's url charset if (SUCCEEDED(_pmsc->Exec(&CGID_ShellDocView, SHDVID_DOCFAMILYCHARSET, 0, &varIn, &varOut))) uiCP = (UINT)varOut.lVal; } // Note that we are simply passing the pidl to ISB::BrowseObject, // assuming that new shell32.dll allows us to bind to DocObject // documents. // DWORD flags = (grfHLNF & HLNF_OPENINNEWWINDOW) ? (SBSP_NEWBROWSER | SBSP_ABSOLUTE | SBSP_INITIATEDBYHLINKFRAME) : (SBSP_SAMEBROWSER | SBSP_ABSOLUTE | SBSP_INITIATEDBYHLINKFRAME); flags |= ((grfHLNF & HLNF_ALLOW_AUTONAVIGATE) ? (SBSP_ALLOW_AUTONAVIGATE) : 0); flags |= ((grfHLNF & SHHLNF_WRITENOHISTORY) ? (SBSP_WRITENOHISTORY) : 0); flags |= ((grfHLNF & SHHLNF_NOAUTOSELECT) ? (SBSP_NOAUTOSELECT) : 0); if (pbc && SUCCEEDED(pbc->GetObjectParam(TARGET_NOTIFY_OBJECT_NAME, &punkNotify))) { if (FAILED(punkNotify->QueryInterface(IID_PPV_ARG(ITargetNotify, &ptgnNotify)))) ptgnNotify = NULL; punkNotify->Release(); QueryInterface(IID_PPV_ARG(IUnknown, &punkThis)); } if (grfHLNF & HLNF_CREATENOHISTORY) flags |= SBSP_REDIRECT; if (flags & SBSP_NEWBROWSER) { TCHAR *pszHeaders = NULL; BYTE *pPostData = NULL; DWORD cbPostData = 0; TCHAR szFrameName[MAX_URL_STRING+1]; STGMEDIUM stgPostData = { TYMED_NULL, NULL, NULL }; //Qfe:1478 If restricted to open in new window, return failure. if ((grfHLNF & HLNF_OPENINNEWWINDOW) && SHIsRestricted2W(_hwnd, REST_NoOpeninNewWnd, NULL, 0)) { SAFERELEASE(punkThis); return E_ACCESSDENIED; } szFrameName[0] = 0; // Here is where if we are doing a new window we must // extract frame, post etc and append to pidl. These must // be done in the following order (to match extraction code): // URLID_FRAMENAME,URLID_POSTDATA,URLID_HEADERS if (pszFrameName) { SHUnicodeToTChar(pszFrameName, szFrameName, ARRAYSIZE(szFrameName)); } if (pibsc) { GetHeadersAndPostData(pibsc,&pszHeaders,&stgPostData,&cbPostData, NULL); if (stgPostData.tymed == TYMED_HGLOBAL) { pPostData = (LPBYTE) stgPostData.hGlobal; } } hres = _PidlFromUrlEtc(uiCP, pszUrl, pszLocation, &pidl); HFRMMSG2("_JumpTo _PidlFromUrlEtc returned", hres, pidl); if (SUCCEEDED(hres)) { IUnknown* punkNewWindow = NULL; BOOL fCancel = FALSE; // The NewWindow2 event may return the window for us. FireEvent_NewWindow2(_GetOuter(), &punkNewWindow, &fCancel); if (!fCancel) { BOOL fProcessed = FALSE; // We might need the old NewWindow event... if (!punkNewWindow) { _RegisterCallback(szFrameName, ptgnNotify); // fire an event to indicate a new window needs to be created // to allow a container to handle it itself if it wants // since we may be aggregated, QI our parent // Yet another Compuserve workaround (IE 60688): // If the target frame name is "_blank", Compuserve will pass that name // in to the Navigate call of the new window. We would then create a new window // (which would fire this event) causing a loop. Break the recursion by sending // an empty string for the frame name. HWND hwnd = _GetHWND(); if (hwnd) { FireEvent_NewWindow(_GetOuter(), hwnd, pidl,pszLocation,0, StrCmpI(szFrameName, TEXT("_blank")) ? szFrameName : TEXT(""), // Target frame name pPostData,cbPostData,pszHeaders,&fProcessed); } } if (!fProcessed) { if (!punkNewWindow) { #ifdef INCLUDE_BUSTED_OC_QI IUnknown* pdvb = NULL; #endif _RevokeCallback(); #ifdef INCLUDE_BUSTED_OC_QI // For some unidentifiable reason the old code did NOT // create a new window if we were hosted in the WebBrowserOC. // mikesh/cheechew/jeremys/chrisfra don't know why this happens. // Who knows what app will break if we change this... // (Note: IDefViewBrowser is a CWebBrowseSB only interface) // // NOTE: chrisfra 3/11/97, this code breaks open a // new window for a non-existent target, when in // desktop component or browser band fCancel = !(_psbTop && FAILED(_psbTop->QueryInterface(IID_PPV_ARG(IDefViewBrowser, &pdvb)))); if (pdvb) pdvb->Release(); #endif } // what we really want to do is just hand this off to // _psbTop->BrowseObject and let it (CWebBrowserSB or CShellBrowser) // decide whether to use HlinkFrameNavigate or not, but if we // do that, then we lose the grfHLNF and pihlNavigate. // So put that logic here... // if (!fCancel) { hres = CreateTargetFrame(pszFrameName, &punkNewWindow); if (SUCCEEDED(hres)) { // Notify ptgnNotify, then release and remove from bindctx if (ptgnNotify) { _HandleOpenOptions( punkNewWindow, ptgnNotify); ptgnNotify->OnCreate(punkNewWindow, GetTickCount()); ptgnNotify->Release(); ptgnNotify = NULL; pbc->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME); } LPHLINKFRAME phf; hres = punkNewWindow->QueryInterface(IID_PPV_ARG(IHlinkFrame, &phf)); if (SUCCEEDED(hres)) { ITargetFramePriv * ptgfp; if (NULL == pihlNavigate) { hres = punkNewWindow->QueryInterface(IID_PPV_ARG(ITargetFramePriv, &ptgfp)); } if (SUCCEEDED(hres)) { if (pihlNavigate) { hres = phf->Navigate(grfHLNF & ~HLNF_OPENINNEWWINDOW, pbc, pibsc, pihlNavigate); } else { // HACK - see this methods comments _SetReferrer(ptgfp); hres = ptgfp->NavigateHack(grfHLNF & ~HLNF_OPENINNEWWINDOW, pbc, pibsc, NULL, pszUrl, pszLocation); } if (FAILED(hres)) { TraceMsg(DM_ERROR, "CIEFA::_JumpTo marshalled IHlinkFrame::Navigate failed %x", hres); } ShowHlinkFrameWindow(punkNewWindow); if (NULL == pihlNavigate) { ptgfp->Release(); } if(SUCCEEDED(hres) && pibsc) { _SetPendingNavigateContext(NULL, NULL); } } phf->Release(); } } } else { // // If NEWBROWSER is specified when there is no top level // browser, we should ask IE/Shell to do browsing. // We don't pass HLNF_OPENINNEWWINDOW in this case. // // Notify object doing navigation that we are the object implementing IWebBrowserApp if (ptgnNotify) ptgnNotify->OnReuse(punkThis); if (pihlNavigate) { hres = HlinkFrameNavigate(grfHLNF & ~HLNF_OPENINNEWWINDOW, NULL, NULL, pihlNavigate, NULL); } else { hres = HlinkFrameNavigateNHL(grfHLNF & ~HLNF_OPENINNEWWINDOW, NULL, NULL, NULL, pszUrl, pszLocation); } } } else { // Oldstyle AOL or other 3rd Party, wait for registration of // WebBrowserOC, which calls us back on _ptgnNotify _WaitForNotify(); // We timed out the window create, notify caller if (_ptgnNotify) _ptgnNotify->OnCreate(NULL, 0); _RevokeCallback(); } } if (punkNewWindow) punkNewWindow->Release(); } else { TraceMsg(DM_ERROR, "IEAuto::_JumpTo _PidlFromUrlEtc (1) failed %x", hres); } if (pszHeaders) { LocalFree(pszHeaders); pszHeaders = NULL; } if (stgPostData.tymed != TYMED_NULL) { ReleaseStgMedium(&stgPostData); } } else { // Notify object doing navigation that we are the object implementing IWebBrowserApp if (ptgnNotify) ptgnNotify->OnReuse(punkThis); hres = _PidlFromUrlEtc(uiCP, pszUrl, pszLocation, &pidl); if (SUCCEEDED(hres)) { hres = _psb->BrowseObject(pidl, flags); } else { TraceMsg(DM_ERROR, "IEAuto::_JumpTo _PidlFromUrlEtc (2) failed %x", hres); } } if (pidl) { HFRMMSG2("_JumpTo _psb->BrowseObject returned", hres, 0); ILFree(pidl); } if (ptgnNotify) { ptgnNotify->Release(); pbc->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME); } SAFERELEASE(punkThis); return hres; } HRESULT CIEFrameAuto::QueryService(REFGUID guidService, REFIID riid, void ** ppvObj) { *ppvObj = NULL; // WARNING: Note that we are not following the strict semantics of // ISP::QueryService. It is, however, OK because this (the fact that // IHlinkFrame support IServiceProvider) is not public. if (IsEqualIID(guidService, SID_SOmWindow)) { return _omwin.QueryInterface(riid, ppvObj); } else if (IsEqualIID(guidService, IID_IHlinkFrame)) { SERVMSG("QueryService called", _pbc, _pbsc); if (IsEqualIID(riid, IID_IBindCtx) && _pbc) { *ppvObj = _pbc; _pbc->AddRef(); } else if (IsEqualIID(riid, IID_IBindStatusCallback) && _pbsc) { *ppvObj = _pbsc; _pbsc->AddRef(); } else { return QueryInterface(riid, ppvObj); } } else if (IsEqualIID(guidService, SID_PendingBindStatusCallback)) { if (IsEqualIID(riid, IID_IBindStatusCallback) && _pbscPending) { *ppvObj = _pbscPending; _pbscPending->AddRef(); } } else if (_psp) { return _psp->QueryService(guidService, riid, ppvObj); } return *ppvObj ? S_OK : E_FAIL; } HRESULT CIEFrameAuto::Exec( /* [unique][in] */ const GUID *pguidCmdGroup, /* [in] */ DWORD nCmdID, /* [in] */ DWORD nCmdexecopt, /* [unique][in] */ VARIANTARG *pvarargIn, /* [unique][out][in] */ VARIANTARG *pvarargOut) { HRESULT hres = S_OK; if (pguidCmdGroup) { if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup)) { switch(nCmdID) { case SBCMDID_CANCELNAVIGATION: _CancelPendingNavigation(NULL); break; case SBCMDID_SELECTHISTPIDL: case SBCMDID_HISTSFOLDER: if (_poctFrameTop) hres = _poctFrameTop->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); else hres = S_OK; break; case SBCMDID_IESHORTCUT: #ifdef BROWSENEWPROCESS_STRICT // "Nav in new process" has become "Launch in new process", so this is no longer needed // If this is an IE shortcut and browse in a new process is turned on // and we are explorer.exe - we should pass on the request to navigate to // this shortcut. The caller is expected to create a new window/process to // launch this shortcut if (IsBrowseNewProcessAndExplorer()) hres = E_FAIL; else #endif hres = _NavIEShortcut(pvarargIn,pvarargOut); break; case SBCMDID_GETSHORTCUTPATH: if (_pwszShortcutPath && pvarargOut) { pvarargOut->bstrVal = SysAllocString(_pwszShortcutPath); if (pvarargOut->bstrVal) pvarargOut->vt = VT_BSTR; //no need to set hres=S_OK since it is inited already else hres = E_OUTOFMEMORY; } else { if (pvarargOut) pvarargOut->vt = VT_EMPTY; hres = E_FAIL; } break; default: hres = OLECMDERR_E_NOTSUPPORTED; } } else if (IsEqualGUID(CGID_ShortCut, *pguidCmdGroup)) { if (_poctFrameTop) // we must check! hres = _poctFrameTop->Exec(&CGID_ShortCut, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); else hres = OLECMDERR_E_NOTSUPPORTED; } else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup)) { switch (nCmdID) { case SHDVID_DELEGATEWINDOWOM: _omwin.SetDelegationPolicy(V_BOOL(pvarargIn)); break; default: hres = OLECMDERR_E_NOTSUPPORTED; } } else if (IsEqualGUID(CGID_InternetExplorer, *pguidCmdGroup)) // CGID_InternetExplorer are public defined in msiehost.h { switch (nCmdID) { case IECMDID_CLEAR_AUTOCOMPLETE_FOR_FORMS: { if (pvarargIn->vt == VT_I4) { hres = ClearAutoSuggestForForms(V_I4(pvarargIn)); } else hres = E_INVALIDARG; } break; case IECMDID_SETID_AUTOCOMPLETE_FOR_FORMS: { if ((pvarargIn->vt == VT_UI8) || (pvarargIn->vt == VT_I8)) { hres = SetIdAutoSuggestForForms(((GUID *)(&pvarargIn->ullVal)), _omwin.IntelliForms()); } else hres = E_INVALIDARG; } break; default: hres = OLECMDERR_E_NOTSUPPORTED; } } else { hres = OLECMDERR_E_UNKNOWNGROUP; } } else { hres = OLECMDERR_E_UNKNOWNGROUP; } return hres; } BOOL CIEFrameAuto::_fNavigationPending() { // unfortunately, the hyperlink frame doesn't REALLY know when there's // a navigation pending or not because people might not call OnReleaseNavigation. // only the real browser knows. if (_pmsc) { MSOCMD rgCmd; rgCmd.cmdID = SBCMDID_CANCELNAVIGATION; rgCmd.cmdf = 0; _pmsc->QueryStatus(&CGID_Explorer, 1, &rgCmd, NULL); return (rgCmd.cmdf & MSOCMDF_ENABLED); } return FALSE; } HRESULT CIEFrameAuto::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { if (pguidCmdGroup && IsEqualGUID(CGID_Explorer, *pguidCmdGroup)) { for (ULONG i = 0; i < cCmds; i++) { switch (rgCmds[i].cmdID) { case SBCMDID_CANCELNAVIGATION: rgCmds[i].cmdf = _fNavigationPending() ? MSOCMDF_ENABLED : 0; break; case SBCMDID_WRITEHIST: rgCmds[i].cmdf = _fSuppressHistory ? 0:MSOCMDF_ENABLED; break; case SBCMDID_SELECTHISTPIDL: rgCmds[i].cmdf = _fSuppressSelect || !_poctFrameTop ? 0:MSOCMDF_ENABLED; break; default: rgCmds[i].cmdf = 0; break; } } } else { return OLECMDERR_E_UNKNOWNGROUP; } if (pcmdtext) { pcmdtext->cmdtextf = MSOCMDTEXTF_NONE; pcmdtext->cwActual = 0; } return NOERROR; } HRESULT CIEFrameAuto::_PidlFromUrlEtc(UINT uiCP, LPCWSTR pszUrl, LPWSTR pszLocation, LPITEMIDLIST* ppidl) { *ppidl = NULL; // assumes error // ALGORITHM: // - First, we call IEParseDisplayName to generate the pidl // to the specified URL or file name. // - if we have fragment (pszLocation) specified, // we call IEILAppendFragment() to add the hidden fragment id if (_pbs == NULL) //Make sure we have a IBrowserService. return (S_FALSE); HRESULT hr = _pbs->IEParseDisplayName(uiCP, pszUrl, ppidl); // This is ugly, if it's a file path that failed to parse because // it doesn't exist, we want to create a SimpleIDList so we display // a res: navigation failed IFrame instead of the err dlg displayed // in DisplayParseError() below. if (FAILED(hr)) { TCHAR szPath[MAX_PATH]; DWORD cchBuf = ARRAYSIZE(szPath); // If it's a FILE URL, convert it to a path. if (IsFileUrlW(pszUrl) && SUCCEEDED(PathCreateFromUrl(pszUrl, szPath, &cchBuf, 0))) { // That worked, we are done because our buffer is now full. } else { // We now need to copy to the buffer and we assume it's a path. StrCpyN(szPath, pszUrl, ARRAYSIZE(szPath)); } *ppidl = SHSimpleIDListFromPath(szPath); if (*ppidl) hr = S_OK; } if (SUCCEEDED(hr)) { if (pszLocation && *pszLocation) { *ppidl = IEILAppendFragment(*ppidl, pszLocation); hr = *ppidl ? S_OK : E_OUTOFMEMORY; } } else { // // NOTES: This behavior is new in IE4.0. We are adding // this message box based on the request (bug-report) // from Office guys. (SatoNa) // hr = _pbs->DisplayParseError(hr, pszUrl); } return hr; } HRESULT CIEFrameAuto::_NavIEShortcut(VARIANT *pvarIn, VARIANT *pvarargOut) { // need to validate verb and clsid HRESULT hr = E_ACCESSDENIED; READYSTATE ready; BOOL fForceNavigate = pvarargOut ? ((VT_BOOL == pvarargOut->vt ) && (pvarargOut->boolVal)) : FALSE; get_ReadyState(&ready); ASSERT(pvarIn); ASSERT(pvarIn->vt == VT_BSTR); // // we dont want to allow the exec to go through if this window // is busy with something else. we should probably allow // READYSTATE_COMPLETE and READYSTATE_UNINITIALIZED. // if we use READYSTATE_UNINITIALIZED, we need to init the browser // and make it visible and stuff like that. something to the // check that IPersisteHistory->LoadHistory() does in shvocx.cpp. // right now we will only allow COMPLETE. // TraceMsgW(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut entered '%s' ready = %d", this, pvarIn->bstrVal, ready); if (((ready == READYSTATE_COMPLETE || ready == READYSTATE_UNINITIALIZED) || (fForceNavigate)) && S_OK == IUnknown_Exec(_psbTop, &CGID_Explorer, SBCMDID_ISIEMODEBROWSER, 0, NULL, NULL)) { IPersistFile *ppf; if (SUCCEEDED(CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_PPV_ARG(IPersistFile, &ppf)))) { if (SUCCEEDED(ppf->Load(pvarIn->bstrVal, STGM_READ))) { LPWSTR pszUrl = NULL; TraceMsg(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut shortcut inited with file", this); IUniformResourceLocatorW *purl; if (SUCCEEDED(ppf->QueryInterface(IID_PPV_ARG(IUniformResourceLocatorW, &purl)))) { purl->GetURL(&pszUrl); purl->Release(); } if (pszUrl) { TraceMsgW(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut found %s", this, pszUrl); LPITEMIDLIST pidl; IEParseDisplayNameW(CP_ACP, pszUrl, &pidl); if (pidl) { ASSERT(NULL == _pwszShortcutPathPending); if (_pwszShortcutPathPending) LocalFree(_pwszShortcutPathPending); _pwszShortcutPathPending = StrDupW(pvarIn->bstrVal); hr = _BrowseObject(pidl, SBSP_SAMEBROWSER); if (SUCCEEDED(hr)) { if (ready == READYSTATE_UNINITIALIZED) put_Visible(VARIANT_TRUE); HWND hwnd = _GetHWND(); if (hwnd) { if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE); else SetForegroundWindow(hwnd); } } ILFree(pidl); } SHFree(pszUrl); } } ppf->Release(); } } TraceMsg(DM_SHELLEXECOBJECT, "IEAuto_NavIEShortcut returns 0x%X", hr); return hr; }