1732 lines
55 KiB
C++
1732 lines
55 KiB
C++
/*
|
|
* isurl.cpp - IUniformResourceLocator implementation for Intshcut class.
|
|
*/
|
|
#include "priv.h"
|
|
#include "ishcut.h"
|
|
#include "urlprop.h"
|
|
#include "shlwapi.h"
|
|
#include "infotip.h"
|
|
#include "resource.h"
|
|
#include <intshctp.h>
|
|
|
|
#include <mluisupp.h>
|
|
|
|
#define DM_PLUGGABLE DM_TRACE
|
|
#define DM_SHELLEXECOBJECT 0x80000000
|
|
|
|
extern HRESULT CreateTargetFrame(LPCOLESTR pszTargetName, LPUNKNOWN /*IN,OUT*/ *ppunk);
|
|
|
|
BOOL
|
|
GetClassDefaultVerb(
|
|
LPCTSTR pcszClass,
|
|
LPTSTR pszDefaultVerbBuf,
|
|
UINT cchBufLen)
|
|
{
|
|
// No; get the default verb
|
|
TCHAR szKey[MAX_PATH];
|
|
|
|
StrCpyN(szKey, pcszClass, SIZECHARS(szKey));
|
|
StrCatBuff(szKey, TEXT("\\"), SIZECHARS(szKey));
|
|
StrCatBuff(szKey, TEXT("shell"), SIZECHARS(szKey));
|
|
DWORD cbSize = CbFromCch(cchBufLen);
|
|
|
|
if (NO_ERROR != SHGetValue(HKEY_CLASSES_ROOT, szKey, NULL, NULL, pszDefaultVerbBuf, &cbSize)
|
|
|| !*pszDefaultVerbBuf)
|
|
{
|
|
// Default to "open" if the registry doesn't specify one
|
|
StrCpyN(pszDefaultVerbBuf, TEXT("open"), cchBufLen);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
BOOL
|
|
IsValidPCURLINVOKECOMMANDINFO(
|
|
PCURLINVOKECOMMANDINFO pcurlici)
|
|
{
|
|
return(IS_VALID_READ_PTR(pcurlici, CURLINVOKECOMMANDINFO) &&
|
|
EVAL(pcurlici->dwcbSize >= SIZEOF(*pcurlici)) &&
|
|
FLAGS_ARE_VALID(pcurlici->dwFlags, ALL_IURL_INVOKECOMMAND_FLAGS) &&
|
|
(IsFlagClear(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI) ||
|
|
NULL == pcurlici->hwndParent ||
|
|
IS_VALID_HANDLE(pcurlici->hwndParent, WND)) &&
|
|
(IsFlagSet(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB) ||
|
|
IS_VALID_STRING_PTR(pcurlici->pcszVerb, -1)));
|
|
}
|
|
|
|
#endif
|
|
|
|
/********************************** Methods **********************************/
|
|
|
|
typedef struct
|
|
{
|
|
UINT idsVerb;
|
|
UINT idsMenuHelp;
|
|
LPCTSTR pszVerb;
|
|
} ISCM;
|
|
|
|
const static ISCM g_rgiscm[] =
|
|
{
|
|
{ IDS_MENUOPEN, IDS_MH_OPEN, TEXT("open") }, // IDCMD_ISCM_OPEN
|
|
{ IDS_SYNCHRONIZE, IDS_MH_SYNCHRONIZE, TEXT("update now")}, // IDCMD_ISCM_SYNC
|
|
{ IDS_MAKE_OFFLINE, IDS_MH_MAKE_OFFLINE, TEXT("subscribe")}, // IDCMD_ISCM_SUB
|
|
};
|
|
|
|
// WARNING - these must match their index into g_rgiscm
|
|
#define IDCMD_ISCM_OPEN 0
|
|
#define IDCMD_ISCM_SYNC 1
|
|
#define IDCMD_ISCM_SUB 2
|
|
|
|
BOOL _IsSubscribed(LPCWSTR pszUrl, BOOL *pfSubscribable)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
ISubscriptionMgr * pMgr;
|
|
|
|
*pfSubscribable = FALSE;
|
|
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ISubscriptionMgr, &pMgr))))
|
|
{
|
|
pMgr->IsSubscribed(pszUrl, &fRet);
|
|
|
|
|
|
pMgr->Release();
|
|
}
|
|
|
|
if (!fRet)
|
|
{
|
|
//test if we CAN subscribe to this thing
|
|
if (!SHRestricted2W(REST_NoAddingSubscriptions, pszUrl, 0) &&
|
|
IsFeaturePotentiallyAvailable(CLSID_SubscriptionMgr))
|
|
{
|
|
*pfSubscribable = IsSubscribableW(pszUrl);
|
|
}
|
|
}
|
|
else
|
|
*pfSubscribable = TRUE;
|
|
|
|
return fRet;
|
|
}
|
|
|
|
void _InsertISCM(UINT indexISCM, HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT uFlags)
|
|
{
|
|
TCHAR szMenu[CCH_MENUMAX];
|
|
uFlags |= MF_BYPOSITION | MF_STRING;
|
|
|
|
MLLoadShellLangString(g_rgiscm[indexISCM].idsVerb, szMenu, SIZECHARS(szMenu));
|
|
InsertMenu_PrivateNoMungeW(hmenu, indexMenu, uFlags, idCmdFirst + indexISCM, szMenu);
|
|
}
|
|
|
|
// IContextMenu::QueryContextMenu handler for Intshcut
|
|
// The context menu handler adds the open verb for .url
|
|
// files. This is because we remove the shell\open\command
|
|
// key in Nashville for this file type.
|
|
|
|
STDMETHODIMP Intshcut::QueryContextMenu(
|
|
IN HMENU hmenu,
|
|
IN UINT indexMenu,
|
|
IN UINT idCmdFirst,
|
|
IN UINT idCmdLast,
|
|
IN UINT uFlags)
|
|
{
|
|
//
|
|
// LEGACY - .URL files have to maintain an open verb in the registry - ZekeL - 14-APR-99
|
|
// we would like to just use the "open" verb here in the context menu extension,
|
|
// but we need to not duplicate the open verb that is added by DefCM
|
|
// on NT5+ shell32 we disable that verb so we can add it here.
|
|
// on earlier shell32 we want to add "open" any time we arent
|
|
// initialized by DefCM. if we think that DefCM added us,
|
|
// then we go ahead and allow the DefCM's open from the registry.
|
|
//
|
|
if (!m_fProbablyDefCM || GetUIVersion() >= 5)
|
|
{
|
|
_InsertISCM(IDCMD_ISCM_OPEN, hmenu, indexMenu, idCmdFirst, 0);
|
|
if (-1 == GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0))
|
|
SetMenuDefaultItem(hmenu, indexMenu, MF_BYPOSITION);
|
|
indexMenu++;
|
|
}
|
|
|
|
#ifndef UNIX
|
|
/* v-sriran: 12/8/97
|
|
* disabling the context menu item for subscribe, separators etc.
|
|
* because we are not supporting subscriptions right now
|
|
*/
|
|
|
|
// skip this if we only want default or if there is no room for more.
|
|
if (!(uFlags & CMF_DEFAULTONLY) && (idCmdLast - idCmdFirst >= ARRAYSIZE(g_rgiscm)))
|
|
{
|
|
WCHAR *pwszURL;
|
|
if (SUCCEEDED(GetURLW(&pwszURL)))
|
|
{
|
|
BOOL bSubscribable = FALSE; //can be subscribed to
|
|
BOOL bSub = _IsSubscribed(pwszURL, &bSubscribable);
|
|
m_bCheckForDelete = bSub && m_pszFile;
|
|
|
|
if (bSubscribable || bSub)
|
|
{
|
|
// add a separator for our subscription stuff
|
|
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
|
UINT uMenuFlags = 0;
|
|
|
|
if (bSub)
|
|
{
|
|
uMenuFlags |= MF_CHECKED;
|
|
|
|
if (SHRestricted2W(REST_NoRemovingSubscriptions, pwszURL, 0))
|
|
{
|
|
uMenuFlags |= MF_GRAYED;
|
|
}
|
|
}
|
|
|
|
_InsertISCM(IDCMD_ISCM_SUB, hmenu, indexMenu++, idCmdFirst, uMenuFlags);
|
|
|
|
if (bSub)
|
|
{
|
|
uMenuFlags = 0;
|
|
|
|
if (SHRestricted2W(REST_NoManualUpdates, NULL, 0))
|
|
{
|
|
uMenuFlags |= MF_GRAYED;
|
|
}
|
|
_InsertISCM(IDCMD_ISCM_SYNC, hmenu, indexMenu++, idCmdFirst, uMenuFlags);
|
|
}
|
|
}
|
|
|
|
SHFree(pwszURL);
|
|
}
|
|
}
|
|
|
|
#endif /* UNIX */
|
|
|
|
return ResultFromShort(ARRAYSIZE(g_rgiscm));
|
|
}
|
|
|
|
STDMETHODIMP Intshcut::InvokeCommand(IN LPCMINVOKECOMMANDINFO pici)
|
|
{
|
|
HRESULT hres = E_INVALIDARG;
|
|
|
|
ASSERT(pici);
|
|
|
|
if (pici && SIZEOF(*pici) <= pici->cbSize)
|
|
{
|
|
UINT idCmd;
|
|
|
|
if (0 == HIWORD(pici->lpVerb)) // Is the ID cmd given?
|
|
{
|
|
idCmd = LOWORD(pici->lpVerb); // Yes
|
|
|
|
// Old versions of ShellExec() didnt get the right default command - Zekel - 15-MAR-99
|
|
// since our QCM implementation doesnt add anything to the menu
|
|
// if we fix the QCM to work correctly, then this problem will go away.
|
|
// it sent 0xfffe instead. so just adjust here.
|
|
if (idCmd == 0xfffe && GetUIVersion() <= 4)
|
|
idCmd = IDCMD_ISCM_OPEN;
|
|
}
|
|
else
|
|
{
|
|
// No; a language-independent verb was supplied
|
|
int i;
|
|
LPCTSTR pszVerb;
|
|
LPCMINVOKECOMMANDINFOEX piciex = (LPCMINVOKECOMMANDINFOEX)pici;
|
|
ASSERT(SIZEOF(*piciex) <= piciex->cbSize);
|
|
|
|
WCHAR szVerb[40];
|
|
|
|
if (piciex->lpVerbW)
|
|
{
|
|
pszVerb = piciex->lpVerbW;
|
|
}
|
|
else
|
|
{
|
|
if (piciex->lpVerb)
|
|
{
|
|
ASSERT(lstrlenA(piciex->lpVerb) < ARRAYSIZE(szVerb));
|
|
SHAnsiToUnicode(piciex->lpVerb, szVerb, ARRAYSIZE(szVerb));
|
|
}
|
|
else
|
|
{
|
|
szVerb[0] = L'\0';
|
|
}
|
|
|
|
pszVerb = szVerb;
|
|
}
|
|
|
|
idCmd = (UINT)-1;
|
|
for (i = 0; i < ARRAYSIZE(g_rgiscm); i++)
|
|
{
|
|
if (0 == StrCmpI(g_rgiscm[i].pszVerb, pszVerb))
|
|
{
|
|
idCmd = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (idCmd)
|
|
{
|
|
case IDCMD_ISCM_OPEN:
|
|
{
|
|
URLINVOKECOMMANDINFO urlici;
|
|
|
|
urlici.dwcbSize = SIZEOF(urlici);
|
|
urlici.hwndParent = pici->hwnd;
|
|
urlici.pcszVerb = NULL;
|
|
urlici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
|
|
|
|
if (IsFlagClear(pici->fMask, CMIC_MASK_FLAG_NO_UI))
|
|
{
|
|
SetFlag(urlici.dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI);
|
|
}
|
|
if (IsFlagSet(pici->fMask, SEE_MASK_FLAG_DDEWAIT))
|
|
{
|
|
SetFlag(urlici.dwFlags, IURL_INVOKECOMMAND_FL_DDEWAIT);
|
|
}
|
|
hres = InvokeCommand(&urlici);
|
|
m_bCheckForDelete = FALSE;
|
|
}
|
|
break;
|
|
|
|
case IDCMD_ISCM_SUB:
|
|
case IDCMD_ISCM_SYNC:
|
|
{
|
|
hres = S_OK;
|
|
|
|
WCHAR *pwszURL;
|
|
if (SUCCEEDED(GetURLW(&pwszURL)))
|
|
{
|
|
ISubscriptionMgr * pMgr;
|
|
if (SUCCEEDED(JITCoCreateInstance(CLSID_SubscriptionMgr,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARG(ISubscriptionMgr, &pMgr),
|
|
pici->hwnd,
|
|
FIEF_FLAG_FORCE_JITUI)))
|
|
{
|
|
if (idCmd == IDCMD_ISCM_SUB)
|
|
{
|
|
BOOL bSubscribed;
|
|
|
|
pMgr->IsSubscribed(pwszURL, &bSubscribed);
|
|
|
|
if (!bSubscribed)
|
|
{
|
|
SHFILEINFO sfi = {0};
|
|
WCHAR wszName[MAX_PATH];
|
|
wszName[0] = 0;
|
|
if (SHGetFileInfo(m_pszFile, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME))
|
|
{
|
|
SHTCharToUnicode(sfi.szDisplayName, wszName, ARRAYSIZE(wszName));
|
|
}
|
|
|
|
if (!wszName[0])
|
|
StrCpyNW(wszName, pwszURL, ARRAYSIZE(wszName));
|
|
|
|
//all subscriptions to local .urls are treated as subscribing something
|
|
//that's already in Favorites, so user isn't forced to add it to their
|
|
//favorites as they subscribe.
|
|
if (SUCCEEDED(pMgr->CreateSubscription(pici->hwnd, pwszURL, wszName,
|
|
CREATESUBS_FROMFAVORITES,
|
|
SUBSTYPE_URL,
|
|
NULL)))
|
|
{
|
|
pMgr->UpdateSubscription(pwszURL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pMgr->DeleteSubscription(pwszURL, pici->hwnd);
|
|
}
|
|
}
|
|
else if (idCmd == IDCMD_ISCM_SYNC)
|
|
{
|
|
pMgr->UpdateSubscription(pwszURL);
|
|
}
|
|
pMgr->Release();
|
|
}
|
|
SHFree(pwszURL);
|
|
m_bCheckForDelete = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
hres = E_INVALIDARG;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IContextMenu::GetCommandString handler for Intshcut
|
|
|
|
*/
|
|
STDMETHODIMP Intshcut::GetCommandString(
|
|
IN UINT_PTR idCmd,
|
|
IN UINT uType,
|
|
IN OUT UINT* puReserved,
|
|
IN OUT LPSTR pszName,
|
|
IN UINT cchMax)
|
|
{
|
|
HRESULT hres;
|
|
TCHAR szMenu[CCH_MENUMAX];
|
|
|
|
ASSERT(NULL == puReserved);
|
|
ASSERT(IS_VALID_WRITE_BUFFER(pszName, char, cchMax));
|
|
|
|
switch (uType)
|
|
{
|
|
case GCS_HELPTEXTA:
|
|
case GCS_HELPTEXTW:
|
|
if (idCmd < ARRAYSIZE(g_rgiscm))
|
|
{
|
|
MLLoadString(g_rgiscm[idCmd].idsMenuHelp, szMenu, SIZECHARS(szMenu));
|
|
|
|
if (GCS_HELPTEXTA == uType)
|
|
{
|
|
UnicodeToAnsi(szMenu, pszName, cchMax);
|
|
}
|
|
else
|
|
{
|
|
StrCpyN((LPWSTR)pszName, szMenu, cchMax);
|
|
}
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
case GCS_VALIDATEA:
|
|
case GCS_VALIDATEW:
|
|
hres = idCmd < ARRAYSIZE(g_rgiscm) ? S_OK : S_FALSE;
|
|
break;
|
|
|
|
case GCS_VERBA:
|
|
case GCS_VERBW:
|
|
if (idCmd < ARRAYSIZE(g_rgiscm))
|
|
{
|
|
LPCTSTR pszVerb = g_rgiscm[idCmd].pszVerb;
|
|
|
|
if (GCS_VERBA == uType)
|
|
{
|
|
UnicodeToAnsi(pszVerb, pszName, cchMax);
|
|
}
|
|
else
|
|
{
|
|
StrCpyN((LPWSTR)pszName, pszVerb, cchMax);
|
|
}
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
// IContextMenu2::HandleMenuMsg handler for Intshcut
|
|
STDMETHODIMP Intshcut::HandleMenuMsg(IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Returns the protocol scheme value (URL_SCHEME_*).
|
|
|
|
STDMETHODIMP_(DWORD)
|
|
Intshcut::GetScheme(void)
|
|
{
|
|
DWORD dwScheme = URL_SCHEME_UNKNOWN;
|
|
|
|
if (SUCCEEDED(InitProp()))
|
|
{
|
|
m_pprop->GetProp(PID_IS_SCHEME, &dwScheme);
|
|
}
|
|
return dwScheme;
|
|
}
|
|
|
|
|
|
// IUniformResourceLocator::SetURL handler for Intshcut
|
|
//
|
|
// Note:
|
|
// 1. SetURL clears the IDList, so that when we launch this shortcut,
|
|
// we will use the URL.
|
|
|
|
STDMETHODIMP
|
|
Intshcut::SetURL(
|
|
IN LPCTSTR pszURL, OPTIONAL
|
|
IN DWORD dwFlags)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
|
|
ASSERT(! pszURL ||
|
|
IS_VALID_STRING_PTR(pszURL, -1));
|
|
ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_IURL_SETURL_FLAGS));
|
|
|
|
hres = InitProp();
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = m_pprop->SetURLProp(pszURL, dwFlags);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// if the path was set successfully, clear the pidl.
|
|
m_pprop->SetIDListProp(NULL);
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IUniformResourceLocatorA::SetURL handler for Intshcut
|
|
|
|
Ansi version
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
Intshcut::SetURL(
|
|
IN LPCSTR pcszURL, OPTIONAL
|
|
IN DWORD dwInFlags)
|
|
{
|
|
if ( !pcszURL )
|
|
{
|
|
return SetURL((LPCTSTR)NULL, dwInFlags);
|
|
}
|
|
else
|
|
{
|
|
WCHAR wszURL[MAX_URL_STRING];
|
|
|
|
ASSERT(IS_VALID_STRING_PTRA(pcszURL, -1));
|
|
|
|
AnsiToUnicode(pcszURL, wszURL, SIZECHARS(wszURL));
|
|
|
|
return SetURL(wszURL, dwInFlags);
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP Intshcut::GetURLW(WCHAR **ppwsz)
|
|
{
|
|
LPTSTR pszURL;
|
|
HRESULT hres = GetURL(&pszURL);
|
|
if (S_OK == hres)
|
|
{
|
|
hres = SHStrDup(pszURL, ppwsz);
|
|
SHFree(pszURL);
|
|
}
|
|
else
|
|
hres = E_FAIL; // map S_FALSE to FAILED()
|
|
return hres;
|
|
}
|
|
|
|
// IUniformResourceLocator::GetURL handler for Intshcut
|
|
|
|
STDMETHODIMP Intshcut::GetURL(LPTSTR * ppszURL)
|
|
{
|
|
HRESULT hres;
|
|
TCHAR szURL[MAX_URL_STRING];
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
|
|
ASSERT(IS_VALID_WRITE_PTR(ppszURL, PTSTR));
|
|
|
|
*ppszURL = NULL;
|
|
|
|
hres = InitProp();
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
|
|
if (S_OK == hres)
|
|
{
|
|
// (+ 1) for null terminator.
|
|
int cch = lstrlen(szURL) + 1;
|
|
*ppszURL = (PTSTR)SHAlloc(CbFromCch(cch));
|
|
if (*ppszURL)
|
|
StrCpyN(*ppszURL, szURL, cch);
|
|
else
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
|
|
ASSERT((hres == S_OK &&
|
|
IS_VALID_STRING_PTR(*ppszURL, -1)) ||
|
|
((hres == S_FALSE ||
|
|
hres == E_OUTOFMEMORY) &&
|
|
! *ppszURL));
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IUniformResourceLocatorA::GetURL handler for Intshcut
|
|
|
|
Ansi version
|
|
|
|
*/
|
|
STDMETHODIMP Intshcut::GetURL(LPSTR * ppszURL)
|
|
{
|
|
HRESULT hres;
|
|
TCHAR szURL[MAX_URL_STRING];
|
|
|
|
ASSERT(IS_VALID_WRITE_PTR(ppszURL, PSTR));
|
|
|
|
*ppszURL = NULL;
|
|
|
|
hres = InitProp();
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
|
|
|
|
if (S_OK == hres)
|
|
{
|
|
DWORD cch = WideCharToMultiByte(CP_ACP, 0, szURL, -1, NULL, 0, NULL, NULL);
|
|
*ppszURL = (LPSTR)SHAlloc(CbFromCchA(cch + 1));
|
|
|
|
if (*ppszURL)
|
|
UnicodeToAnsi(szURL, *ppszURL, cch);
|
|
else
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT HandlePluggableProtocol(LPCTSTR pszURL, LPCTSTR pszProtocol)
|
|
{
|
|
HRESULT hres = E_UNEXPECTED;
|
|
HKEY hkey;
|
|
TraceMsg(DM_PLUGGABLE, "HandlePluggableProtocol called");
|
|
|
|
if (RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("PROTOCOLS\\Handler"), 0, KEY_READ, &hkey) == ERROR_SUCCESS) {
|
|
HKEY hkeyProtocol;
|
|
if (RegOpenKeyEx(hkey, pszProtocol, 0, KEY_READ, &hkeyProtocol) == ERROR_SUCCESS) {
|
|
TraceMsg(DM_PLUGGABLE, "HandlePluggableProtocol found %s", pszProtocol);
|
|
IUnknown* punk = NULL; // CreateTargetFrame's ppunk is [IN][OUT]
|
|
hres = CreateTargetFrame(NULL, &punk);
|
|
if (SUCCEEDED(hres)) {
|
|
IWebBrowser2* pauto;
|
|
hres = punk->QueryInterface(IID_IWebBrowser2, (LPVOID*)&pauto);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
TraceMsg(DM_PLUGGABLE, "HandlePluggableProtocol calling navigate with %s", pszURL);
|
|
|
|
LBSTR::CString strUrl;
|
|
|
|
LPTSTR pstrUrl = strUrl.GetBuffer( MAX_URL_STRING );
|
|
|
|
if ( strUrl.GetAllocLength() < MAX_URL_STRING )
|
|
{
|
|
TraceMsg( TF_WARNING, "HandlePluggableProtocol() - strUrl Allocation Failed!" );
|
|
|
|
strUrl.Empty();
|
|
}
|
|
else
|
|
{
|
|
SHTCharToUnicode( pszURL, pstrUrl, MAX_URL_STRING );
|
|
|
|
// Let CString class own the buffer again.
|
|
strUrl.ReleaseBuffer();
|
|
}
|
|
|
|
pauto->Navigate( strUrl, PVAREMPTY, PVAREMPTY, PVAREMPTY, PVAREMPTY );
|
|
pauto->put_Visible(TRUE);
|
|
pauto->Release();
|
|
}
|
|
punk->Release();
|
|
}
|
|
RegCloseKey(hkeyProtocol);
|
|
} else {
|
|
TraceMsg(DM_WARNING, "HandlePluggableProtocol can't find %s", pszProtocol);
|
|
}
|
|
RegCloseKey(hkey);
|
|
} else {
|
|
ASSERT(0);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT _IEExecFile_TryRunningWindow(VARIANT *pvarIn, DWORD cid)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
ASSERT(pvarIn);
|
|
|
|
IShellWindows *psw = WinList_GetShellWindows(TRUE);
|
|
if (psw)
|
|
{
|
|
IUnknown *punk;
|
|
if (SUCCEEDED(psw->_NewEnum(&punk)))
|
|
{
|
|
VARIANT var = {0};
|
|
IEnumVARIANT *penum;
|
|
|
|
//
|
|
// its too bad _NewEnum doesnt return an penum....
|
|
// this should never fail.
|
|
//
|
|
punk->QueryInterface(IID_PPV_ARG(IEnumVARIANT, &penum));
|
|
ASSERT(penum);
|
|
|
|
//
|
|
// this can be super spendy since every one of these
|
|
// items is marshalled.
|
|
//
|
|
// should we clone the stream here??
|
|
//
|
|
while (FAILED(hr) && S_OK == penum->Next(1, &var, NULL))
|
|
{
|
|
ASSERT(var.vt == VT_DISPATCH);
|
|
ASSERT(var.pdispVal);
|
|
IOleCommandTarget *poct;
|
|
|
|
if (SUCCEEDED(var.pdispVal->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &poct))))
|
|
{
|
|
CoAllowSetForegroundWindow(poct, NULL);
|
|
|
|
hr = poct->Exec(&CGID_Explorer, cid, 0, pvarIn, NULL);
|
|
|
|
poct->Release();
|
|
}
|
|
|
|
// this should release the pdisp
|
|
VariantClear(&var);
|
|
}
|
|
|
|
punk->Release();
|
|
penum->Release();
|
|
}
|
|
|
|
psw->Release();
|
|
}
|
|
|
|
|
|
TraceMsgW(DM_SHELLEXECOBJECT, "IEExecFile_Running returns 0x%X", hr);
|
|
return hr;
|
|
}
|
|
|
|
BOOL IsIESchemeHandler(LPTSTR pszVerb, LPTSTR pszScheme)
|
|
{
|
|
// if we fail to get any value at all, the we must assume that it
|
|
// is some protocol like about: or res: that is not in the registry
|
|
// so we default to success.
|
|
BOOL fRet = FALSE;
|
|
TCHAR szExe[MAX_PATH];
|
|
|
|
if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, pszScheme, pszVerb, szExe, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szExe)))))
|
|
{
|
|
// if we find something and it aint us, then fail.
|
|
if ((StrStrI(szExe, TEXT("iexplore.exe")) || StrStrI(szExe, TEXT("explorer.exe"))))
|
|
{
|
|
fRet = TRUE;
|
|
|
|
TraceMsg(DM_SHELLEXECOBJECT, "IsIEScheme() found %s", szExe);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// these are unregistered schemes, we are the only ones that
|
|
// should ever even use the unregistered schemes like
|
|
// res: or shell: so return TRUE here too.
|
|
fRet = *pszScheme && *pszScheme != TEXT('.');
|
|
}
|
|
|
|
TraceMsg(DM_SHELLEXECOBJECT, "IsIEScheme() returns %d for %s", fRet, pszScheme);
|
|
return fRet;
|
|
}
|
|
|
|
HRESULT IEExecFile(LPTSTR pszVerb, LPTSTR pszScheme, DWORD cid, LPTSTR pszPath)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
ASSERT(pszVerb);
|
|
ASSERT(pszScheme);
|
|
ASSERT(pszPath);
|
|
|
|
if (IsIESchemeHandler(pszVerb, pszScheme))
|
|
{
|
|
VARIANT varIn = {0};
|
|
varIn.vt = VT_BSTR;
|
|
|
|
SHSTRW str;
|
|
str.SetStr(pszPath);
|
|
varIn.bstrVal = SysAllocString(str.GetStr());
|
|
if (varIn.bstrVal)
|
|
{
|
|
if (!SHRegGetBoolUSValue(REGSTR_PATH_MAIN, TEXT("AllowWindowReuse"), FALSE, TRUE)
|
|
|| FAILED(hr = _IEExecFile_TryRunningWindow(&varIn, cid)))
|
|
{
|
|
IOleCommandTarget *poct;
|
|
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
|
|
IID_PPV_ARG(IOleCommandTarget, &poct))))
|
|
{
|
|
hr = poct->Exec(&CGID_Explorer, cid, 0, &varIn, NULL);
|
|
poct->Release();
|
|
}
|
|
}
|
|
|
|
SysFreeString(varIn.bstrVal);
|
|
}
|
|
|
|
}
|
|
|
|
TraceMsg(DM_SHELLEXECOBJECT, "IEExecFile returns 0x%X for %s", hr, pszPath);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IUniformResourceLocator::InvokeCommand for Intshcut
|
|
|
|
Note:
|
|
1. If the internet shortcut comes with a pidl, use it to ShellExec,
|
|
otherwise use the URL.
|
|
|
|
*/
|
|
STDMETHODIMP Intshcut::InvokeCommand(PURLINVOKECOMMANDINFO purlici)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
|
|
ASSERT(IS_VALID_STRUCT_PTR(purlici, CURLINVOKECOMMANDINFO));
|
|
|
|
if (purlici && EVAL(SIZEOF(*purlici) == purlici->dwcbSize))
|
|
{
|
|
//
|
|
// App compat. Don't use stack space for the URL. We use up 16-bit app
|
|
// stack space when we they shell exec urls.
|
|
//
|
|
|
|
LPWSTR pszURL = (LPWSTR)LocalAlloc(LPTR, MAX_URL_STRING * sizeof(WCHAR));
|
|
|
|
if (pszURL)
|
|
{
|
|
hr = InitProp();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// App Compat: Don't use up stack space.
|
|
//
|
|
|
|
LPWSTR pszT = (LPWSTR)LocalAlloc(LPTR, MAX_PATH * sizeof(WCHAR));
|
|
|
|
if (pszT)
|
|
{
|
|
SHELLEXECUTEINFO sei = {0};
|
|
LPITEMIDLIST pidl = NULL;
|
|
LPTSTR pszProtocol = NULL;
|
|
PARSEDURL pu;
|
|
pu.nScheme = 0; // init to avoid bogus C4701 warning
|
|
|
|
sei.fMask = SEE_MASK_NO_HOOKS;
|
|
|
|
// check if we have a pidl for the target.
|
|
hr = GetIDListInternal(&pidl);
|
|
if ((hr == S_OK) && pidl)
|
|
{
|
|
// yse, use the pidl to ShellExec.
|
|
sei.fMask |= SEE_MASK_INVOKEIDLIST;
|
|
sei.lpIDList = pidl;
|
|
}
|
|
else
|
|
{
|
|
// no, get the URL and invoke class handler.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pprop->GetProp(PID_IS_URL, pszURL, MAX_URL_STRING);
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
hr = CopyURLProtocol(pszURL, &pszProtocol, &pu);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = IsProtocolRegistered(pszProtocol);
|
|
if (FAILED(hr)) {
|
|
if (SUCCEEDED(HandlePluggableProtocol(pszURL, pszProtocol))) {
|
|
hr = S_OK;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ResultFromWin32(RegOpenKeyExW(HKEY_CLASSES_ROOT, pszProtocol, 0, KEY_READ, &sei.hkeyClass));
|
|
sei.fMask |= SEE_MASK_CLASSKEY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// the prop code returns S_FALSE when it fails to get anything
|
|
if (S_FALSE == hr)
|
|
hr = URL_E_INVALID_SYNTAX;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// App Compat: Don't use up stack space.
|
|
//
|
|
|
|
LPWSTR pszVerb = (LPWSTR)LocalAlloc(LPTR, MAX_PATH * sizeof(WCHAR));
|
|
|
|
if (pszVerb)
|
|
{
|
|
int nShowCmd;
|
|
|
|
// Execute URL via registered protocol handler.
|
|
|
|
if (IsFlagClear(purlici->dwFlags,
|
|
IURL_INVOKECOMMAND_FL_ALLOW_UI))
|
|
SetFlag(sei.fMask, SEE_MASK_FLAG_NO_UI);
|
|
|
|
if (purlici->dwFlags & IURL_INVOKECOMMAND_FL_DDEWAIT)
|
|
SetFlag(sei.fMask, SEE_MASK_FLAG_DDEWAIT);
|
|
|
|
if (IsFlagClear(purlici->dwFlags,
|
|
IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB))
|
|
{
|
|
sei.lpVerb = purlici->pcszVerb;
|
|
}
|
|
else
|
|
{
|
|
if (pszProtocol &&
|
|
GetClassDefaultVerb(pszProtocol, pszVerb,
|
|
MAX_PATH))
|
|
sei.lpVerb = pszVerb;
|
|
else
|
|
ASSERT(! sei.lpVerb);
|
|
}
|
|
|
|
ASSERT(m_pprop);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pprop->GetProp(PID_IS_WORKINGDIR, pszT, MAX_PATH);
|
|
m_pprop->GetProp(PID_IS_SHOWCMD, &nShowCmd); // inits to zero if not found
|
|
|
|
// if we have a file try using a direct connection
|
|
// to the shell to give the whole shortcut
|
|
if (m_pszFile && ((IsIEDefaultBrowser()) || (_IsInFavoritesFolder())))
|
|
{
|
|
LPTSTR pszType = pszProtocol;
|
|
if (pu.nScheme == URL_SCHEME_FILE)
|
|
pszType = PathFindExtension(pszURL);
|
|
|
|
hr = IEExecFile(pszVerb, pszType, SBCMDID_IESHORTCUT, m_pszFile);
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
// if we failed to pass it to IE, then we should just default
|
|
// to the old behavior
|
|
if (FAILED(hr))
|
|
{
|
|
|
|
sei.cbSize = SIZEOF(sei);
|
|
sei.hwnd = purlici->hwndParent;
|
|
sei.lpFile = pszURL;
|
|
sei.lpDirectory = pszT;
|
|
sei.nShow = nShowCmd ? nShowCmd : SW_NORMAL;
|
|
|
|
// We have to special case "file:" URLs,
|
|
// because Nashville's Explorer typically handles
|
|
// file: URLs via DDE, which fails for executables
|
|
// (eg, "file://c:\windows\notepad.exe") and
|
|
// non-hostable docs (like text files).
|
|
//
|
|
// So in this case, we remove the protocol class
|
|
// and execute the suffix.
|
|
|
|
// App Compat: Don't use up stack space.
|
|
DWORD cchPath = MAX_PATH;
|
|
LPWSTR pszPath = (LPWSTR)LocalAlloc(LPTR, cchPath * sizeof(WCHAR));
|
|
|
|
if (pszPath)
|
|
{
|
|
if (IsFlagSet(sei.fMask, SEE_MASK_CLASSKEY) &&
|
|
(URL_SCHEME_FILE == pu.nScheme) &&
|
|
SUCCEEDED(PathCreateFromUrl(pszURL, pszPath, &cchPath, 0)))
|
|
{
|
|
sei.hkeyClass = NULL;
|
|
ClearFlag(sei.fMask, SEE_MASK_CLASSKEY);
|
|
sei.lpFile = pszPath;
|
|
|
|
}
|
|
|
|
if (m_pszFile && IsOS(OS_WHISTLERORGREATER))
|
|
{
|
|
// this is the security context
|
|
// so that shellexec() can do zone checks
|
|
sei.lpClass = m_pszFile;
|
|
sei.fMask |= SEE_MASK_HASTITLE | SEE_MASK_HASLINKNAME;
|
|
}
|
|
|
|
|
|
TraceMsg(TF_INTSHCUT, "Intshcut::InvokeCommand(): Invoking %s verb on URL %s.",
|
|
sei.lpVerb ? sei.lpVerb : TEXT("open"),
|
|
sei.lpFile);
|
|
|
|
hr = ShellExecuteEx(&sei) ? S_OK : IS_E_EXEC_FAILED;
|
|
|
|
LocalFree(pszPath);
|
|
pszPath = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
if (hr != S_OK)
|
|
TraceMsg(TF_WARNING, "Intshcut::InvokeCommand(): ShellExecuteEx() via registered protcol handler failed for %s.",
|
|
pszURL);
|
|
|
|
LocalFree(pszVerb);
|
|
pszVerb = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
if (pszProtocol)
|
|
{
|
|
LocalFree(pszProtocol);
|
|
pszProtocol = NULL;
|
|
}
|
|
|
|
if (pidl)
|
|
ILFree(pidl);
|
|
|
|
if (sei.hkeyClass)
|
|
RegCloseKey(sei.hkeyClass);
|
|
|
|
if (FAILED(hr) && (purlici->dwFlags & IURL_INVOKECOMMAND_FL_ALLOW_UI))
|
|
{
|
|
switch (hr)
|
|
{
|
|
case IS_E_EXEC_FAILED:
|
|
break;
|
|
|
|
case URL_E_INVALID_SYNTAX:
|
|
MLShellMessageBox(
|
|
purlici->hwndParent,
|
|
MAKEINTRESOURCE(IDS_IS_EXEC_INVALID_SYNTAX),
|
|
MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
|
|
(MB_OK | MB_ICONEXCLAMATION),
|
|
pszURL);
|
|
|
|
break;
|
|
|
|
case URL_E_UNREGISTERED_PROTOCOL:
|
|
{
|
|
LPTSTR pszProtocol;
|
|
|
|
if (CopyURLProtocol(pszURL, &pszProtocol, NULL) == S_OK)
|
|
{
|
|
MLShellMessageBox(
|
|
purlici->hwndParent,
|
|
MAKEINTRESOURCE(IDS_IS_EXEC_UNREGISTERED_PROTOCOL),
|
|
MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
|
|
(MB_OK | MB_ICONEXCLAMATION),
|
|
pszProtocol);
|
|
|
|
LocalFree(pszProtocol);
|
|
pszProtocol = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case E_OUTOFMEMORY:
|
|
MLShellMessageBox(
|
|
purlici->hwndParent,
|
|
MAKEINTRESOURCE(IDS_IS_EXEC_OUT_OF_MEMORY),
|
|
MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
|
|
(MB_OK | MB_ICONEXCLAMATION));
|
|
break;
|
|
|
|
default:
|
|
ASSERT(hr == E_ABORT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalFree(pszT);
|
|
pszT = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
LocalFree(pszURL);
|
|
pszURL = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
|
|
ASSERT(hr == S_OK ||
|
|
hr == E_ABORT ||
|
|
hr == E_OUTOFMEMORY ||
|
|
hr == URL_E_INVALID_SYNTAX ||
|
|
hr == URL_E_UNREGISTERED_PROTOCOL ||
|
|
hr == IS_E_EXEC_FAILED ||
|
|
hr == E_INVALIDARG);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: IUniformResourceLocatorA::InvokeCommand for Intshcut
|
|
|
|
Ansi version
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
Intshcut::InvokeCommand(
|
|
IN PURLINVOKECOMMANDINFOA purlici)
|
|
|
|
{
|
|
HRESULT hres = E_INVALIDARG;
|
|
|
|
ASSERT(purlici);
|
|
ASSERT(SIZEOF(*purlici) == purlici->dwcbSize);
|
|
|
|
if (SIZEOF(*purlici) == purlici->dwcbSize)
|
|
{
|
|
URLINVOKECOMMANDINFOW ici;
|
|
|
|
ici.dwcbSize = SIZEOF(ici);
|
|
ici.dwFlags = purlici->dwFlags;
|
|
ici.hwndParent = purlici->hwndParent;
|
|
|
|
ici.pcszVerb = NULL;
|
|
|
|
if (purlici->pcszVerb)
|
|
{
|
|
//
|
|
// App compat hack.
|
|
//
|
|
// Note: use local alloc here instead of the stack since 16-bit code
|
|
// can shell exec urls and we don't want to use up their stack.
|
|
//
|
|
|
|
int cch = lstrlenA(purlici->pcszVerb) + 1;
|
|
|
|
ici.pcszVerb = (LPWSTR)LocalAlloc(LPTR, cch * sizeof(WCHAR));
|
|
|
|
if (ici.pcszVerb)
|
|
{
|
|
AnsiToUnicode(purlici->pcszVerb, (LPWSTR)ici.pcszVerb, cch);
|
|
}
|
|
}
|
|
|
|
hres = InvokeCommand(&ici);
|
|
|
|
if (ici.pcszVerb)
|
|
{
|
|
LocalFree((void*)ici.pcszVerb);
|
|
ici.pcszVerb = NULL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP Intshcut::Create(REFFMTID fmtid, const CLSID *pclsid,
|
|
DWORD grfFlags, DWORD grfMode, IPropertyStorage **pppropstg)
|
|
{
|
|
*pppropstg = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Intshcut::Open(REFFMTID fmtid, DWORD grfMode, IPropertyStorage **pppropstg)
|
|
{
|
|
HRESULT hres = E_FAIL; // assume failure
|
|
|
|
*pppropstg = NULL;
|
|
|
|
if (IsEqualGUID(fmtid, FMTID_Intshcut))
|
|
{
|
|
// Create a URLProp object for this format ID
|
|
hres = CIntshcutProp_CreateInstance(NULL, IID_PPV_ARG(IPropertyStorage, pppropstg));
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Initialize this object
|
|
IntshcutProp * pisprop = (IntshcutProp *)*pppropstg;
|
|
hres = pisprop->InitFromFile(m_pszFile);
|
|
}
|
|
}
|
|
else if (IsEqualGUID(fmtid, FMTID_InternetSite))
|
|
{
|
|
// Create a URLProp object for this format ID
|
|
hres = CIntsiteProp_CreateInstance(NULL, IID_PPV_ARG(IPropertyStorage, pppropstg));
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = InitProp();
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
TCHAR szURL[MAX_URL_STRING];
|
|
hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IntsiteProp * pisprop = (IntsiteProp *)*pppropstg;
|
|
hres = pisprop->InitFromDB(szURL, this, FALSE);
|
|
}
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
(*pppropstg)->Release();
|
|
*pppropstg = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Intshcut::Delete(REFFMTID fmtid)
|
|
{
|
|
return STG_E_ACCESSDENIED;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Intshcut::Enum(OUT IEnumSTATPROPSETSTG ** ppenum)
|
|
{
|
|
*ppenum = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDAPI GetStringPropURL(IPropertyStorage *ppropstg, PROPID propid, LPTSTR pszBuf, DWORD cchBuf)
|
|
{
|
|
HRESULT hres = GetStringProp(ppropstg, propid, pszBuf, cchBuf);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// get rid of the query string for display
|
|
if (UrlIs(pszBuf, URLIS_HASQUERY))
|
|
UrlCombine(pszBuf, TEXT("?..."), pszBuf, &cchBuf, 0);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
BOOL Intshcut::_TryLink(REFIID riid, void **ppvOut)
|
|
{
|
|
HRESULT hr = InitProp();
|
|
|
|
if (SUCCEEDED(hr) && URL_SCHEME_FILE == GetScheme())
|
|
{
|
|
// This shortcut is not in the favorites folder as far as we know
|
|
TCHAR szURL[INTERNET_MAX_URL_LENGTH];
|
|
DWORD cch = SIZECHARS(szURL);
|
|
|
|
*szURL = 0;
|
|
|
|
m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
|
|
|
|
if (*szURL && SUCCEEDED(PathCreateFromUrl(szURL, szURL, &cch, 0)))
|
|
{
|
|
if (!_punkLink)
|
|
{
|
|
hr = _CreateShellLink(szURL, &_punkLink);
|
|
}
|
|
|
|
if (_punkLink)
|
|
{
|
|
if (SUCCEEDED(_punkLink->QueryInterface(riid, ppvOut)))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
ATOMICRELEASE(_punkLink);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STDMETHODIMP Intshcut::GetInfoTip(DWORD dwFlags, WCHAR **ppwszTip)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IQueryInfo *pqi;
|
|
|
|
if (_TryLink(IID_PPV_ARG(IQueryInfo, &pqi)))
|
|
{
|
|
hr = pqi->GetInfoTip(dwFlags, ppwszTip);
|
|
pqi->Release();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
static const ITEM_PROP c_rgTitleAndURL[] = {
|
|
{ &FMTID_InternetSite, PID_INTSITE_TITLE, GetStringProp, IDS_FAV_STRING },
|
|
{ &FMTID_Intshcut, PID_IS_URL, GetStringPropURL, IDS_FAV_STRING },
|
|
{ NULL, 0, 0, 0 },
|
|
};
|
|
|
|
hr = GetInfoTipFromStorage(SAFECAST(this, IPropertySetStorage *), c_rgTitleAndURL, ppwszTip);
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
STDMETHODIMP Intshcut::GetInfoFlags(DWORD *pdwFlags)
|
|
{
|
|
*pdwFlags = 0;
|
|
#if 0
|
|
// This Function is commented out since it has not been tested.
|
|
// It can be uncommented if we provide support for providing offline cursor
|
|
// for shortucts. I think this needs updates to listview in comctl -- BharatS
|
|
|
|
LPSTR pszURL;
|
|
if (S_OK == GetURL(&pszURL))
|
|
{
|
|
BOOL fCached = UrlIsCached(pszUrl);
|
|
if (!fCached)
|
|
{
|
|
CHAR szCanonicalizedUrlA[MAX_URL_STRING];
|
|
DWORD dwLen = ARRAYSIZE(szCanonicalizedUrlA);
|
|
InternetCanonicalizeUrlA(pszURL, szCanonicalizedUrlA, &dwLen, 0);
|
|
fCached = UrlIsMappedOrInCache(szCanonicalizedUrlA);
|
|
}
|
|
if (fCached)
|
|
*pdwFlags |= QIF_CACHED;
|
|
SHFree(pszURL);
|
|
}
|
|
return S_OK;
|
|
#else
|
|
return E_NOTIMPL;
|
|
#endif
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
IQueryCodePage:
|
|
*/
|
|
STDMETHODIMP Intshcut::GetCodePage(UINT * puiCodePage)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
*puiCodePage = 0; // NULL out the code page.
|
|
if (IsFlagSet(m_dwFlags, ISF_CODEPAGE))
|
|
{
|
|
*puiCodePage = m_uiCodePage;
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP Intshcut::SetCodePage(UINT uiCodePage)
|
|
{
|
|
SetFlag(m_dwFlags, ISF_CODEPAGE);
|
|
m_uiCodePage = uiCodePage;
|
|
return S_OK;
|
|
}
|
|
|
|
/***************************** Exported Functions ****************************/
|
|
|
|
|
|
// This function was ported from URL.DLL. Normally, since our
|
|
// internet shortcut object has a context menu handler, we don't
|
|
// call this function.
|
|
//
|
|
// Only one thing needs this entry point: Exchange. Sigh.
|
|
//
|
|
// Instead of simply calling ShellExecuteEx to handle opening file
|
|
// attachments, they grovel thru the registry themselves. Of course,
|
|
// their code is incomplete and thinks a file-association needs to
|
|
// have an explicit \shell\open\command that works before it executes
|
|
// it. Hmm, it brings to mind a phrase, like:
|
|
//
|
|
//
|
|
//
|
|
// So, we export this API so they will work. But really the invoke
|
|
// occurs in the context menu handler for normal cases.
|
|
//
|
|
|
|
|
|
STDAPI_(void) OpenURL(HWND hwndParent, HINSTANCE hinst, LPSTR pszCmdLine, int nShowCmd)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hrCoInit;
|
|
|
|
|
|
|
|
Intshcut * pIntshcut = new Intshcut; // This must be a 0 INITed memory allocation
|
|
WCHAR wszPath[MAX_PATH];
|
|
|
|
if (!pIntshcut)
|
|
return;
|
|
|
|
hrCoInit = SHCoInitialize(); // gets called from rundll32 in browser only mode - hence we need to
|
|
// make sure that OLE has been init'ed
|
|
|
|
|
|
|
|
ASSERT(IS_VALID_HANDLE(hwndParent, WND));
|
|
ASSERT(IS_VALID_HANDLE(hinst, INSTANCE));
|
|
ASSERT(IS_VALID_STRING_PTRA(pszCmdLine, -1));
|
|
ASSERT(IsValidShowCmd(nShowCmd));
|
|
|
|
// Assume the entire command line is an Internet Shortcut file path.
|
|
|
|
TrimWhiteSpaceA(pszCmdLine);
|
|
|
|
TraceMsgA(TF_INTSHCUT, "OpenURL(): Trying to open Internet Shortcut %s.",
|
|
pszCmdLine);
|
|
|
|
#ifndef UNIX
|
|
|
|
AnsiToUnicode(pszCmdLine, wszPath, SIZECHARS(wszPath));
|
|
hr = pIntshcut->LoadFromFile(wszPath);
|
|
|
|
#else /* UNIX */
|
|
|
|
#ifndef ANSI_SHELL32_ON_UNIX
|
|
// IEUNIX : Our Shell32 calls this function with unicode command line
|
|
hr = pIntshcut->LoadFromFile((LPWSTR)pszCmdLine);
|
|
#else
|
|
hr = pIntshcut->LoadFromFile(pszCmdLine);
|
|
#endif
|
|
|
|
#endif /* !UNIX */
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
URLINVOKECOMMANDINFO urlici;
|
|
|
|
urlici.dwcbSize = SIZEOF(urlici);
|
|
urlici.hwndParent = hwndParent;
|
|
urlici.pcszVerb = NULL;
|
|
urlici.dwFlags = (IURL_INVOKECOMMAND_FL_ALLOW_UI |
|
|
IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB);
|
|
|
|
hr = pIntshcut->InvokeCommand(&urlici);
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
MLShellMessageBox(
|
|
hwndParent,
|
|
MAKEINTRESOURCE(IDS_IS_LOADFROMFILE_FAILED),
|
|
MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
|
|
(MB_OK | MB_ICONEXCLAMATION),
|
|
wszPath);
|
|
}
|
|
|
|
pIntshcut->Release();
|
|
|
|
SHCoUninitialize(hrCoInit);
|
|
|
|
}
|
|
|
|
// INamedPropertyBag Methods
|
|
//
|
|
// Reads & writes properties from a section in the shortcut ini file
|
|
|
|
|
|
const TCHAR c_szSizeSuffix[] = TEXT("__Size");
|
|
|
|
|
|
STDMETHODIMP Intshcut::WritePropertyNPB(
|
|
LPCOLESTR pszSectionNameW,
|
|
/* [in] */ LPCOLESTR pszPropNameW,
|
|
/* [out][in] */ PROPVARIANT *pVar)
|
|
{
|
|
const TCHAR *pszSectionName;
|
|
const TCHAR *pszPropName;
|
|
HRESULT hr;
|
|
if((NULL == pszSectionNameW) || (NULL == pszPropNameW) || (NULL == pVar))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
if(S_OK != _CreateTemporaryBackingFile())
|
|
{
|
|
ASSERT(NULL == m_pszTempFileName);
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
ASSERT(m_pszTempFileName);
|
|
|
|
pszSectionName = pszSectionNameW;
|
|
pszPropName = pszPropNameW;
|
|
// Write the appropriate value in depending on the type
|
|
|
|
switch(pVar->vt)
|
|
{
|
|
// NOTE: (andrewgu) these types we also can round-trip using the same code pass as for
|
|
// unsigned types, except bharats in a codereview recommended we comment these out because
|
|
// they'll look goofy in the *.ini file (you wrote -5 but see 4294967290 junk instead).
|
|
// VT_UINT is not listed as "may appear in an OLE property set" in <wtypes.h>.
|
|
/* case VT_I1:
|
|
case VT_I2:
|
|
case VT_I4:
|
|
case VT_INT:
|
|
case VT_UINT: */
|
|
|
|
case VT_UI1:
|
|
case VT_UI2:
|
|
case VT_UI4:
|
|
hr = WriteUnsignedToFile(m_pszTempFileName, pszSectionName, pszPropName, pVar->ulVal);
|
|
break;
|
|
|
|
case VT_BSTR:
|
|
hr = WriteGenericString(m_pszTempFileName, pszSectionName, pszPropName, pVar->bstrVal);
|
|
break;
|
|
|
|
case VT_BLOB:
|
|
{
|
|
TCHAR *pszSizePropName = NULL;
|
|
int cchPropName = lstrlen(pszPropName) + ARRAYSIZE(c_szSizeSuffix) + 1;
|
|
DWORD dwAllocSize = cchPropName * sizeof(TCHAR);
|
|
|
|
pszSizePropName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, dwAllocSize);
|
|
if(pszSizePropName)
|
|
{
|
|
DWORD dwBufferSize;
|
|
StrCpyN(pszSizePropName, pszPropName, cchPropName);
|
|
StrCatBuff(pszSizePropName, c_szSizeSuffix, cchPropName);
|
|
|
|
// OK Now - we have the name for the size
|
|
// we write it out
|
|
|
|
dwBufferSize = pVar->blob.cbSize;
|
|
hr = WriteBinaryToFile(m_pszTempFileName, pszSectionName, pszSizePropName,
|
|
(LPVOID)(&dwBufferSize), sizeof(DWORD));
|
|
|
|
if(S_OK == hr)
|
|
{
|
|
// Write out the buffer
|
|
hr = WriteBinaryToFile(m_pszTempFileName, pszSectionName, pszPropName,
|
|
(LPVOID)(pVar->blob.pBlobData), dwBufferSize);
|
|
}
|
|
|
|
LocalFree((LPVOID)pszSizePropName);
|
|
pszSizePropName = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
hr = WriteBinaryToFile(m_pszTempFileName, pszSectionName, pszPropName, (LPVOID)pVar, sizeof(PROPVARIANT));
|
|
break;
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP Intshcut::ReadPropertyNPB(
|
|
/* [in] */ LPCOLESTR pszSectionNameW,
|
|
/* [in] */ LPCOLESTR pszPropNameW,
|
|
/* [out][in] */ PROPVARIANT *pVar)
|
|
{
|
|
const TCHAR *pszSectionName;
|
|
const TCHAR *pszPropName;
|
|
TCHAR *pszFileToReadFrom;
|
|
HRESULT hr;
|
|
|
|
if((NULL == pszSectionNameW) || (NULL == pszPropNameW) || (NULL == pVar))
|
|
{
|
|
if (NULL != pVar)
|
|
pVar->vt = VT_ERROR;
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
if(m_pszTempFileName)
|
|
{
|
|
pszFileToReadFrom = m_pszTempFileName;
|
|
}
|
|
else if(m_pszFile)
|
|
{
|
|
pszFileToReadFrom = m_pszFile;
|
|
}
|
|
else
|
|
{
|
|
pVar->vt = VT_EMPTY;
|
|
return S_FALSE;
|
|
}
|
|
|
|
pszSectionName = pszSectionNameW;
|
|
pszPropName = pszPropNameW;
|
|
|
|
switch(pVar->vt)
|
|
{
|
|
// NOTE: (andrewgu) these types we also can round-trip using the same code pass as for
|
|
// unsigned types, except bharats in a codereview recommended we comment these out because
|
|
// they'll look goofy in the *.ini file (you wrote -5 but see 4294967290 junk instead).
|
|
// VT_UINT is not listed as "may appear in an OLE property set" in <wtypes.h>.
|
|
/* case VT_I1:
|
|
case VT_I2:
|
|
case VT_I4:
|
|
case VT_INT:
|
|
case VT_UINT: */
|
|
|
|
case VT_UI1:
|
|
case VT_UI2:
|
|
case VT_UI4:
|
|
pVar->ulVal = 0;
|
|
hr = ReadUnsignedFromFile(pszFileToReadFrom, pszSectionName, pszPropName, &(pVar->ulVal));
|
|
break;
|
|
|
|
case VT_BSTR:
|
|
// It is a string
|
|
pVar->vt = VT_BSTR;
|
|
pVar->bstrVal = NULL;
|
|
hr = ReadBStrFromFile(pszFileToReadFrom, pszSectionName, pszPropName, &(pVar->bstrVal));
|
|
break;
|
|
|
|
case VT_BLOB:
|
|
{
|
|
TCHAR *pszSizePropName = NULL;
|
|
int cchPropName = lstrlen(pszPropName) + ARRAYSIZE(c_szSizeSuffix) + 1;
|
|
DWORD dwAllocSize = cchPropName * sizeof(TCHAR);
|
|
|
|
pszSizePropName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, dwAllocSize);
|
|
if(pszSizePropName)
|
|
{
|
|
DWORD dwBufferSize;
|
|
StrCpyN(pszSizePropName, pszPropName, cchPropName);
|
|
StrCatBuff(pszSizePropName, c_szSizeSuffix, cchPropName);
|
|
// Read the Size first
|
|
hr = ReadBinaryFromFile(pszFileToReadFrom, pszSectionName, pszSizePropName,
|
|
&dwBufferSize, sizeof(DWORD));
|
|
if(S_OK == hr)
|
|
{
|
|
|
|
pVar->blob.pBlobData = (unsigned char *)CoTaskMemAlloc(dwBufferSize);
|
|
if(pVar->blob.pBlobData)
|
|
{
|
|
hr = ReadBinaryFromFile(pszFileToReadFrom, pszSectionName, pszPropName,
|
|
pVar->blob.pBlobData, dwBufferSize);
|
|
|
|
if(S_OK == hr)
|
|
{
|
|
pVar->blob.cbSize = dwBufferSize;
|
|
}
|
|
else
|
|
{
|
|
CoTaskMemFree(pVar->blob.pBlobData);
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(pszSizePropName);
|
|
pszSizePropName = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// all else
|
|
PROPVARIANT tmpPropvar = {0};
|
|
|
|
hr = ReadBinaryFromFile(pszFileToReadFrom, pszSectionName, pszPropName, &tmpPropvar, sizeof(PROPVARIANT));
|
|
if((S_OK == hr) && (tmpPropvar.vt == pVar->vt))
|
|
{
|
|
memcpy(pVar, &tmpPropvar, sizeof(PROPVARIANT));
|
|
}
|
|
else
|
|
{
|
|
pVar->vt = VT_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if(hr != S_OK)
|
|
{
|
|
memset(pVar, 0, sizeof(PROPVARIANT));
|
|
pVar->vt = VT_EMPTY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP Intshcut::RemovePropertyNPB (
|
|
/* [in] */ LPCOLESTR pszSectionNameW,
|
|
/* [in] */ LPCOLESTR pszPropNameW)
|
|
{
|
|
const TCHAR *pszSectionName;
|
|
const TCHAR *pszPropName;
|
|
HRESULT hr;
|
|
TCHAR *pszFileToDeleteFrom;
|
|
|
|
// Return if there is no file name
|
|
if((NULL == pszSectionNameW) || (NULL == pszPropNameW))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if(m_pszTempFileName)
|
|
{
|
|
pszFileToDeleteFrom = m_pszTempFileName;
|
|
}
|
|
else if(m_pszFile)
|
|
{
|
|
pszFileToDeleteFrom = m_pszFile;
|
|
}
|
|
else
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
// Just delete the key corresponding to this property name
|
|
pszSectionName = pszSectionNameW;
|
|
pszPropName = pszPropNameW;
|
|
|
|
hr = SHDeleteIniString(pszSectionName, pszPropName, pszFileToDeleteFrom)? S_OK : E_FAIL;
|
|
|
|
return hr;
|
|
}
|