Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4178 lines
121 KiB

#include "priv.h"
#include "sccls.h"
#include "mlang.h" // fo char conversion
#include "bandprxy.h"
#include "resource.h"
#include <shdocvw.h>
#include <icwcfg.h>
#include <advpub.h> // for IE activesetup GUID
#include <shellapi.h>
#include "apithk.h" //for WM_KEYBOARDCUES msg
#include <platform.h>
#include <mobsync.h>
#include <mobsyncp.h>
#include "..\shell32\shitemid.h" // for SHID_XX
#ifdef UNIX
#include "unixstuff.h"
#endif /* UNIX */
#define MLUI_INIT
#include "mluisupp.h"
//small (previously duplicated) functions shared between shdcovw and browseui
#include "..\inc\brutil.cpp"
// #define MLUI_SUPPORT 1
LCID g_lcidLocale = MAKELCID(LANG_USER_DEFAULT, SORT_DEFAULT);
#define DM_NAV TF_SHDNAVIGATE
#define DM_ZONE TF_SHDNAVIGATE
#define DM_IEDDE DM_TRACE
#define DM_CANCELMODE 0
#define DM_UIWINDOW 0
#define DM_ENABLEMODELESS 0
#define DM_EXPLORERMENU 0
#define DM_BACKFORWARD 0
#define DM_PROTOCOL 0
#define DM_ITBAR 0
#define DM_STARTUP 0
#define DM_AUTOLIFE 0
#define DM_PALETTE 0
const VARIANT c_vaEmpty = {0};
const LARGE_INTEGER c_li0 = { 0, 0 };
#undef VariantCopy
WINOLEAUTAPI VariantCopyLazy(VARIANTARG * pvargDest, VARIANTARG * pvargSrc)
{
VariantClearLazy(pvargDest);
switch(pvargSrc->vt)
{
case VT_I4:
case VT_UI4:
case VT_BOOL:
// we can add more
*pvargDest = *pvargSrc;
return S_OK;
case VT_UNKNOWN:
if (pvargDest)
{
*pvargDest = *pvargSrc;
pvargDest->punkVal->AddRef();
return S_OK;
}
ASSERT(0);
return E_INVALIDARG;
}
return VariantCopy(pvargDest, pvargSrc);
}
//
// WARNING: This function must be placed at the end because we #undef
// VariantClear
//
#undef VariantClear
HRESULT VariantClearLazy(VARIANTARG *pvarg)
{
switch(pvarg->vt)
{
case VT_I4:
case VT_UI4:
case VT_EMPTY:
case VT_BOOL:
// No operation
break;
default:
return VariantClear(pvarg);
}
return S_OK;
}
HRESULT QueryService_SID_IBandProxy(IUnknown * punkParent, REFIID riid, IBandProxy ** ppbp, void **ppvObj)
{
HRESULT hr = E_FAIL;
if (ppbp)
{
if (NULL == (*ppbp))
hr = IUnknown_QueryService(punkParent, SID_IBandProxy, IID_PPV_ARG(IBandProxy, ppbp));
if (*ppbp && ppvObj)
hr = (*ppbp)->QueryInterface(riid, ppvObj); // They already have the object.
}
return hr;
}
HRESULT CreateIBandProxyAndSetSite(IUnknown * punkParent, REFIID riid, IBandProxy ** ppbp, void **ppvObj)
{
ASSERT(ppbp);
HRESULT hr = CoCreateInstance(CLSID_BandProxy, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandProxy, ppbp));
if (SUCCEEDED(hr))
{
// Set the site
ASSERT(*ppbp);
(*ppbp)->SetSite(punkParent);
if (ppvObj)
hr = (*ppbp)->QueryInterface(riid, ppvObj);
}
else
{
if (ppvObj)
*ppvObj = NULL;
}
return hr;
}
HRESULT IUnknown_FileSysChange(IUnknown* punk, DWORD dwEvent, LPCITEMIDLIST* ppidl)
{
HRESULT hres = E_FAIL;
if (punk)
{
IAddressBand * pab;
hres = punk->QueryInterface(IID_PPV_ARG(IAddressBand, &pab));
if (SUCCEEDED(hres))
{
hres = pab->FileSysChange(dwEvent, ppidl);
pab->Release();
}
}
return hres;
}
UINT g_cfURL = 0;
UINT g_cfHIDA = 0;
UINT g_cfFileDescA = 0;
UINT g_cfFileDescW = 0;
UINT g_cfFileContents = 0;
UINT g_cfPreferedEffect = 0;
void InitClipboardFormats()
{
if (g_cfURL == 0)
{
g_cfURL = RegisterClipboardFormat(CFSTR_SHELLURL);
g_cfHIDA = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
g_cfFileDescA = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
g_cfFileDescW = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
g_cfFileContents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
g_cfPreferedEffect = RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
}
}
DEFFOLDERSETTINGS g_dfs = INIT_DEFFOLDERSETTINGS;
void _InitDefaultFolderSettings()
{
if (GetSystemMetrics(SM_CLEANBOOT))
return;
g_dfs.vid = g_bRunOnNT5 ? VID_LargeIcons : DFS_VID_Default;
DEFFOLDERSETTINGS dfs = g_dfs;
DWORD dwType, cbData = sizeof(dfs);
if (SUCCEEDED(SKGetValue(SHELLKEY_HKCU_EXPLORER, REGVALUE_STREAMS, TEXT("Settings"), &dwType, &dfs, &cbData))
&& dwType == REG_BINARY)
{
if (cbData < sizeof(DEFFOLDERSETTINGS_W2K) || dfs.dwStructVersion < DFS_NASH_VER)
{
dfs.vid = g_bRunOnNT5 ? VID_LargeIcons : DFS_VID_Default;
dfs.dwStructVersion = DFS_NASH_VER;
dfs.bUseVID = TRUE;
}
if (cbData < sizeof(DEFFOLDERSETTINGS) || dfs.dwStructVersion < DFS_WHISTLER_VER)
{
dfs.dwViewPriority = VIEW_PRIORITY_CACHEMISS;
dfs.dwStructVersion = DFS_WHISTLER_VER;
}
g_dfs = dfs;
}
}
CABINETSTATE g_CabState = { 0 };
extern HANDLE g_hCabStateChange;
LONG g_lCabStateCount = -1; // never a valid count
void GetCabState(CABINETSTATE *pcs)
{
if (g_hCabStateChange == NULL)
g_hCabStateChange = SHGlobalCounterCreate(GUID_FolderSettingsChange);
LONG lCabStateCur = SHGlobalCounterGetValue(g_hCabStateChange);
if (g_lCabStateCount != lCabStateCur)
{
g_lCabStateCount = lCabStateCur;
if (!ReadCabinetState(&g_CabState, sizeof(g_CabState)))
{
WriteCabinetState(&g_CabState);
}
}
*pcs = g_CabState;
}
typedef struct tagINIPAIR
{
DWORD dwFlags;
LPCTSTR pszSection;
}
INIPAIR, *PINIPAIR;
const INIPAIR c_aIniPairs[] =
{
EICH_KINET, TEXT("Software\\Microsoft\\Internet Explorer"),
EICH_KINETMAIN, TEXT("Software\\Microsoft\\Internet Explorer\\Main"),
EICH_KWIN, TEXT("Software\\Microsoft\\Windows\\CurrentVersion"),
EICH_KWINEXPLORER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"),
EICH_KWINEXPLSMICO, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SmallIcons"),
EICH_KWINPOLICY, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies"),
EICH_SSAVETASKBAR, TEXT("SaveTaskbar"),
EICH_SWINDOWMETRICS, TEXT("WindowMetrics"),
EICH_SSHELLMENU, TEXT("ShellMenu"),
EICH_SPOLICY, TEXT("Policy"),
EICH_SWINDOWS, TEXT("Windows"),
};
DWORD SHIsExplorerIniChange(WPARAM wParam, LPARAM lParam)
{
DWORD dwFlags = 0;
if (lParam == 0)
{
if (wParam == 0)
{
dwFlags = EICH_UNKNOWN;
}
}
else
{
//
// In the wacky world of browseui, UNICODE-ANSI doesn't vary from
// window to window. Instead, on NT browseui registers all windows
// UNICODE, while on 9x user browseui registers all windows ANSI.
//
LPCTSTR pszSection;
TCHAR szTemp[MAX_PATH]; // not a filename, but a section/registry key name
if (g_fRunningOnNT)
{
#ifdef UNICODE
pszSection = (LPCTSTR)lParam;
#else
UINT cch = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)lParam, -1, szTemp, ARRAYSIZE(szTemp), NULL, NULL);
if (cch == 0)
{
szTemp[0] = TEXT('\0'); // it won't compare
}
#endif
}
else
{
#ifdef UNICODE
UINT cch = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lParam, -1, szTemp, ARRAYSIZE(szTemp));
if (cch == 0)
{
szTemp[0] = TEXT('\0'); // it won't compare
}
#else
pszSection = (LPCTSTR)lParam;
#endif
}
for (int i = 0; !dwFlags && i < ARRAYSIZE(c_aIniPairs); i++)
{
if (StrCmpI(pszSection, c_aIniPairs[i].pszSection) == 0)
{
dwFlags = c_aIniPairs[i].dwFlags;
}
}
}
return dwFlags;
}
void _InitAppGlobals()
{
static BOOL fInitialized = FALSE;
if (!fInitialized)
{
_InitComCtl32();
_InitDefaultFolderSettings();
// dont put anything else here. instead init on demand
fInitialized = TRUE; // allow a race on the above calls
}
}
BOOL _InitComCtl32()
{
static BOOL fInitialized = FALSE;
if (!fInitialized)
{
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_USEREX_CLASSES | ICC_COOL_CLASSES | ICC_INTERNET_CLASSES | ICC_PAGESCROLLER_CLASS
| ICC_NATIVEFNTCTL_CLASS;
fInitialized = InitCommonControlsEx(&icc);
}
return fInitialized;
}
DWORD GetPreferedDropEffect(IDataObject *pdtobj)
{
InitClipboardFormats();
DWORD dwEffect = 0;
STGMEDIUM medium;
DWORD *pdw = (DWORD *)DataObj_GetDataOfType(pdtobj, g_cfPreferedEffect, &medium);
if (pdw)
{
dwEffect = *pdw;
ReleaseStgMediumHGLOBAL(pdw,&medium);
}
return dwEffect;
}
HRESULT _SetPreferedDropEffect(IDataObject *pdtobj, DWORD dwEffect)
{
InitClipboardFormats();
HRESULT hres = E_OUTOFMEMORY;
DWORD *pdw = (DWORD *)GlobalAlloc(GPTR, sizeof(*pdw));
if (pdw)
{
STGMEDIUM medium;
FORMATETC fmte = {g_cfPreferedEffect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
*pdw = dwEffect;
medium.tymed = TYMED_HGLOBAL;
medium.hGlobal = pdw;
medium.pUnkForRelease = NULL;
hres = pdtobj->SetData(&fmte, &medium, TRUE);
if (FAILED(hres))
GlobalFree((HGLOBAL)pdw);
}
return hres;
}
//*** Reg_GetStrs -- get values from registry, assign to struct
void Reg_GetStrs(HKEY hkey, const struct regstrs *tab, LPTSTR szBuf, int cchBuf, void *pv)
{
for (; tab->name != NULL; tab++)
{
ULONG cbTmp = cchBuf;
// NOTE: IE4 did *NOT* support SHLoadRegUIString, so don't call Reg_GetStrs
// on roamable data. (Or at least don't register plugui strings there.)
if (ERROR_SUCCESS == SHLoadRegUIString(hkey, tab->name, szBuf, cbTmp))
{
// pv->field = StrDup(szBuf)
*(LPTSTR *)((char *)pv + tab->off) = StrDup(szBuf);
}
}
return;
}
BOOL g_fNewNotify = FALSE; // Are we using classic mode (W95 or new mode?
PFNSHCHANGENOTIFYREGISTER g_pfnSHChangeNotifyRegister = NULL;
PFNSHCHANGENOTIFYDEREGISTER g_pfnSHChangeNotifyDeregister = NULL;
#define GET_PRIVATE_PROC_ADDRESS(_hinst, _fname, _ord) GetProcAddress(_hinst, _ord)
BOOL _DelayLoadRegisterNotify(void)
{
// See if we need to still figure out which version of SHChange Notify to call?
if (g_pfnSHChangeNotifyDeregister == NULL)
{
// This should never fail, since we are load-time-linked to SHELL32
HMODULE hmodShell32 = GetModuleHandleA("SHELL32.DLL");
if (hmodShell32)
{
g_pfnSHChangeNotifyRegister = (PFNSHCHANGENOTIFYREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32, "NTSHChangeNotifyRegister",MAKEINTRESOURCEA(640));
if (g_pfnSHChangeNotifyRegister && (WhichPlatform() == PLATFORM_INTEGRATED))
{
g_pfnSHChangeNotifyDeregister = (PFNSHCHANGENOTIFYDEREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32,"NTSHChangeNotifyDeregister", MAKEINTRESOURCEA(641));
g_fNewNotify = TRUE;
}
else
{
g_pfnSHChangeNotifyRegister = (PFNSHCHANGENOTIFYREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32, "SHChangeNotifyRegister", MAKEINTRESOURCEA(2));
g_pfnSHChangeNotifyDeregister = (PFNSHCHANGENOTIFYDEREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32, "SHChangeNotifyDeregister",MAKEINTRESOURCEA(4));
}
}
}
return (NULL == g_pfnSHChangeNotifyDeregister) ? FALSE : TRUE;
}
ULONG RegisterNotify(HWND hwnd, UINT nMsg, LPCITEMIDLIST pidl, DWORD dwEvents, UINT uFlags, BOOL fRecursive)
{
if (_DelayLoadRegisterNotify())
{
SHChangeNotifyEntry fsne;
if (g_fNewNotify)
uFlags |= SHCNRF_NewDelivery;
fsne.fRecursive = fRecursive;
fsne.pidl = pidl;
return g_pfnSHChangeNotifyRegister(hwnd, uFlags, dwEvents, nMsg, 1, &fsne);
}
return 0;
}
int PropBag_ReadInt4(IPropertyBag* pPropBag, LPWSTR pszKey, int iDefault)
{
SHPropertyBag_ReadInt(pPropBag, pszKey, &iDefault);
return iDefault;
}
STDAPI_(BOOL) _EnsureLoaded(HINSTANCE *phinst, LPCSTR pszDLL)
{
if (*phinst == NULL)
{
#ifdef DEBUG
if (g_dwDumpFlags & DF_DELAYLOADDLL)
{
TraceMsg(TF_ALWAYS, "DLLLOAD: Loading %s for the first time", pszDLL);
}
if (g_dwBreakFlags & 0x00000080)
{
DebugBreak();
}
#endif
*phinst = LoadLibraryA(pszDLL);
if (*phinst == NULL)
{
return FALSE;
}
}
return TRUE;
}
// global g_hinst values
HINSTANCE g_hinstSHDOCVW = NULL;
HINSTANCE g_hinstShell32 = NULL;
HINSTANCE HinstShdocvw()
{
_EnsureLoaded(&g_hinstSHDOCVW, "shdocvw.dll");
return g_hinstSHDOCVW;
}
HINSTANCE HinstShell32()
{
_EnsureLoaded(&g_hinstShell32, "shell32.dll");
return g_hinstShell32;
}
STDAPI_(BOOL) CallCoInternetQueryInfo(LPCTSTR pszURL, QUERYOPTION QueryOption)
{
DWORD fRetVal;
DWORD dwSize;
WCHAR wszURL[MAX_URL_STRING];
SHTCharToUnicode(pszURL, wszURL, ARRAYSIZE(wszURL));
return SUCCEEDED(CoInternetQueryInfo(
wszURL, QueryOption,
0, &fRetVal, sizeof(fRetVal), &dwSize, 0)) && fRetVal;
}
HRESULT IURLQualifyW(IN LPCWSTR pcwzURL, DWORD dwFlags, OUT LPWSTR pwzTranslatedURL, LPBOOL pbWasSearchURL, LPBOOL pbWasCorrected)
{
return IURLQualify(pcwzURL, dwFlags, pwzTranslatedURL, pbWasSearchURL, pbWasCorrected);
}
BSTR LoadBSTR(HINSTANCE hinst, UINT uID)
{
WCHAR wszBuf[128];
if (LoadStringW(hinst, uID, wszBuf, ARRAYSIZE(wszBuf)))
{
return SysAllocString(wszBuf);
}
return NULL;
}
HRESULT _SetStdLocation(LPTSTR szPath, UINT id)
{
HRESULT hres = E_FAIL;
WCHAR szDefaultPath[MAX_URL_STRING];
ASSERT(id == DVIDM_GOHOME);
if (SUCCEEDED(URLSubLoadString(MLGetHinst(), IDS_DEF_HOME, szDefaultPath, SIZECHARS(szDefaultPath), URLSUB_ALL)))
{
if (!StrCmp(szDefaultPath, szPath))
return S_OK; // We don't need to write out the name string.
}
DWORD cbSize = (lstrlen(szPath) + 1) * sizeof(TCHAR);
if (ERROR_SUCCESS == SHSetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Main"), (id==DVIDM_GOHOME) ? TEXT("Start Page") : TEXT("Search Page"),
REG_SZ, (LPBYTE)szPath, cbSize))
{
hres = S_OK;
}
return hres;
}
//*** IsVK_TABCycler -- is key a TAB-equivalent
// ENTRY/EXIT
// dir 0 if not a TAB, non-0 if a TAB
// NOTES
// NYI: -1 for shift+tab, 1 for tab
//
int IsVK_TABCycler(MSG *pMsg)
{
int nDir = 0;
if (!pMsg)
return nDir;
if (pMsg->message != WM_KEYDOWN)
return nDir;
if (! (pMsg->wParam == VK_TAB || pMsg->wParam == VK_F6))
return nDir;
nDir = (GetKeyState(VK_SHIFT) < 0) ? -1 : 1;
#ifdef KEYBOARDCUES
HWND hwndParent = GetParent(pMsg->hwnd);
if (hwndParent)
SendMessage(hwndParent, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
#endif
return nDir ;
}
BOOL IsVK_CtlTABCycler(MSG *pMsg)
{
if (IsVK_TABCycler(pMsg))
{
if (GetKeyState(VK_CONTROL) < 0 || (pMsg->wParam == VK_F6))
return TRUE;
}
return FALSE;
}
const ITEMIDLIST s_idlNULL = { 0 } ;
// Copied from shell32 (was _ILCreate), which does not export this.
// The fsmenu code needs this function.
STDAPI_(LPITEMIDLIST) IEILCreate(UINT cbSize)
{
LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbSize);
if (pidl)
memset(pidl, 0, cbSize); // needed for external task allicator
return pidl;
}
void SaveDefaultFolderSettings(UINT flags)
{
ASSERT(!(flags & ~GFSS_VALID));
if (flags & GFSS_SETASDEFAULT)
g_dfs.dwDefRevCount++;
SKSetValue(SHELLKEY_HKCU_EXPLORER, REGVALUE_STREAMS, TEXT("Settings"), REG_BINARY, &g_dfs, sizeof(g_dfs));
}
BOOL ViewIDFromViewMode(UINT uViewMode, SHELLVIEWID *pvid)
{
switch (uViewMode)
{
case FVM_ICON:
*pvid = VID_LargeIcons;
break;
case FVM_SMALLICON:
*pvid = VID_SmallIcons;
break;
case FVM_LIST:
*pvid = VID_List;
break;
case FVM_DETAILS:
*pvid = VID_Details;
break;
case FVM_THUMBNAIL:
*pvid = VID_Thumbnails;
break;
case FVM_TILE:
*pvid = VID_Tile;
break;
default:
*pvid = VID_LargeIcons;
return(FALSE);
}
return(TRUE);
}
// This is a hack for IE6 23652 Beta 2. Remove in Whistler RC 1.
BOOL CheckForOutlookExpress()
{
HKEY hKeyMail = NULL;
HKEY hKeyOE = NULL;
DWORD dwErr = 0;
TCHAR szBuf[MAX_PATH];
BOOL bRet = FALSE;
// Open the key for default internet mail client
// HKLM\Software\Clients\Mail
dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Clients\\Mail"), 0, KEY_QUERY_VALUE, &hKeyMail);
if(dwErr != ERROR_SUCCESS)
{
// DebugTrace( TEXT("RegopenKey %s Failed -> %u\n"), szDefMailKey, dwErr);
goto out;
}
dwErr = SHRegGetString(hKeyMail, NULL, NULL, szBuf, ARRAYSIZE(szBuf));
if(dwErr != ERROR_SUCCESS)
{
goto out;
}
if(!lstrcmpi(szBuf, TEXT("Outlook Express")))
{
// Yes its outlook express ..
bRet = TRUE;
}
out:
if(hKeyOE)
RegCloseKey(hKeyOE);
if(hKeyMail)
RegCloseKey(hKeyMail);
return bRet;
}
HRESULT DropOnMailRecipient(IDataObject *pdtobj, DWORD grfKeyState)
{
IDropTarget *pdrop;
HRESULT hres = CoCreateInstance(CLSID_MailRecipient,
NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IID_PPV_ARG(IDropTarget, &pdrop));
ULONG_PTR uCookie = 0;
if (CheckForOutlookExpress())
{
SHActivateContext(&uCookie);
}
if (SUCCEEDED(hres))
{
hres = SHSimulateDrop(pdrop, pdtobj, grfKeyState, NULL, NULL);
pdrop->Release();
}
if (uCookie)
{
SHDeactivateContext(uCookie);
}
return hres;
}
//
// This function cannot return Non -NULL pointers if
// it returns a FAILED(hr)
//
HRESULT CreateShortcutSetSiteAndGetDataObjectIfPIDLIsNetUrl(
LPCITEMIDLIST pidl,
IUnknown *pUnkSite,
IUniformResourceLocator **ppUrlOut,
IDataObject **ppdtobj
)
{
HRESULT hr;
TCHAR szUrl[MAX_URL_STRING];
ASSERT(ppUrlOut);
ASSERT(ppdtobj);
*ppUrlOut = NULL;
*ppdtobj = NULL;
szUrl[0] = TEXT('\0');
hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szUrl, SIZECHARS(szUrl), NULL);
if ((S_OK == hr) && (*szUrl))
{
BOOL fIsHTML = FALSE;
BOOL fHitsNet = UrlHitsNetW(szUrl);
if (!fHitsNet)
{
if (URL_SCHEME_FILE == GetUrlScheme(szUrl))
{
TCHAR *szExt = PathFindExtension(szUrl);
if (szExt)
{
fIsHTML = ((0 == StrCmpNI(szExt, TEXT(".htm"),4)) ||
(0 == StrCmpNI(szExt, TEXT(".html"),5)));
}
}
}
if (fHitsNet || fIsHTML)
{
// Create a shortcut object and
HRESULT hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARG(IUniformResourceLocator, ppUrlOut));
if (SUCCEEDED(hr))
{
hr = (*ppUrlOut)->SetURL(szUrl, 0);
if (S_OK == hr)
{
// Get the IDataObject and send that back for the Drag Drop
hr = (*ppUrlOut)->QueryInterface(IID_PPV_ARG(IDataObject, ppdtobj));
if (SUCCEEDED(hr))
{
IUnknown_SetSite(*ppUrlOut, pUnkSite); // Only set the site if we're sure of
// returning SUCCESS
}
}
}
}
else
{
hr = E_FAIL;
}
}
if (FAILED(hr))
{
SAFERELEASE(*ppUrlOut);
SAFERELEASE(*ppdtobj);
}
return hr;
}
HRESULT SendDocToMailRecipient(LPCITEMIDLIST pidl, UINT uiCodePage, DWORD grfKeyState, IUnknown *pUnkSite)
{
IDataObject *pdtobj = NULL;
IUniformResourceLocator *purl = NULL;
HRESULT hr = CreateShortcutSetSiteAndGetDataObjectIfPIDLIsNetUrl(pidl, pUnkSite, &purl, &pdtobj);
if (FAILED(hr))
{
ASSERT(NULL == pdtobj);
ASSERT(NULL == purl);
hr = GetDataObjectForPidl(pidl, &pdtobj);
}
if (SUCCEEDED(hr))
{
IQueryCodePage * pQcp;
if (SUCCEEDED(pdtobj->QueryInterface(IID_PPV_ARG(IQueryCodePage, &pQcp))))
{
pQcp->SetCodePage(uiCodePage);
pQcp->Release();
}
hr = DropOnMailRecipient(pdtobj, grfKeyState);
pdtobj->Release();
}
if (purl)
{
IUnknown_SetSite(purl, NULL);
purl->Release();
}
return hr;
}
#ifdef DEBUG
/****************************************************\
FUNCTION: Dbg_PidlStr
DESCRIPTION:
Create a display name for the pidl passed in
and store the display name in pszBuffer.
\****************************************************/
LPTSTR Dbg_PidlStr(LPCITEMIDLIST pidl, LPTSTR pszBuffer, DWORD cchBufferSize)
{
if (pidl)
{
if (ILIsRooted(pidl))
{
StringCchCopy(pszBuffer, cchBufferSize, TEXT("<ROOTED>")); // ok to truncate for debug display only
}
else
{
IEGetNameAndFlags(pidl, SHGDN_FORPARSING, pszBuffer, cchBufferSize, NULL);
}
}
else
{
StringCchCopy(pszBuffer, cchBufferSize, TEXT("<NULL>")); // ok to truncate for debug display only
}
return pszBuffer;
}
#endif // DEBUG
#ifdef DEBUG
#define MAX_DEPTH 8
void Dbg_RecursiveDumpMenu(HMENU hmenu, int iDepth)
{
if (!hmenu || iDepth > MAX_DEPTH)
return;
TCHAR szTabs[MAX_DEPTH + 1];
for (int i = 0; i < iDepth; i++)
{
szTabs[i] = '\t';
}
szTabs[iDepth] = '\0';
int cItems = GetMenuItemCount(hmenu);
for (i = 0; i < cItems; i++)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_ID | MIIM_SUBMENU | MIIM_TYPE;
TCHAR szTmp[64];
mii.dwTypeData = szTmp;
mii.cch = ARRAYSIZE(szTmp);
if (GetMenuItemInfoWrap(hmenu, i, TRUE, &mii))
{
LPTSTR pszType;
if (mii.fType == MFT_STRING && mii.dwTypeData)
pszType = mii.dwTypeData;
else
pszType = TEXT("");
TraceMsg(TF_ALWAYS, "%swID %x\tfType %x\t%s", szTabs, mii.wID, mii.fType, pszType);
if (mii.hSubMenu)
{
Dbg_RecursiveDumpMenu(mii.hSubMenu, iDepth + 1);
}
}
}
}
// FUNCTION: Dbg_DumpMenu
//
// walk hmenu & dump every item
void Dbg_DumpMenu(LPCTSTR psz, HMENU hmenu)
{
if (IsFlagSet(g_dwDumpFlags, DF_DEBUGMENU))
{
TraceMsg(TF_ALWAYS, "Dumping hmenu %x (%s)", hmenu, psz);
Dbg_RecursiveDumpMenu(hmenu, 0);
TraceMsg(TF_ALWAYS, "End hmenu dump");
}
}
#endif
// evil evil evil. for browse only mode support. not the right way to do things
STDAPI LookForDesktopIniText(IShellFolder *psf, LPCITEMIDLIST pidl, LPCTSTR pszKey, LPTSTR pszBuffer, DWORD cbSize);
#define CLSID_SIZE 40
HRESULT LoadHandler(const CLSID * pCLSID, LPCWSTR pszBuffer, REFIID riid, void **ppvObj)
{
ASSERT(pszBuffer);
CLSID clsid;
if (!pCLSID)
{
// find the extension first ....
// REARCHITECT - Shouldn't this be PathFindExtension?
// Otherwise we will get confused by "foo.bar\baz"
LPCWSTR pszDot = StrRChrW(pszBuffer, NULL, WCHAR('.'));
if (!pszDot)
{
return E_NOINTERFACE;
}
HKEY hKey;
LONG lRes = RegOpenKeyEx(HKEY_CLASSES_ROOT, pszDot, 0, KEY_QUERY_VALUE, &hKey);
if (lRes != ERROR_SUCCESS)
{
return E_NOINTERFACE;
}
TCHAR szSubKey[CLSID_SIZE + 10];
TCHAR szCLSID[CLSID_SIZE];
DWORD dwType = REG_SZ;
lRes = ERROR_FILE_NOT_FOUND;
HRESULT hr = StringCchCopy(szSubKey, ARRAYSIZE(szSubKey), TEXT("shellex\\"));
if (SUCCEEDED(hr))
{
int cch = SHStringFromGUID(riid, szSubKey + 8, ARRAYSIZE(szSubKey) - 8);
if (cch != 0)
{
DWORD cbSize = sizeof(szCLSID);
// should we test for a value as well as a key ?
lRes = SHGetValue(hKey, szSubKey, TEXT(""), &dwType, szCLSID, &cbSize);
}
}
RegCloseKey(hKey);
if (lRes != ERROR_SUCCESS || dwType != REG_SZ)
{
return E_NOINTERFACE;
}
if (!GUIDFromString(szCLSID, &clsid))
{
return E_NOINTERFACE;
}
pCLSID = &clsid;
}
ASSERT(pCLSID);
IPersistFile *pFile;
HRESULT hr = CoCreateInstance(*pCLSID, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARG(IPersistFile, &pFile));
if (FAILED(hr))
{
return E_NOINTERFACE;
}
*ppvObj = NULL;
hr = pFile->Load(pszBuffer, TRUE);
if (SUCCEEDED(hr))
{
hr = pFile->QueryInterface(riid, ppvObj);
}
ATOMICRELEASE(pFile);
return hr;
}
// routine used to make us think it really came from the right place....
HRESULT FakeGetUIObjectOf(IShellFolder *psf, LPCITEMIDLIST pidl, UINT * prgfFlags, REFIID riid, void **ppvObj)
{
HRESULT hr = E_NOINTERFACE;
if (WhichPlatform() == PLATFORM_INTEGRATED)
{
// we are on Nashville try the new mechanism first...
hr = psf->GetUIObjectOf(NULL, 1, & pidl, riid, NULL, ppvObj);
if (SUCCEEDED(hr))
{
return hr;
}
}
// failure cases...
if (riid == IID_IExtractImage || riid == IID_IExtractLogo || riid == IID_IQueryInfo)
{
// make sure this hacked up code is only executed for browser only release....
// otherwise people will not register their stuff right and what a mess that will be.....
if (WhichPlatform() == PLATFORM_INTEGRATED)
{
return hr;
}
// try the IconExtractor first ....
IExtractIconA *pIcon;
hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IExtractIconA, NULL, &pIcon));
if (SUCCEEDED(hr))
{
if (riid != IID_IQueryInfo)
{
hr = pIcon->QueryInterface(IID_IExtractLogo, ppvObj);
ATOMICRELEASE(pIcon);
if (SUCCEEDED(hr))
return NOERROR;
}
else
{
hr = pIcon->QueryInterface(IID_IQueryInfo, ppvObj);
ATOMICRELEASE(pIcon);
//if someone is asking for an IQueryInfo, don't try giving them an IExtractImage
return hr;
}
}
// browser mode only hack so we can detect if we are asking for the normal logo or the wide one...
LPCTSTR pszTag = TEXT("Logo");
if (prgfFlags != NULL && *prgfFlags)
{
pszTag = TEXT("WideLogo");
}
TCHAR szBuffer[MAX_PATH];
hr = LookForDesktopIniText(psf, pidl, pszTag, szBuffer, ARRAYSIZE(szBuffer));
if (SUCCEEDED(hr))
{
// use IID_IExtractImage, this is the same interface as IExtractLogo, just IExtractLogo
// allows us to restrict the things that show up in Logo View...
hr = LoadHandler(NULL, szBuffer, IID_IExtractImage, ppvObj);
}
}
return hr;
}
BOOL GetInfoTipEx(IShellFolder* psf, DWORD dwFlags, LPCITEMIDLIST pidl, LPTSTR pszText, int cchTextMax)
{
BOOL fRet = FALSE;
*pszText = 0; // empty for failure
if (pidl)
{
IQueryInfo *pqi;
if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IQueryInfo, NULL, &pqi))))
{
WCHAR *pwszTip;
pqi->GetInfoTip(dwFlags, &pwszTip);
if (pwszTip)
{
fRet = TRUE;
SHUnicodeToTChar(pwszTip, pszText, cchTextMax);
SHFree(pwszTip);
}
pqi->Release();
}
else if (SUCCEEDED(FakeGetUIObjectOf(psf, pidl, 0, IID_PPV_ARG(IQueryInfo, &pqi))))
{
WCHAR *pwszTip;
pqi->GetInfoTip(0, &pwszTip);
if (pwszTip)
{
fRet = TRUE;
SHUnicodeToTChar(pwszTip, pszText, cchTextMax);
SHFree(pwszTip);
}
pqi->Release();
}
}
return fRet;
}
BOOL GetInfoTip(IShellFolder* psf, LPCITEMIDLIST pidl, LPTSTR pszText, int cchTextMax)
{
return GetInfoTipEx(psf, 0, pidl, pszText, cchTextMax);
}
#define MAX_CLASS 80 // From ..\shell32\fstreex.c
BOOL IsBrowsableShellExt(LPCITEMIDLIST pidl)
{
DWORD cb;
LPCTSTR pszExt;
TCHAR szFile[MAX_PATH];
TCHAR szProgID[MAX_CLASS];
TCHAR szCLSID[GUIDSTR_MAX], szCATID[GUIDSTR_MAX];
TCHAR szKey[GUIDSTR_MAX * 4];
HKEY hkeyProgID = NULL;
BOOL fRet = FALSE;
for (;;)
{
// Make sure we have a file extension
if (
!SHGetPathFromIDList(pidl, szFile)
||
((pszExt = PathFindExtension(szFile)) == NULL)
||
(pszExt[0] != TEXT('.'))
)
{
break;
}
// Get the ProgID.
cb = sizeof(szProgID);
if (
(SHGetValue(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, szProgID, &cb) != ERROR_SUCCESS)
||
(RegOpenKeyEx(HKEY_CLASSES_ROOT, szProgID, 0, KEY_QUERY_VALUE, &hkeyProgID) != ERROR_SUCCESS)
)
{
break;
}
// From the ProgID, get the CLSID.
cb = sizeof(szCLSID);
if (SHGetValue(hkeyProgID, TEXT("CLSID"), NULL, NULL, szCLSID, &cb) != ERROR_SUCCESS)
break;
// Construct the registry key that detects if
// a CLSID is a member of a CATID.
SHStringFromGUID(CATID_BrowsableShellExt, szCATID, ARRAYSIZE(szCATID));
HRESULT hr = StringCchPrintf(szKey, ARRAYSIZE(szKey),
TEXT("CLSID\\%s\\Implemented Categories\\%s"),
szCLSID, szCATID);
if (FAILED(hr))
break;
// See if it's there.
cb = 0;
if (SHGetValue(HKEY_CLASSES_ROOT, szKey, NULL, NULL, NULL, &cb) != ERROR_SUCCESS)
break;
fRet = TRUE;
break;
}
if (hkeyProgID != NULL)
RegCloseKey(hkeyProgID);
return fRet;
}
void OpenFolderPidl(LPCITEMIDLIST pidl)
{
SHELLEXECUTEINFO shei = { 0 };
shei.cbSize = sizeof(shei);
shei.fMask = SEE_MASK_INVOKEIDLIST;
shei.nShow = SW_SHOWNORMAL;
shei.lpIDList = (LPITEMIDLIST)pidl;
ShellExecuteEx(&shei);
}
void OpenFolderPath(LPCTSTR pszPath)
{
LPITEMIDLIST pidl = ILCreateFromPath(pszPath);
if (pidl)
{
OpenFolderPidl(pidl);
ILFree(pidl);
}
}
// NOTE: this is only called from browseui, why is it in the lib directory?
STDAPI UpdateSubscriptions()
{
#ifndef DISABLE_SUBSCRIPTIONS
HRESULT hr;
if (!SHRestricted2W(REST_NoManualUpdates, NULL, 0))
{
ISyncMgrSynchronizeInvoke *pSyncMgrInvoke;
hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IID_PPV_ARG(ISyncMgrSynchronizeInvoke, &pSyncMgrInvoke));
if (SUCCEEDED(hr))
{
hr = pSyncMgrInvoke->UpdateAll();
pSyncMgrInvoke->Release();
}
}
else
{
SHRestrictedMessageBox(NULL);
hr = S_FALSE;
}
return hr;
#else /* !DISABLE_SUBSCRIPTIONS */
return E_FAIL;
#endif /* !DISABLE_SUBSCRIPTIONS */
}
STDAPI_(int) _SHHandleUpdateImage(LPCITEMIDLIST pidlExtra)
{
SHChangeUpdateImageIDList * pUs = (SHChangeUpdateImageIDList*) pidlExtra;
if (!pUs)
{
return -1;
}
// if in the same process, or an old style notification
if (pUs->dwProcessID == GetCurrentProcessId())
{
return (int) pUs->iCurIndex;
}
else
{
WCHAR szBuffer[MAX_PATH];
int iIconIndex = pUs->iIconIndex;
UINT uFlags = pUs->uFlags;
HRESULT hr = StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), pUs->szName);
if (SUCCEEDED(hr))
{
// we are in a different process, look up the hash in our index to get the right one...
return Shell_GetCachedImageIndex(szBuffer, iIconIndex, uFlags);
}
else
{
return -1;
}
}
}
// As perf, share IShellLink implementations between bands and ask
// the bandsite for an implementation. Don't rely on the bandsite
// because you never know who will host us in the future. (And bandsite
// can change to not have us hosted at save/load time. Ex: it doesn't
// set our site before loading us from the stream, which sounds buggy.)
//
HRESULT SavePidlAsLink(IUnknown* punkSite, IStream *pstm, LPCITEMIDLIST pidl)
{
HRESULT hr = E_FAIL;
IShellLinkA* psl;
if (punkSite)
hr = IUnknown_QueryService(punkSite, IID_IBandSite, IID_PPV_ARG(IShellLinkA, &psl));
if (FAILED(hr))
hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &psl));
if (SUCCEEDED(hr))
{
IPersistStream *pps;
hr = psl->QueryInterface(IID_PPV_ARG(IPersistStream, &pps));
if (EVAL(SUCCEEDED(hr)))
{
ASSERT(pidl);
psl->SetIDList(pidl);
hr = pps->Save(pstm, FALSE);
// Win95 and NT4 shell32 have a bug in the CShellLink implementation
// THEY DON'T NULL TERMINATE THEIR "EXTRA DATA SECTION". This causes
// the object to trash the rest of the stream when it reads back in.
// Fix this by writing the NULL out in the Browser Only case.
if (SUCCEEDED(hr) && (PLATFORM_BROWSERONLY == WhichPlatform()))
{
DWORD dw = 0;
pstm->Write(&dw, sizeof(dw), NULL);
}
pps->Release();
}
psl->Release();
}
return hr;
}
HRESULT LoadPidlAsLink(IUnknown* punkSite, IStream *pstm, LPITEMIDLIST *ppidl)
{
IShellLinkA* psl;
HRESULT hr = IUnknown_QueryService(punkSite, IID_IBandSite, IID_PPV_ARG(IShellLinkA, &psl));
if (FAILED(hr))
hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &psl));
if (SUCCEEDED(hr))
{
IPersistStream *pps;
hr = psl->QueryInterface(IID_PPV_ARG(IPersistStream, &pps));
if (EVAL(SUCCEEDED(hr)))
{
hr = pps->Load(pstm);
if (EVAL(SUCCEEDED(hr)))
{
hr = psl->GetIDList(ppidl);
// Don't make me resolve the link because it's soo slow because
// it often loads 80k of networking dlls.
if (!EVAL(SUCCEEDED(hr)))
{
hr = psl->Resolve(NULL, SLR_NOUPDATE | SLR_NO_UI);
if (EVAL(SUCCEEDED(hr)))
hr = psl->GetIDList(ppidl);
}
hr = *ppidl ? S_OK : E_FAIL;
}
pps->Release();
}
psl->Release();
}
return hr;
}
// AdjustECPosition
//
// purpose: because FE NT always uses WCHAR position for ComboBoxEx32
// even though we're ANSI module for EM_GETSEL/EM_SETSEL,
// we need to adjust between WCHAR and TCHAR position.
// iType: ADJUST_TO_WCHAR_POS or ADJUST_TO_TCHAR_POS
//
int AdjustECPosition(char *psz, int iPos, int iType)
{
char *pstr = psz;
int iNewPos = iPos;
if (psz && g_fRunOnFE && g_fRunningOnNT)
{
if (ADJUST_TO_WCHAR_POS == iType)
{
iNewPos = 0;
while (*pstr && (pstr - psz != iPos))
{
pstr = CharNextA(pstr);
iNewPos++;
}
}
else if (ADJUST_TO_TCHAR_POS == iType)
{
while (*pstr && iPos--)
pstr = CharNextA(pstr);
iNewPos = (int)(pstr - psz);
}
}
return iNewPos;
}
int CALLBACK _CompareIDs(LPARAM p1, LPARAM p2, LPARAM psf)
{
HRESULT hr = ((IShellFolder*)psf)->CompareIDs(0, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
//ASSERT(SUCCEEDED(hr))
return (short)HRESULT_CODE(hr);
}
HDPA GetSortedIDList(LPITEMIDLIST pidl)
{
HDPA hdpa = DPA_Create(4);
if (hdpa)
{
IShellFolder* psf;
if (SUCCEEDED(IEBindToObject(pidl, &psf)))
{
LPENUMIDLIST penum;
SHELLSTATE ss = {0};
SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
if (S_OK == IShellFolder_EnumObjects(psf, NULL,
ss.fShowAllObjects ? SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN : SHCONTF_FOLDERS,
&penum))
{
LPITEMIDLIST pidl;
ULONG celt;
while (penum->Next(1, &pidl, &celt) == S_OK && celt == 1)
{
if (DPA_AppendPtr(hdpa, pidl) == -1)
{
SHFree(pidl);
}
}
penum->Release();
}
DPA_Sort(hdpa, (PFNDPACOMPARE)_CompareIDs, (LPARAM)psf);
psf->Release();
}
}
return hdpa;
}
int DPA_SHFreeCallback(void * p, void * d)
{
SHFree((LPITEMIDLIST)p);
return 1;
}
void FreeSortedIDList(HDPA hdpa)
{
DPA_DestroyCallback(hdpa, (PFNDPAENUMCALLBACK)DPA_SHFreeCallback, 0);
hdpa = NULL;
}
/****************************************************\
FUNCTION: StrCmpIWithRoot
PARAMETERS:
szDispNameIn - Str to see if it is the same as the
Display Name of the Root ISF.
fTotalStrCmp - If TRUE, pszDispNameIn has to completely equal the
Root's Display Name to succeed. If FALSE, only the first part
of pszDispNameIn needs to compare to the Root's Display Name
for this function to return successful.
ppszCachedRoot (In/Out Optional) - If this function will be called more than
once, this function will cache the string and make it run
quicker. The first time this function is called, (*ppszCachedRoot)
needs to be NULL. This function will allocate and the caller
needs to call LocalFree() when it's no longer needed.
DESCRIPTION:
This function will get the Display Name of the Root ISF (Desktop) and
see if the first cchDispNameComp chars of szDispNameIn
match that display name. S_OK will be returned if TRUE, and
S_FALSE if not.
\****************************************************/
HRESULT StrCmpIWithRoot(LPCTSTR pszDispNameIn, BOOL fTotalStrCmp, LPTSTR * ppszCachedRoot)
{
HRESULT hr;
TCHAR szDispNameTemp[MAX_PATH];
LPTSTR pszDispName = szDispNameTemp;
ASSERT(IS_VALID_STRING_PTR(pszDispNameIn, -1));
ASSERT(NULL == ppszCachedRoot || IS_VALID_WRITE_PTR(ppszCachedRoot, LPTSTR));
// Did the caller supply the display name of the namespace root?
if ((!ppszCachedRoot) ||
(ppszCachedRoot && !*ppszCachedRoot))
{
MLLoadString(IDS_DESKTOP, szDispNameTemp, SIZECHARS(szDispNameTemp));
// Cache this guy?
if (ppszCachedRoot)
{
// Yes
*ppszCachedRoot = StrDup(szDispNameTemp);
if (!*ppszCachedRoot)
return E_OUTOFMEMORY;
}
}
if (ppszCachedRoot && *ppszCachedRoot)
pszDispName = *ppszCachedRoot;
// Do we want to compare the entire string or just the first part of it?
if (fTotalStrCmp)
hr = (0 == lstrcmpi(pszDispName, pszDispNameIn)) ? S_OK : S_FALSE; // Entire String
else if (ppszCachedRoot)
{
// Compare the first part of the string
DWORD cchDispNameComp = lstrlen(*ppszCachedRoot);
hr = (0 == StrCmpNI(pszDispName, pszDispNameIn, cchDispNameComp)) ? S_OK : S_FALSE;
}
else
{
hr = S_FALSE;
}
return hr;
}
/****************************************************\
FUNCTION: GetMRUEntry
PARAMETERS:
hKey - Pointer to Registry Key to retrieve MRU entries from.
dwMRUIndex - 0 based MRU Index to retrieve.
pszMRUEntry - Location to store MRU Entry string.
cchMRUEntry - Size of Buffer in characters.
DESCRIPTION:
This function will retrieve the MRU Entry specified
by dwMRUIndex.
\****************************************************/
HRESULT GetMRUEntry(HKEY hKey, DWORD dwMRUIndex, LPTSTR pszMRUEntry, DWORD cchMRUEntry, LPITEMIDLIST * ppidl)
{
HRESULT hr = S_OK;
TCHAR szValueName[15]; // big enough for "url99999"
ASSERT(hKey);
ASSERT(pszMRUEntry);
ASSERT(cchMRUEntry);
// make a value name a la "url1" (1-based for historical reasons)
hr = StringCchPrintf(szValueName, ARRAYSIZE(szValueName), SZ_REGVAL_MRUENTRY, dwMRUIndex+1);
if (SUCCEEDED(hr))
{
cchMRUEntry *= sizeof(TCHAR);
if (ERROR_SUCCESS != SHQueryValueEx(hKey, szValueName, NULL, NULL, (LPBYTE) pszMRUEntry, &cchMRUEntry))
{
pszMRUEntry[0] = TEXT('\0');
hr = E_FAIL;
}
}
return hr;
}
/*----------------------------------------------------------
Purpose: Gets a registry value that is User Specifc.
This will open HKEY_CURRENT_USER if it exists,
otherwise it will open HKEY_LOCAL_MACHINE.
Returns: DWORD containing success or error code.
Cond: --
*/
LONG OpenRegUSKey(LPCTSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
{
DWORD dwRet = RegOpenKeyEx(HKEY_CURRENT_USER, lpSubKey, ulOptions, samDesired, phkResult);
if (ERROR_SUCCESS != dwRet)
dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, ulOptions, samDesired, phkResult);
return dwRet;
}
typedef struct tagSTREAMHEADER
{
DWORD dwHeaderSize;
DWORD dwDataSize;
DWORD dwSignature;
DWORD dwVersion;
} STREAMHEADER;
/****************************************************\
FUNCTION: LoadStreamHeader
PARAMETERS:
dwSignature - The signature that the caller supports
dwStartVersion - The lowest version the caller chooses to support.
dwEndVersion - The highest version the caller chooses to support.
pdwSize (OUT) - The size that the caller should read.
pdwVersionOut (OUT) - The version found.
return - if S_OK, then the caller needs to read pdwSize bytes
of data.
if S_FALSE, then the caller should use default settings
and return S_OK.
DESCRIPTION:
This function see if the caller owns this
data in the stream. If the caller does own the
stream segment, the size and version will be returned.
If the caller doesn't own the stream segment then S_FALSE is
returned to indicate that the caller should use default
data. If the caller doesn't claim to support the version
found (because of the dwStartVersion-dwEndVersion range),
then S_FALSE is returned to indicate to use default values
and this function skips over that segment in the stream
so the next segment can be parsed.
\****************************************************/
HRESULT LoadStreamHeader(IStream *pstm,
DWORD dwSignature, // What version?
DWORD dwStartVersion, // What is the earlies version supported?
DWORD dwEndVersion, // What is the oldest version supported?
DWORD * pdwSize, // What is the size to read?
DWORD * pdwVersionOut) // What version was found in the stream?
{
HRESULT hr;
STREAMHEADER shHeader;
BOOL fNotOurs = FALSE;
BOOL fSkipData = FALSE;
hr = pstm->Read(&shHeader, sizeof(shHeader), NULL);
ASSERT(pdwSize && pdwVersionOut);
*pdwSize = 0;
*pdwVersionOut = 0;
if (SUCCEEDED(hr))
{
if (shHeader.dwHeaderSize != sizeof(shHeader))
fNotOurs = TRUE;
else if (shHeader.dwSignature != dwSignature)
fNotOurs = TRUE;
else if (shHeader.dwVersion < dwStartVersion)
fSkipData = TRUE;
else if (shHeader.dwVersion > dwEndVersion)
fSkipData = TRUE;
if (fNotOurs)
{
// It's not, so reset it so the next guy will be able to read correctly.
LARGE_INTEGER li;
li.LowPart = (DWORD)-(LONG)sizeof(shHeader);
li.HighPart = 0;
hr = pstm->Seek(li, STREAM_SEEK_CUR, NULL);
hr = S_FALSE; // Means caller should use default data.
}
// Do we want to skip the Data for this part of the stream?
if (fSkipData)
{
ASSERT(STREAMSIZE_UNKNOWN != shHeader.dwDataSize); // SERIOUS, we cannot skip over data because we don't know size.
if (STREAMSIZE_UNKNOWN != shHeader.dwDataSize)
{
// Yes. The caller cannot read in this data because the caller doesn't support
// this version of the data. Therefore, we skip past the data and return S_FALSE
// to indicate to the caller that default settings should be used.
LARGE_INTEGER li;
li.LowPart = shHeader.dwDataSize;
li.HighPart = 0;
hr = pstm->Seek(li, STREAM_SEEK_CUR, NULL);
hr = S_FALSE; // Means caller should use default data.
}
}
if (!fNotOurs && !fSkipData)
{
*pdwSize = shHeader.dwDataSize;
*pdwVersionOut = shHeader.dwVersion;
}
}
return hr;
}
/****************************************************\
FUNCTION: SaveStreamHeader
DESCRIPTION:
This function will save a StreamHeader to
the stream that will allow the caller to verify
if he/she owns the data the next time it's read in.
It will also support the ability to ignore old
or future versions of data.
\****************************************************/
HRESULT SaveStreamHeader(IStream *pstm, DWORD dwSignature, DWORD dwVersion, DWORD dwSize)
{
HRESULT hr;
STREAMHEADER shHeader;
shHeader.dwHeaderSize = sizeof(STREAMHEADER);
shHeader.dwDataSize = dwSize;
shHeader.dwSignature = dwSignature;
shHeader.dwVersion = dwVersion;
hr = pstm->Write(&shHeader, sizeof(shHeader), NULL);
return hr;
}
//----------------------------------------------------------------------
//
// CMenuList
//
//----------------------------------------------------------------------
typedef struct
{
HMENU hmenu;
BITBOOL bObject:1; // TRUE: menu belongs to object
} MLITEM; // CMenuList item
CMenuList::CMenuList(void)
{
ASSERT(NULL == _hdsa);
}
CMenuList::~CMenuList(void)
{
if (_hdsa)
{
DSA_Destroy(_hdsa);
_hdsa = NULL;
}
}
/*----------------------------------------------------------
Purpose: Set the menu list (comparable to HOLEMENU) so we can
dispatch commands to the frame or the object correctly.
We do this since menu bands bypass OLE's FrameFilterWndProc.
We build the menu list by comparing the given hmenuShared
with hmenuFrame. Anything in hmenuShared that is not
in hmenuFrame belongs to the object.
*/
void CMenuList::Set(HMENU hmenuShared, HMENU hmenuFrame)
{
ASSERT(NULL == hmenuShared || IS_VALID_HANDLE(hmenuShared, MENU));
ASSERT(NULL == hmenuFrame || IS_VALID_HANDLE(hmenuFrame, MENU));
if (_hdsa)
{
ASSERT(IS_VALID_HANDLE(_hdsa, DSA));
DSA_DeleteAllItems(_hdsa);
}
else
_hdsa = DSA_Create(sizeof(MLITEM), 10);
if (_hdsa && hmenuShared && hmenuFrame)
{
int i;
int iFrame = 0;
int cmenu = GetMenuItemCount(hmenuShared);
int cmenuFrame = GetMenuItemCount(hmenuFrame);
BOOL bMatched;
int iSaveFrame;
int iHaveFrame = -1;
TCHAR sz[64];
TCHAR szFrame[64];
MENUITEMINFO miiFrame;
MENUITEMINFO mii;
MLITEM mlitem;
miiFrame.cbSize = sizeof(miiFrame);
miiFrame.hSubMenu = NULL;
mii.cbSize = sizeof(mii);
for (i = 0; i < cmenu; i++)
{
mii.cch = SIZECHARS(sz);
mii.fMask = MIIM_SUBMENU | MIIM_TYPE;
mii.dwTypeData = sz;
EVAL(GetMenuItemInfoWrap(hmenuShared, i, TRUE, &mii));
ASSERT(IS_VALID_HANDLE(mii.hSubMenu, MENU));
mlitem.hmenu = mii.hSubMenu;
iSaveFrame = iFrame;
bMatched = FALSE;
// DocObject might have dropped some of our menus, like edit and view
// Need to be able to skip over dropped frame menus
while (1)
{
if (iHaveFrame != iFrame)
{
iHaveFrame = iFrame;
if (iFrame < cmenuFrame)
{
miiFrame.cch = SIZECHARS(szFrame);
miiFrame.fMask = MIIM_SUBMENU | MIIM_TYPE;
miiFrame.dwTypeData = szFrame;
EVAL(GetMenuItemInfoWrap(hmenuFrame, iFrame, TRUE, &miiFrame));
}
else
{
// Make it so it won't compare
miiFrame.hSubMenu = NULL;
*szFrame = 0;
}
}
ASSERT(iFrame >= cmenuFrame || IS_VALID_HANDLE(miiFrame.hSubMenu, MENU));
// The browser may have a menu that was not merged into
// the shared menu because the object put one in with
// the same name. Have we hit this case? Check by comparing
// sz and szFrame
if (mii.hSubMenu == miiFrame.hSubMenu || 0 == StrCmp(sz, szFrame))
{
bMatched = TRUE;
break;
}
else
{
if (iFrame >= cmenuFrame)
{
break;
}
iFrame++;
}
}
// Is this one of our menus?
mlitem.bObject = (mii.hSubMenu == miiFrame.hSubMenu) ? FALSE:TRUE;
if (bMatched)
{
iFrame++;
}
else
{
iFrame = iSaveFrame;
}
DSA_SetItem(_hdsa, i, &mlitem);
}
}
}
/*----------------------------------------------------------
Purpose: Adds the given hmenu to the list.
*/
void CMenuList::AddMenu(HMENU hmenu)
{
ASSERT(NULL == hmenu || IS_VALID_HANDLE(hmenu, MENU));
if (_hdsa && hmenu)
{
MLITEM mlitem;
mlitem.hmenu = hmenu;
mlitem.bObject = TRUE;
DSA_AppendItem(_hdsa, &mlitem);
}
}
/*----------------------------------------------------------
Purpose: Removes the given hmenu from the list.
*/
void CMenuList::RemoveMenu(HMENU hmenu)
{
ASSERT(NULL == hmenu || IS_VALID_HANDLE(hmenu, MENU));
if (_hdsa && hmenu)
{
int i = DSA_GetItemCount(_hdsa) - 1;
for (; i >= 0; i--)
{
MLITEM * pmlitem = (MLITEM *)DSA_GetItemPtr(_hdsa, i);
ASSERT(pmlitem);
if (hmenu == pmlitem->hmenu)
{
DSA_DeleteItem(_hdsa, i);
break;
}
}
}
}
/*----------------------------------------------------------
Purpose: Returns TRUE if the given hmenu belongs to the object.
*/
BOOL CMenuList::IsObjectMenu(HMENU hmenu)
{
BOOL bRet = FALSE;
ASSERT(NULL == hmenu || IS_VALID_HANDLE(hmenu, MENU));
if (_hdsa && hmenu)
{
int i;
for (i = 0; i < DSA_GetItemCount(_hdsa); i++)
{
MLITEM * pmlitem = (MLITEM *)DSA_GetItemPtr(_hdsa, i);
ASSERT(pmlitem);
if (hmenu == pmlitem->hmenu)
{
bRet = pmlitem->bObject;
break;
}
}
}
return bRet;
}
#ifdef DEBUG
void CMenuList::Dump(LPCTSTR pszMsg)
{
if (IsFlagSet(g_dwDumpFlags, DF_DEBUGMENU))
{
TraceMsg(TF_ALWAYS, "CMenuList: Dumping menus for %#08x %s", this, pszMsg);
if (_hdsa)
{
int i;
for (i = 0; i < DSA_GetItemCount(_hdsa); i++)
{
MLITEM * pmlitem = (MLITEM *)DSA_GetItemPtr(_hdsa, i);
ASSERT(pmlitem);
TraceMsg(TF_ALWAYS, " [%d] = %x", i, pmlitem->hmenu);
}
}
}
}
#endif
#define REGVAL_FIRST_HOME_PAGE TEXT("First Home Page")
#define REGVAL_UPDATE_CHECK_PAGE TEXT("Update_Check_Page")
#define REGVAL_UPDATE_CHECK_INTERVAL TEXT("Update_Check_Interval")
#define REGVAL_LASTCHECKEDHI TEXT("LastCheckedHi")
#define REGSTR_PATH_INFODEL_REST TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\Infodelivery\\Restrictions")
#define REGVAL_IEUPDATECHECK_REST TEXT("NoUpdateCheck")
#define DEFAULT_IEUPDATECHECK_PAGE TEXT("http://www.microsoft.com/isapi/redir.dll?Prd=ie&Pver=5.0&Ar=ie5update&O1=b1")
BOOL
IsUpdateCheckRestricted()
{
HKEY hkeyRest = 0;
BOOL bUpdateCheckRest = FALSE;
DWORD dwValue = 0;
DWORD dwLen = sizeof(DWORD);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_INFODEL_REST, 0, KEY_QUERY_VALUE, &hkeyRest) == ERROR_SUCCESS)
{
if (ERROR_SUCCESS == SHRegGetDWORD(hkeyRest, NULL, REGVAL_IEUPDATECHECK_REST, &dwValue)
&& dwValue)
{
bUpdateCheckRest = TRUE;
}
RegCloseKey(hkeyRest);
}
if (!bUpdateCheckRest)
{
// Check to see if the user has turned it off under advanced options
dwValue = 0;
dwLen = sizeof(DWORD);
if (SHRegGetUSValue(REGSTR_PATH_MAIN, REGVAL_IEUPDATECHECK_REST, NULL, (LPBYTE)&dwValue, &dwLen, 0,NULL,0) == ERROR_SUCCESS && dwValue)
bUpdateCheckRest = TRUE;
}
return bUpdateCheckRest;
}
HRESULT
CheckIEMinimalUpdate()
{
HRESULT hr = S_OK;
HKEY hkeyIE = 0;
TCHAR szUpdateUrl[MAX_URL_STRING];
DWORD dwSize;
DWORD dwType;
FILETIME ftlast, ftnow;
DWORD dwMagicDays = 0;
DWORD dwMagicPerDay = 201;
if (IsUpdateCheckRestricted())
{
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
goto Exit;
}
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_MAIN, 0, KEY_QUERY_VALUE, &hkeyIE))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
if (ERROR_SUCCESS != SHRegGetDWORD(hkeyIE, NULL, REGVAL_UPDATE_CHECK_INTERVAL, &dwMagicDays)
|| dwMagicDays == 0)
{
dwMagicDays = 30; // hardcode default to check every 30 days.
}
if (ERROR_SUCCESS != SHRegGetString(hkeyIE, NULL, REGVAL_UPDATE_CHECK_PAGE, szUpdateUrl, ARRAYSIZE(szUpdateUrl)))
{
hr = StringCchCopy(szUpdateUrl, ARRAYSIZE(szUpdateUrl), DEFAULT_IEUPDATECHECK_PAGE);
if (FAILED(hr))
{
szUpdateUrl[0] = TEXT('\0');
}
}
RegCloseKey(hkeyIE);
if (RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_MAIN, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyIE) == ERROR_SUCCESS)
{
dwType = REG_SZ;
dwSize = MAX_URL_STRING;
if (RegQueryValueEx(hkeyIE, REGVAL_FIRST_HOME_PAGE, NULL, &dwType,
NULL, &dwSize) == ERROR_SUCCESS)
{
// if already exists then skip this write
hr = S_FALSE;
goto Exit;
}
GetSystemTimeAsFileTime(&ftnow);
ftnow.dwLowDateTime = 0;
ZeroMemory(&ftlast, sizeof(ftlast));
if (ERROR_SUCCESS == SHRegGetDWORD(hkeyIE, NULL, REGVAL_LASTCHECKEDHI, &ftlast.dwHighDateTime))
{
ftlast.dwHighDateTime += (dwMagicPerDay * dwMagicDays);
}
if (CompareFileTime(&ftlast, &ftnow) > 0)
{
hr = S_FALSE;
}
else if (szUpdateUrl[0] == TEXT('\0'))
{
hr = E_FAIL;
}
else
{
RegSetValueEx(hkeyIE,REGVAL_FIRST_HOME_PAGE, NULL,
REG_SZ,(LPBYTE)szUpdateUrl, (lstrlen(szUpdateUrl)+1)*sizeof(TCHAR));
RegSetValueEx(hkeyIE, REGVAL_LASTCHECKEDHI, NULL, REG_DWORD,
(unsigned char *)&ftnow.dwHighDateTime, sizeof(DWORD));
}
RegCloseKey(hkeyIE);
}
Exit:
return hr;
}
static BOOL s_fSUCheckComplete = FALSE;
// returns:
// TRUE The user clicked Update Now and we ShellExe'ed
// the update URL.
// FALSE We did not launch a browser to the update page.
// NOTE: the "run-once-ness" of this is controlled by the ICW check
// variable g_fICWCheckComplete.
BOOL CheckSoftwareUpdateUI(HWND hwndOwner, IShellBrowser *pisb)
{
BOOL fLaunchUpdate = FALSE;
#ifndef UNIX
HRESULT hr = S_OK;
int nRes;
SOFTDISTINFO sdi = { 0 };
sdi.cbSize = sizeof(SOFTDISTINFO);
if (s_fSUCheckComplete)
return FALSE;
else
s_fSUCheckComplete = TRUE;
// We're putting up a message box, so make the msg pump modal
pisb->EnableModelessSB(FALSE);
nRes = SoftwareUpdateMessageBox(hwndOwner, awchMSIE4GUID, 0, &sdi);
pisb->EnableModelessSB(TRUE);
if (nRes != IDABORT)
{
if (nRes == IDYES)
{
// Okay, we tried to do this a couple of different ways.
// Originally, this was done with ShellExecEx. This failed
// because the http hook wasn't 100% reliable on Win95.
// The next stab was to:
//LPITEMIDLIST pidl;
// The user wants to navigate to the install page.
//hr = pibs->IEParseDisplayName(CP_ACP, sdi.szHREF, &pidl);
//if (SUCCEEDED(hr))
//{
// OpenFolderPidl(pidl);
// ILFree(pidl);
//}
hr = NavToUrlUsingIEW(sdi.szHREF, TRUE);
} // if user wants update
if (sdi.szTitle != NULL)
CoTaskMemFree(sdi.szTitle);
if (sdi.szAbstract != NULL)
CoTaskMemFree(sdi.szAbstract);
if (sdi.szHREF != NULL)
CoTaskMemFree(sdi.szHREF);
fLaunchUpdate = nRes == IDYES && SUCCEEDED(hr);
}
if (!fLaunchUpdate)
{
// for minimal install of IE every N days or so we want to
// hijack the home page to check if an update is available
// for us.
CheckIEMinimalUpdate();
}
#endif
return fLaunchUpdate;
}
BOOL g_fICWCheckComplete = FALSE;
// returns:
// TRUE Internet Connection Wizard (ICW) was run, and we should exit
// the browser since we likely need to restart the system
// FALSE did not run the ICW, continue on as normal
BOOL CheckRunICW(LPCTSTR pszURL)
{
if (g_fICWCheckComplete)
return FALSE;
DWORD dwICWCompleted = 0;
BOOL fRet = FALSE;
// Check if ICW has already been run
DWORD dwSize = sizeof(dwICWCompleted);
SHGetValue(HKEY_CURRENT_USER, TEXT(ICW_REGPATHSETTINGS), TEXT(ICW_REGKEYCOMPLETED), NULL, &dwICWCompleted, &dwSize);
if (!dwICWCompleted)
{
HINSTANCE hInetCfgDll = LoadLibrary(TEXT("inetcfg.dll"));
// set this to TRUE here so that if there's an error in loading the dll, or getting the proc address,
// we don't keep trying to do that.
g_fICWCheckComplete = TRUE;
if (hInetCfgDll)
{
PFNCHECKCONNECTIONWIZARD fp = (PFNCHECKCONNECTIONWIZARD)GetProcAddress(hInetCfgDll, "CheckConnectionWizard");
if (fp)
{
DWORD dwRet;
DWORD dwFlags = ICW_LAUNCHFULL | ICW_LAUNCHMANUAL | ICW_FULL_SMARTSTART;
if (pszURL)
{
PFNSETSHELLNEXT fpSetShellNext = (PFNSETSHELLNEXT)GetProcAddress(hInetCfgDll, "SetShellNext");
if (fpSetShellNext)
{
CHAR szAnsiUrl[MAX_URL_STRING];
SHTCharToAnsi(pszURL, szAnsiUrl, ARRAYSIZE(szAnsiUrl));
dwFlags |= ICW_USE_SHELLNEXT;
fpSetShellNext(szAnsiUrl);
}
}
// if we get this far, set the fICWCheckComplete back to FALSE (had to be false since we didn't early out)
// and let the ICW set the reg key. this is so that if the user decides to cancel and come back later,
// we respect that.
g_fICWCheckComplete = FALSE;
// Launch ICW full or manual path, whichever is available
// NOTE: the ICW code makes sure only a single instance is up
fp(dwFlags, &dwRet);
// If it was launched successfully, we need to exit
// since ICW may restart the machine if it needs to
// install system files.
if (dwRet & (ICW_LAUNCHEDFULL | ICW_LAUNCHEDMANUAL))
{
fRet = TRUE;
}
}
FreeLibrary(hInetCfgDll);
}
}
else
{
g_fICWCheckComplete = TRUE;
}
return fRet;
}
int GetColorComponent(LPSTR *ppsz)
{
int iColor = 0;
if (*ppsz)
{
LPSTR pBuf = *ppsz;
iColor = StrToIntA(pBuf);
// find the next comma
while(pBuf && *pBuf && *pBuf!=L',')
pBuf++;
// if valid and not NULL...
if (pBuf && *pBuf)
pBuf++; // increment
*ppsz = pBuf;
}
return iColor;
}
// Read the registry for a string (REG_SZ) of comma separated RGB values
COLORREF RegGetColorRefString(HKEY hkey, LPTSTR RegValue, COLORREF Value)
{
CHAR SmallBuf[80];
CHAR szRegKey[MAXIMUM_SUB_KEY_LENGTH];
LPSTR pszBuf;
DWORD cb;
int iRed, iGreen, iBlue;
SHTCharToAnsi(RegValue, szRegKey, ARRAYSIZE(szRegKey));
cb = sizeof(SmallBuf);
if (SHQueryValueExA(hkey, szRegKey, NULL, NULL, (LPBYTE)&SmallBuf, &cb)
== ERROR_SUCCESS)
{
pszBuf = SmallBuf;
iRed = GetColorComponent(&pszBuf);
iGreen = GetColorComponent(&pszBuf);
iBlue = GetColorComponent(&pszBuf);
// make sure all values are valid
iRed %= 256;
iGreen %= 256;
iBlue %= 256;
Value = RGB(iRed, iGreen, iBlue);
}
return Value;
}
LRESULT SetHyperlinkCursor(IShellFolder* pShellFolder, LPCITEMIDLIST pidl)
{
HCURSOR hCursor;
BOOL fCursorSet = FALSE;
if (!pidl)
return 0;
if (SHIsGlobalOffline())
{
IQueryInfo *pqi;
if (SUCCEEDED(pShellFolder->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IQueryInfo, NULL, &pqi))))
{
DWORD dwFlags = 0;
if (SUCCEEDED(pqi->GetInfoFlags(&dwFlags)))
{
if (0 == (dwFlags & QIF_CACHED))
{
// Load Offline cursor since not cached
hCursor = (HCURSOR)LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_OFFLINE_HAND));
if (hCursor)
{
SetCursor(hCursor);
fCursorSet = TRUE;
}
}
}
pqi->Release();
}
}
if (!fCursorSet)
{
// For whatever reason, offline cursor was not set
hCursor = LoadHandCursor(0);
if (hCursor)
SetCursor(hCursor);
}
return 1;
}
BOOL IsSubscribableA(LPCSTR pszUrl)
{
// REARCHITECT: this should be method on the subscription mgr interface - zekel
DWORD dwScheme = GetUrlSchemeA(pszUrl);
return (dwScheme == URL_SCHEME_HTTP) || (dwScheme == URL_SCHEME_HTTPS);
}
BOOL IsSubscribableW(LPCWSTR pwzUrl)
{
// REARCHITECT: this should be method on the subscription mgr interface - zekel
DWORD dwScheme = GetUrlSchemeW(pwzUrl);
return (dwScheme == URL_SCHEME_HTTP) || (dwScheme == URL_SCHEME_HTTPS);
}
HWND GetTrayWindow()
{
#ifndef UNIX
static HWND s_hwndTray = NULL;
if (!IsWindow(s_hwndTray))
{
s_hwndTray = FindWindow(TEXT(WNDCLASS_TRAYNOTIFY), NULL);
}
return s_hwndTray;
#else
return NULL;
#endif
}
void FireEventSzA(LPCSTR szEvent)
{
HANDLE hEvent = OpenEventA(EVENT_MODIFY_STATE, FALSE, szEvent);
if (hEvent)
{
SetEvent(hEvent);
CloseHandle(hEvent);
}
}
void FireEventSzW(LPCWSTR pszEvent)
{
USES_CONVERSION;
FireEventSzA(W2A(pszEvent));
}
BOOL IsNamedWindow(HWND hwnd, LPCTSTR pszClass)
{
#ifndef UNIX
TCHAR szClass[32];
#else // UNIX use this function for trident dialog window
TCHAR szClass[64];
#endif
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
return lstrcmp(szClass, pszClass) == 0;
}
BOOL IsExplorerWindow(HWND hwnd)
{
return IsNamedWindow(hwnd, c_szExploreClass);
}
BOOL IsFolderWindow(HWND hwnd)
{
TCHAR szClass[32];
GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
return (lstrcmp(szClass, c_szCabinetClass) == 0) || (lstrcmp(szClass, c_szIExploreClass) == 0);
}
// returns TRUE if the unknown is on a window that was opened as IE
// returns FALSE if the window was opened on the shell namespace, even if it's now showing a web page
// returns FALSE in other cases e.g. on the taskbar
STDAPI_(BOOL) WasOpenedAsBrowser(IUnknown *punkSite)
{
// this is a more reliable way of distinguishing windows opened for a URL. Checking
// the hwnd's classname does not work -- clicking on a hyperlink from Outlook 98 opens
// a browser window with a shell window's classname.
return (S_OK == IUnknown_QueryServiceExec(punkSite, SID_STopLevelBrowser, &CGID_Explorer, SBCMDID_STARTEDFORINTERNET, 0, NULL, NULL));
}
#define DXTRACK 1
void FrameTrack(HDC hdc, LPRECT prc, UINT uFlags)
{
COLORREF clrSave, clr;
RECT rc;
// upperleft
switch (uFlags)
{
case TRACKHOT:
clr = GetSysColor(COLOR_BTNHILIGHT);
break;
case TRACKNOCHILD:
case TRACKEXPAND:
clr = GetSysColor(COLOR_BTNSHADOW);
break;
default:
ASSERT(FALSE);
break;
}
clrSave = SetBkColor(hdc, clr);
rc = *prc;
rc.bottom = rc.top + DXTRACK;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
rc.bottom = prc->bottom;
rc.right = rc.left + DXTRACK;
rc.top = prc->top + DXTRACK;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
// lowerright
switch (uFlags)
{
case TRACKHOT:
clr = GetSysColor(COLOR_BTNSHADOW);
break;
case TRACKNOCHILD:
case TRACKEXPAND:
clr = GetSysColor(COLOR_BTNHILIGHT);
break;
default:
ASSERT(FALSE);
break;
}
SetBkColor(hdc, clr);
if (uFlags & (TRACKHOT | TRACKNOCHILD))
{
rc.right = prc->right;
rc.top = rc.bottom - DXTRACK;
rc.left = prc->left;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
}
rc.right = prc->right;
rc.left = prc->right - DXTRACK;
rc.top = prc->top;
rc.bottom = prc->bottom;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
SetBkColor(hdc, clrSave);
return;
}
#ifdef DEBUG // {
//*** SearchDW -- scan for DWORD in buffer
// ENTRY/EXIT
// pdwBuf buffer
// cbBuf size of buffer in *bytes* (*not* DWORDs)
// dwVal DWORD we're looking for
// dOff (return) byte offset in buffer; o.w. -1 if not found
//
int SearchDWP(DWORD_PTR *pdwBuf, int cbBuf, DWORD_PTR dwVal)
{
int dOff;
for (dOff = 0; dOff < cbBuf; dOff += sizeof(DWORD_PTR), pdwBuf++)
{
if (*pdwBuf == dwVal)
return dOff;
}
return -1;
}
#endif // }
int CAssociationList::FindEntry(DWORD dwKey)
{
if (_hdsa)
{
for (int i = 0; i < DSA_GetItemCount(_hdsa); i++)
{
ASSOCDATA* pad;
pad = (ASSOCDATA*)DSA_GetItemPtr(_hdsa, i);
if (pad->dwKey == dwKey)
return i;
}
}
return -1;
}
HRESULT CAssociationList::Find(DWORD dwKey, void ** ppData)
{
HRESULT hr = E_FAIL;
ENTERCRITICAL;
int i = FindEntry(dwKey);
if (i != -1)
{
ASSOCDATA* pad = (ASSOCDATA*)DSA_GetItemPtr(_hdsa, i);
ASSERT(dwKey == pad->dwKey);
*ppData = pad->lpData;
hr = S_OK;
}
LEAVECRITICAL;
return hr;
}
void CAssociationList::Delete(DWORD dwKey)
{
ENTERCRITICAL;
int i = FindEntry(dwKey);
if (i != -1)
{
DSA_DeleteItem(_hdsa, i);
}
LEAVECRITICAL;
}
BOOL CAssociationList::Add(DWORD dwKey, void *lpData)
{
ENTERCRITICAL;
if (!_hdsa)
{
_hdsa = DSA_Create(sizeof(ASSOCDATA), 4);
}
LEAVECRITICAL;
BOOL fRet = FALSE;
if (_hdsa)
{
ASSOCDATA ad;
ad.dwKey = dwKey;
ad.lpData = lpData;
ENTERCRITICAL;
fRet = DSA_AppendItem(_hdsa, &ad) != -1;
LEAVECRITICAL;
}
return fRet;
}
int g_cxSmIcon = 0;
int g_cySmIcon = 0;
HIMAGELIST g_himlSysSmall = NULL;
void _InitSmallImageList()
{
if (!g_himlSysSmall)
{
Shell_GetImageLists(NULL, &g_himlSysSmall);
ImageList_GetIconSize(g_himlSysSmall, &g_cxSmIcon, &g_cySmIcon);
}
}
#define CXIMAGEGAP 6
STDAPI_(void) DrawMenuItem(DRAWITEMSTRUCT* lpdi, LPCTSTR lpszMenuText, UINT iIcon)
{
_InitSmallImageList();
if ((lpdi->itemAction & ODA_SELECT) || (lpdi->itemAction & ODA_DRAWENTIRE))
{
int x, y;
SIZE sz;
RECT rc;
// Draw the image (if there is one).
GetTextExtentPoint32(lpdi->hDC, lpszMenuText, lstrlen(lpszMenuText), &sz);
if (lpdi->itemState & ODS_SELECTED)
{
SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
}
else
{
SetTextColor(lpdi->hDC, GetSysColor(COLOR_MENUTEXT));
FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_MENU));
}
rc = lpdi->rcItem;
rc.left += +2 * CXIMAGEGAP + g_cxSmIcon;
DrawText(lpdi->hDC, lpszMenuText, lstrlen(lpszMenuText), &rc, DT_SINGLELINE | DT_VCENTER | DT_EXPANDTABS);
if (iIcon != -1)
{
x = lpdi->rcItem.left + CXIMAGEGAP;
y = (lpdi->rcItem.bottom + lpdi->rcItem.top - g_cySmIcon) / 2;
ImageList_Draw(g_himlSysSmall, iIcon, lpdi->hDC, x, y, ILD_TRANSPARENT);
}
else
{
x = lpdi->rcItem.left + CXIMAGEGAP;
y = (lpdi->rcItem.bottom + lpdi->rcItem.top - g_cySmIcon) / 2;
}
}
}
STDAPI_(LRESULT) MeasureMenuItem(MEASUREITEMSTRUCT *lpmi, LPCTSTR lpszMenuText)
{
LRESULT lres = FALSE;
if (0 == g_cxSmIcon)
{
_InitSmallImageList();
}
// Get the rough height of an item so we can work out when to break the
// menu. User should really do this for us but that would be useful.
HDC hdc = GetDC(NULL);
if (hdc)
{
// REVIEW cache out the menu font?
NONCLIENTMETRICSA ncm;
ncm.cbSize = sizeof(ncm);
if (SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
{
HFONT hfont = CreateFontIndirectA(&ncm.lfMenuFont);
if (hfont)
{
SIZE sz;
HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
GetTextExtentPoint32(hdc, lpszMenuText, lstrlen(lpszMenuText), &sz);
lpmi->itemHeight = max (g_cySmIcon+CXIMAGEGAP/2, ncm.iMenuHeight);
lpmi->itemWidth = g_cxSmIcon + 2*CXIMAGEGAP + sz.cx;
SelectObject(hdc, hfontOld);
DeleteObject(hfont);
lres = TRUE;
}
}
ReleaseDC(NULL, hdc);
}
return lres;
}
//+-------------------------------------------------------------------------
// This function scans the document for the given HTML tag and returns the
// result in a collection.
//--------------------------------------------------------------------------
HRESULT GetDocumentTags
(
IHTMLDocument2 * pHTMLDocument, // doc to search
LPOLESTR pszTagName, // tag name to search for
IHTMLElementCollection ** ppTagsCollection // returned collection
)
{
HRESULT hr;
*ppTagsCollection = NULL;
//
// First get all document elements
//
IHTMLElementCollection * pAllCollection;
if (SUCCEEDED(hr = pHTMLDocument->get_all(&pAllCollection)))
{
//
// Now get all the elements with tags == pszTagName
//
VARIANT v;
v.vt = VT_BSTR;
v.bstrVal = ::SysAllocString(pszTagName);
if (v.bstrVal)
{
IDispatch * pDispTagsCollection;
if (SUCCEEDED(hr = pAllCollection->tags(v, &pDispTagsCollection)))
{
hr = pDispTagsCollection->QueryInterface(IID_PPV_ARG(IHTMLElementCollection, ppTagsCollection));
pDispTagsCollection->Release();
}
pAllCollection->Release();
VariantClear(&v);
}
}
return hr;
}
// This function uses the memory allocator from comctrl (which differs between NT and W95)
BOOL WINAPI Str_SetPtrPrivateW(WCHAR FAR * UNALIGNED * ppwzCurrent, LPCWSTR pwzNew)
{
LPWSTR pwzNewCopy = NULL;
if (pwzNew)
{
pwzNewCopy = StrDup(pwzNew);
if (!pwzNewCopy)
return FALSE;
}
LPWSTR pwzOld = (LPWSTR)InterlockedExchangePointer((void * *)ppwzCurrent, (void *)pwzNewCopy);
if (pwzOld)
LocalFree(pwzOld);
return TRUE;
}
// This function is compatible with API's that use LocalAlloc for string memory
BOOL WINAPI SetStr(WCHAR FAR * UNALIGNED * ppwzCurrent, LPCWSTR pwzNew)
{
int cchLength;
LPWSTR pwzOld;
LPWSTR pwzNewCopy = NULL;
if (pwzNew)
{
cchLength = lstrlenW(pwzNew);
// alloc a new buffer w/ room for the null terminator
pwzNewCopy = (LPWSTR)LocalAlloc(LPTR, (cchLength + 1) * sizeof(WCHAR));
if (!pwzNewCopy)
return FALSE;
HRESULT hr = StringCchCopy(pwzNewCopy, cchLength + 1, pwzNew);
if (FAILED(hr))
{
LocalFree(pwzNewCopy);
return FALSE;
}
}
pwzOld = (LPWSTR)InterlockedExchangePointer((void * *)ppwzCurrent, (void *)pwzNewCopy);
if (pwzOld)
LocalFree(pwzOld);
return TRUE;
}
//---------------------------------------------------------------------------
// If the string contains &ch or begins with ch then return TRUE.
BOOL _MenuCharMatch(LPCTSTR lpsz, TCHAR ch, BOOL fIgnoreAmpersand)
{
LPTSTR pchAS = StrChr(lpsz, TEXT('&')); // Find the first ampersand.
if (pchAS && !fIgnoreAmpersand)
{
// Yep, is the next char the one we want.
if (CharUpperChar(*CharNext(pchAS)) == CharUpperChar(ch))
{
// Yep.
return TRUE;
}
}
else if (CharUpperChar(*lpsz) == CharUpperChar(ch))
{
return TRUE;
}
return FALSE;
}
// Review chrisny: this can be moved into an object easily to handle generic droptarget, dropcursor
// , autoscrool, etc. . .
void _DragEnter(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtObject)
{
RECT rc;
POINT pt;
GetWindowRect(hwndTarget, &rc);
//
// If hwndTarget is RTL mirrored, then measure the
// the client point from the visual right edge
// (near edge in RTL mirrored windows). [samera]
//
if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
pt.x = rc.right - ptStart.x;
else
pt.x = ptStart.x - rc.left;
pt.y = ptStart.y - rc.top;
DAD_DragEnterEx2(hwndTarget, pt, pdtObject);
return;
}
void _DragMove(HWND hwndTarget, const POINTL ptStart)
{
RECT rc;
POINT pt;
GetWindowRect(hwndTarget, &rc);
//
// If hwndTarget is RTL mirrored, then measure the
// the client point from the visual right edge
// (near edge in RTL mirrored windows). [samera]
//
if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
pt.x = rc.right - ptStart.x;
else
pt.x = ptStart.x - rc.left;
pt.y = ptStart.y - rc.top;
DAD_DragMove(pt);
return;
}
HRESULT CheckDesktopIni(LPCTSTR pszPath, LPCTSTR pszKey, LPTSTR pszBuffer, DWORD cchSize)
{
// NOTE:
// NOTE: DO NOT COPY THIS CODE. We only do this here for channels because we expect
// NOTE: the 99% case to be that it succeeds. If you need to find out if it is a
// NOTE: system folder, then you need to hack the pidl to get the system bit
// NOTE:
DWORD dwAttrs = GetFileAttributes(pszPath);
if (dwAttrs == (DWORD) -1 || !(dwAttrs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
return E_NOINTERFACE;
TCHAR szDIPath[MAX_PATH];
if (!PathCombine(szDIPath, pszPath, TEXT("desktop.ini")))
{
return E_FAIL;
}
if (pszKey == NULL)
{
if (GetFileAttributes(szDIPath) == (DWORD) -1)
{
return E_FAIL;
}
}
else
{
GetPrivateProfileString(TEXT(".ShellClassInfo"), pszKey, TEXT(""), pszBuffer, cchSize, szDIPath);
if (*pszBuffer == 0)
return E_NOINTERFACE;
// if its not a URL, then
if (!PathIsURL(pszBuffer))
{
if (!PathCombine(pszBuffer, pszPath, pszBuffer))
{
return E_FAIL;
}
}
}
return NOERROR;
}
STDAPI LookForDesktopIniText(IShellFolder *psf, LPCITEMIDLIST pidl, LPCTSTR pszKey, LPTSTR pszBuffer, DWORD cchSize)
{
TCHAR szPath[MAX_PATH];
DWORD ulFlags = SFGAO_FOLDER | SFGAO_FILESYSTEM;
HRESULT hr = GetPathForItem(psf, pidl, szPath, &ulFlags);
if (SUCCEEDED(hr) && (ulFlags & SFGAO_FOLDER))
{
hr = CheckDesktopIni(szPath, pszKey, pszBuffer, cchSize);
}
return hr;
}
// this is for channel category folders
HRESULT FakeGetNavigateTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl)
{
HRESULT hres = E_FAIL;
WIN32_FIND_DATAA wfd;
// before looking for a desktop.ini (which hits the disk), cheaply
// see if it's a system folder.
//
// SHGetDataFromIDListA returns E_INVALIDARG on IE4.0 shell32 and
// IE4.01 shell32. It is fixed in IE4.01qfe shell32 and IE4.01sp1
// shell32. It also appears to work on NT4 and W95 shell32. If
// SHGetDataFromIDListA returns E_INVALIDARG we drop through and
// do the slow LookForDesktopIniText call.
//
HRESULT hresTemp = SHGetDataFromIDListA(psf, pidl, SHGDFIL_FINDDATA, &wfd, sizeof(wfd));
// on win95 non integrated, only the A version is implemented
if ((E_INVALIDARG == hresTemp) ||
(SUCCEEDED(hresTemp) &&
(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(wfd.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))))
{
// final failure case, check to see if it is a system folder with a desktop.ini file. This is gross and slow,
// but this is only called when the user has navigated so we can be a little slower. Please do NOT COPY this
// code or DavidDS will get really really upset and perf will be bad.
TCHAR szBuffer[MAX_URL_STRING];
// this will check for SFGAO_FOLDER & SFGAO_FILESYSTEM first...
hres = LookForDesktopIniText(psf, pidl, TEXT("URL"), szBuffer, ARRAYSIZE(szBuffer));
if (SUCCEEDED(hres))
{
DWORD dwChar = ARRAYSIZE(szBuffer);
// this call uses a temp, so we can reuse the buffer...
hres = UrlCreateFromPath(szBuffer, szBuffer, &dwChar, 0);
if (SUCCEEDED(hres))
hres = IECreateFromPath(szBuffer, ppidl);
}
}
else
{
hres = E_FAIL;
}
return hres;
}
// Can we browse or navigate to this pidl? If not, need
BOOL ILIsBrowsable(LPCITEMIDLIST pidl, BOOL *pfIsFolder)
{
if (!pidl)
return FALSE;
DWORD dwAttributes = SFGAO_FOLDER | SFGAO_BROWSABLE;
HRESULT hr = IEGetAttributesOf(pidl, &dwAttributes);
if (pfIsFolder && SUCCEEDED(hr))
*pfIsFolder = dwAttributes & SFGAO_FOLDER;
return SUCCEEDED(hr) && (dwAttributes & (SFGAO_FOLDER | SFGAO_BROWSABLE));
}
// gets a target pidl given a name space item. typically this is a .lnk or .url file
//
// in:
// psf shell folder for item
// pidl item relative to psf, single level
//
// in/out
// pdwAttribs [optional] attributes mask to filter on (returned).
// must be initalized
//
//
// returns
// *ppidl the target pidl
// *pdwAttribs [optional] attributes of the source object
STDAPI SHGetNavigateTarget(IShellFolder *psf, LPCITEMIDLIST pidl,
LPITEMIDLIST *ppidl, DWORD *pdwAttribs)
{
ASSERT(IS_VALID_CODE_PTR(psf, IShellFolder));
ASSERT(IS_VALID_PIDL(pidl));
ASSERT(NULL == pdwAttribs || IS_VALID_WRITE_PTR(pdwAttribs, DWORD));
ASSERT(ILFindLastID(pidl) == pidl); // must be single level PIDL
*ppidl = NULL; // assume failure
DWORD dwAttribs = SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_LINK | SFGAO_BROWSABLE;
if (pdwAttribs)
dwAttribs |= *pdwAttribs;
HRESULT hres = psf->GetAttributesOf(1, &pidl, &dwAttribs);
if (SUCCEEDED(hres))
{
// first try the most efficient way
IShellLinkA *psl; // "A" so this works on Win95
hres = psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IShellLinkA, NULL, &psl));
if (SUCCEEDED(hres))
{
hres = psl->GetIDList(ppidl);
psl->Release();
}
// this is for .lnk and .url files that don't register properly
if (FAILED(hres) && (dwAttribs & (SFGAO_FILESYSTEM | SFGAO_LINK)) == (SFGAO_FILESYSTEM | SFGAO_LINK))
{
TCHAR szPath[MAX_PATH];
hres = GetPathForItem(psf, pidl, szPath, NULL);
if (SUCCEEDED(hres))
hres = GetLinkTargetIDList(szPath, NULL, 0, ppidl);
}
// .doc or .html. return the pidl for this.
// (fully qualify against the folder pidl)
if (FAILED(hres) && (dwAttribs & SFGAO_BROWSABLE))
{
LPITEMIDLIST pidlFolder;
hres = SHGetIDListFromUnk(psf, &pidlFolder);
if (SUCCEEDED(hres))
{
*ppidl = ILCombine(pidlFolder, pidl); // navigate to this thing...
hres = *ppidl ? S_OK : E_OUTOFMEMORY;
ILFree(pidlFolder);
}
}
// channel name space items on non integrated
if (FAILED(hres) && WhichPlatform() != PLATFORM_INTEGRATED)
{
IExtractIconA *pxicon; // Use IID_IExtractIconA so we work on W95.
hres = psf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IExtractIconA, NULL, &pxicon));
if (SUCCEEDED(hres))
{
hres = pxicon->QueryInterface(IID_PPV_ARG(IShellLinkA, &psl));
if (SUCCEEDED(hres))
{
hres = psl->GetIDList(ppidl);
psl->Release();
}
pxicon->Release();
}
}
// Callers of SHGetNavigateTarget assume that the returned pidl
// is navigatable (SFGAO_FOLDER or SFGAO_BROWSER), which isn't
// the case for a link (it could be a link to an exe).
//
if (SUCCEEDED(hres) && !ILIsBrowsable(*ppidl, NULL))
{
ILFree(*ppidl);
*ppidl = NULL;
hres = E_FAIL;
}
if (SUCCEEDED(hres) && pdwAttribs)
*pdwAttribs = dwAttribs;
}
return hres;
}
BOOL CreateShortcutAndDoDragDropIfPIDLIsNetUrl(IOleCommandTarget *pcmdt, LPITEMIDLIST pidl, HWND hwnd)
{
IUniformResourceLocator *purl;
IDataObject *pdtobj;
HRESULT hr = CreateShortcutSetSiteAndGetDataObjectIfPIDLIsNetUrl(pidl, pcmdt, &purl, &pdtobj);
if (SUCCEEDED(hr))
{
ASSERT(pdtobj);
ASSERT(purl);
// REARCHITECT: we should be binding to the parent and getting the attributes
// to determine the allowed effects - like we do in DragDrop()
DWORD dwEffect = (DROPEFFECT_COPY | DROPEFFECT_LINK);
::_SetPreferedDropEffect(pdtobj, DROPEFFECT_LINK);
// Win95 Browser Only - the shell32 in this process doesn't know
// ole is loaded, even though it is.
SHLoadOLE(SHELLNOTIFY_OLELOADED);
hr = SHDoDragDrop(hwnd, pdtobj, NULL, dwEffect, &dwEffect);
// the returned value is not S_OK even tho' the drag drop succeeded
// however it is a success return
if (SUCCEEDED(hr))
{
// Since drag drop succeeded
// Bring down the icon for this shortcut
IUnknown_Exec(purl, &CGID_ShortCut, ISHCUTCMDID_DOWNLOADICON, 0, NULL, NULL);
}
pdtobj->Release();
IUnknown_SetSite(purl, NULL);
purl->Release();
}
return SUCCEEDED(hr);
}
BOOL DoDragDropWithInternetShortcut(IOleCommandTarget *pcmdt, LPITEMIDLIST pidl, HWND hwnd)
{
BOOL fDragDropDone = CreateShortcutAndDoDragDropIfPIDLIsNetUrl(pcmdt, pidl, hwnd);
if (FALSE == fDragDropDone)
{
// simply use PIDL and get none of the persistence effect
fDragDropDone = SUCCEEDED(DragDrop(hwnd, NULL, pidl, DROPEFFECT_LINK, NULL));
}
return fDragDropDone;
}
STDAPI_(HWND) GetTopLevelAncestor(HWND hWnd)
{
HWND hwndTemp;
while ((hwndTemp=GetParent(hWnd)) != NULL)
{
hWnd = hwndTemp;
}
return(hWnd);
}
#if 0
BOOL IsIERepairOn()
{
static DWORD dwChecked = -1;
if (dwChecked == -1)
{
DWORD dwSize, dwType;
// First check the OS setting. On NT5 and Win98-OSR, Repair is Off.
// OS turned Off Repair ==> "DisableRepair" RegValue is set to 1.
dwChecked = 1; // The default Repair is ON
dwSize = sizeof(dwChecked);
if (SHRegGetUSValue(SZ_REGKEY_ACTIVE_SETUP, SZ_REGVALUE_DISABLE_REPAIR, &dwType, (void *) &dwChecked, &dwSize, TRUE, (void *)NULL, 0) == ERROR_SUCCESS)
{
// OS Reg setting of 0 ==> Repair is ON
// OS Reg setting of 1 ==> Repair is OFF
dwChecked = (dwChecked == 0) ? 1 : 0;
}
else
{
dwChecked = 1; // if we fail to read Reg, go back to default.
}
// Check for Admin policy only if OS setting leaves Repair On.
if (dwChecked == 1)
{
dwSize = sizeof(dwChecked);
if (SHRegGetUSValue(SZ_REGKEY_IE_POLICIES, SZ_REGVALUE_IEREPAIR, &dwType, (void *) &dwChecked, &dwSize, TRUE, (void *)NULL, 0) != ERROR_SUCCESS)
{
dwChecked = 1; // if we fail to read Reg, go back to default.
}
}
}
return (dwChecked == 1);
}
#endif
BOOL IsResetWebSettingsEnabled(void)
{
static BOOL fUseCache = FALSE; // have we already looked up the answer in the registry?
static BOOL fEnabled; // is the feature enabled or disabled?
if (!fUseCache)
{
DWORD dwData;
DWORD dwSize = sizeof(dwData);
DWORD dwType;
//
// Next time, we'll use the cached value instead of
// looking in the registry
//
fUseCache = TRUE;
//
// Look up the appropriate ieak value in the registry
//
if (ERROR_SUCCESS == SHRegGetUSValue(
SZ_REGKEY_INETCPL_POLICIES,
SZ_REGVALUE_RESETWEBSETTINGS,
&dwType,
(void *)&dwData,
&dwSize,
FALSE,
NULL,
0))
{
//
// If the value was found in the registry, then
// set fEnabled accordingly
//
fEnabled = !dwData;
}
else
{
//
// If the value is missing from the registry, then
// assume the feature is enabled
//
fEnabled = TRUE;
}
}
return fEnabled;
}
STDAPI_(BOOL) InitOCHostClass(const SHDRC * pshdrc)
{
// It would be nice to remove this, but since it was exported, we keep it here for compat
RIPMSG(FALSE, "This export is dead, caller needs to call SHDOCVW!DllRegisterWindowClasses directly");
return DllRegisterWindowClasses(pshdrc);
}
STDAPI SHNavigateToFavorite(IShellFolder* psf, LPCITEMIDLIST pidl, IUnknown* punkSite, DWORD dwFlags)
{
HRESULT hres = S_FALSE;
TCHAR szPath[MAX_PATH];
// Can we navigate to this favorite?
BOOL fNavigateDone = SUCCEEDED(GetPathForItem(psf, pidl, szPath, NULL)) &&
SUCCEEDED(NavFrameWithFile(szPath, punkSite));
if (fNavigateDone)
return S_OK;
LPITEMIDLIST pidlGoto;
ASSERT(!(dwFlags & (SBSP_NEWBROWSER | SBSP_SAMEBROWSER)));
if (SUCCEEDED(SHGetNavigateTarget(psf, pidl, &pidlGoto, NULL)))
{
IShellBrowser* psb;
if (SUCCEEDED(IUnknown_QueryService(punkSite, SID_STopLevelBrowser,
IID_PPV_ARG(IShellBrowser, &psb))))
{
hres = psb->BrowseObject(pidlGoto, dwFlags | SBSP_SAMEBROWSER);
psb->Release();
}
ILFree(pidlGoto);
}
return hres;
}
STDAPI SHGetTopBrowserWindow(IUnknown* punk, HWND* phwnd)
{
IOleWindow* pOleWindow;
HRESULT hr = IUnknown_QueryService(punk, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pOleWindow));
if (SUCCEEDED(hr))
{
hr = pOleWindow->GetWindow(phwnd);
pOleWindow->Release();
}
return hr;
}
BOOL ILIsFolder(LPCITEMIDLIST pidl)
{
BOOL fIsFolder = FALSE;
DWORD dwAttributes = SFGAO_FOLDER;
HRESULT hr = IEGetAttributesOf(pidl, &dwAttributes);
if (SFGAO_FOLDER == dwAttributes)
fIsFolder = TRUE;
return fIsFolder;
}
STDAPI_(LPITEMIDLIST) IEGetInternetRootID(void)
{
LPITEMIDLIST pidl;
//
// HACKHACK - we want the pidl to the Internet SF
// so we make a dummy URL and parse it. then
// we know its parent will be the Internet SF.
//
if (SUCCEEDED(IECreateFromPath(TEXT("dummy://url"), &pidl)))
{
ASSERT(!ILIsEmpty(_ILNext(pidl)));
ASSERT(IsURLChild(pidl, FALSE));
// we only want the parent Internt SF
_ILNext(pidl)->mkid.cb = 0;
return pidl;
}
return NULL;
}
STDAPI_(void) UpdateButtonArray(TBBUTTON *ptbDst, const TBBUTTON *ptbSrc, int ctb, LONG_PTR lStrOffset)
{
memcpy(ptbDst, ptbSrc, ctb*sizeof(TBBUTTON));
if (lStrOffset == -1)
{
// handle failure case
for (int i = 0; i < ctb; i++)
ptbDst[i].iString = 0;
}
else
{
for (int i = 0; i < ctb; i++)
ptbDst[i].iString += lStrOffset;
}
}
//----------------------------------------------------------------------------
// <Swipped from the NT5 version of Shell32>
//
STDAPI PathToAppPathKey(LPCTSTR pszPath, LPTSTR pszKey, int cchKey)
{
HRESULT hr;
// Use the szTemp variable of pseem to build key to the programs specific
// key in the registry as well as other things...
hr = StringCchCopy(pszKey, cchKey, REGSTR_PATH_APPPATHS);
if (SUCCEEDED(hr))
{
hr = StringCchCat(pszKey, cchKey, TEXT("\\"));
if (SUCCEEDED(hr))
{
hr = StringCchCat(pszKey, cchKey, PathFindFileName(pszPath));
if (SUCCEEDED(hr))
{
// Currently we will only look up .EXE if an extension is not
// specified
if (*PathFindExtension(pszKey) == 0)
{
hr = StringCchCat(pszKey, cchKey, TEXT(".exe"));
}
}
}
}
return hr;
}
//----------------------------------------------------------------------------
// <Swipped from the NT5 version of Shell32>
//
// this function checks for the existance of a value called "useURL" under the
// App Paths key in the registry associated with the app that is passed in.
STDAPI_(BOOL) DoesAppWantUrl(LPCTSTR pszCmdLine)
{
TCHAR szRegKeyName[MAX_PATH];
HKEY hKeyAppPaths;
BOOL bRet = FALSE;
// bug 61538 - The edit button never passes in args or quotes and the
// code below was screwing up if there were spaces in the path.
//
// need to copy the string since PathRemoveArgs whacks in a \0
// TCHAR szTemp[MAX_PATH];
// lstrcpyn(szTemp, pszCmdLine, ARRAYSIZE(szTemp));
// PathRemoveArgs(szTemp);
// PathUnquoteSpaces(szTemp);
HRESULT hr;
hr = PathToAppPathKey(pszCmdLine, szRegKeyName, ARRAYSIZE(szRegKeyName));
if (SUCCEEDED(hr))
{
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegKeyName, 0L, KEY_QUERY_VALUE, &hKeyAppPaths) == ERROR_SUCCESS)
{
bRet = RegQueryValueEx(hKeyAppPaths, TEXT("UseURL"), NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
RegCloseKey(hKeyAppPaths);
}
}
return bRet;
}
// thread reference count object, this uses SHSetThreadRef()to let other code
// in this process hold a reference to this main thread, and thus the main thread in this process
class CRefThread : public IUnknown
{
public:
// IUnknown
virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
virtual STDMETHODIMP_(ULONG) AddRef(void);
virtual STDMETHODIMP_(ULONG) Release(void);
CRefThread(LONG *pcRef);
private:
~CRefThread();
LONG *_pcRef;
UINT _idThread;
};
CRefThread::CRefThread(LONG *pcRef)
{
_idThread = GetCurrentThreadId();
_pcRef = pcRef;
*_pcRef = 1;
}
//
// Note that this code tightens but does not close a race window.
// Although we nuke the process reference, the class factory for
// the web browser has yet to be deregistered, so if somebody decides
// to create one, our class factory will wake up and create a
// shell folder, which will flake out because it can't get a
// process reference.
//
CRefThread::~CRefThread()
{
// Avoid re-entrancy during destruction
*_pcRef = 1000;
// If we are the process reference, then revoke the process reference
// since we're going away.
IUnknown *punk;
SHGetInstanceExplorer(&punk);
if (punk == this)
SHSetInstanceExplorer(NULL);
ATOMICRELEASE(punk);
// Nobody should've rescued our reference
ASSERT(*_pcRef == 1000);
*_pcRef = 0;
// get the other thread out of WaitMessage() or GetMessage()
PostThreadMessage(_idThread, WM_NULL, 0, 0);
}
HRESULT CRefThread::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = { { 0 }, };
return QISearch(this, qit, riid, ppvObj);
}
ULONG CRefThread::AddRef()
{
return InterlockedIncrement(_pcRef);
}
ULONG CRefThread::Release()
{
ASSERT( 0 != *_pcRef );
ULONG cRef = InterlockedDecrement(_pcRef);
if ( 0 == cRef )
{
delete this;
}
return cRef;
}
STDAPI SHCreateThreadRef(LONG *pcRef, IUnknown **ppunk)
{
*ppunk = new CRefThread(pcRef);
if (*ppunk)
return S_OK;
*pcRef = 0;
*ppunk = NULL;
return E_OUTOFMEMORY;
}
//
// Returns the cache file associated with a URL. For file: urls, the associated
// disk file is returned. Not that we don't use URLDownloadToCacheFile because
// it causes another I-M-S GET to to be sent to the server
//
HRESULT URLToCacheFile
(
LPCWSTR pszUrl,
LPWSTR pszFile,
int cchFile
)
{
HRESULT hr;
DWORD dwScheme = GetUrlScheme(pszUrl);
if (URL_SCHEME_FILE == dwScheme)
{
ULONG cch = cchFile;
hr = PathCreateFromUrl(pszUrl, pszFile, &cch, 0);
}
else
{
// bug 73386 - GetUrlCacheEntryInfoExW fails to find entries if there is an anchor
// so we have to whack it off.
//
// We should really fix GetUrlCacheEntryInfoExW instead, but apparently
// this is risky for 5.x
//
hr = S_OK;
WCHAR szUrlBuf[MAX_URL_STRING];
if (URL_SCHEME_HTTP == dwScheme || URL_SCHEME_HTTPS == dwScheme)
{
LPWSTR pszAnchor = StrChr(pszUrl, L'#');
if (pszAnchor)
{
hr = StringCchCopyN(szUrlBuf, ARRAYSIZE(szUrlBuf), pszUrl, pszAnchor - pszUrl);
pszUrl = szUrlBuf;
}
}
if (SUCCEEDED(hr))
{
char szBuf[1024];
LPINTERNET_CACHE_ENTRY_INFOW pCE = (LPINTERNET_CACHE_ENTRY_INFOW)szBuf;
DWORD dwEntrySize = sizeof(szBuf);
BOOL fGotCacheInfo = GetUrlCacheEntryInfoExW(pszUrl, pCE, &dwEntrySize, NULL, NULL, NULL, 0);
if (!fGotCacheInfo)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// We guessed too small for the buffer so allocate the correct size & retry
pCE = (LPINTERNET_CACHE_ENTRY_INFOW)LocalAlloc(LPTR, dwEntrySize);
if (pCE)
{
fGotCacheInfo = GetUrlCacheEntryInfoEx(pszUrl, pCE, &dwEntrySize, NULL, NULL, NULL, 0);
}
}
else
{
// Retry using UTF8 encoding
//
// This fix belongs in GetUrlCacheEntryInfoEx (StevePro 01/19/99)
//
char szUrl[MAX_URL_STRING];
if (SHUnicodeToAnsiCP(CP_UTF8, pszUrl, szUrl, ARRAYSIZE(szUrl)))
{
szUrl[ARRAYSIZE(szUrl)-1] = '\0'; // paranoia
// UrlEscapeA Internally converts to unicode which messes up utf8. So we
// copy the string to a WCHAR buffer without coverting and call the unicode version.
// Yuk!
WCHAR wzUrl[ARRAYSIZE(szUrl)];
char* psz = szUrl;
WCHAR* pwz = wzUrl;
while (*psz!= NULL)
{
*pwz++ = ((WCHAR)*psz++) & 0xff;
}
*pwz = L'\0';
ULONG cch = ARRAYSIZE(wzUrl);
UrlEscapeW(wzUrl, wzUrl, &cch, /*URL_ESCAPE_PERCENT*/0);
psz = szUrl;
pwz = wzUrl;
while (*pwz!= NULL)
{
*psz++ = (char)LOWORD(*pwz++);
}
*psz = '\0';
LPINTERNET_CACHE_ENTRY_INFOA pCEA = (LPINTERNET_CACHE_ENTRY_INFOA)szBuf;
dwEntrySize = sizeof(szBuf);
BOOL fUtf8Worked = GetUrlCacheEntryInfoExA(szUrl, pCEA, &dwEntrySize, NULL, NULL, NULL, 0);
if (!fUtf8Worked)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// We guessed too small for the buffer so allocate the correct size & retry
pCEA = (LPINTERNET_CACHE_ENTRY_INFOA)LocalAlloc(LPTR, dwEntrySize);
if (pCEA)
{
fUtf8Worked = GetUrlCacheEntryInfoExA(szUrl, pCEA, &dwEntrySize, NULL, NULL, NULL, 0);
}
}
}
if (fUtf8Worked)
{
if (SHAnsiToUnicode(pCEA->lpszLocalFileName, pszFile, cchFile) >= cchFile)
{
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
else
{
hr = S_OK;
}
}
if ((char *)pCEA != szBuf)
{
LocalFree((HLOCAL)pCEA);
}
}
}
}
if (fGotCacheInfo)
{
hr = StringCchCopy(pszFile, cchFile, pCE->lpszLocalFileName);
}
// Free our GetUrlCacheEntryInfo buffer if we allocated one
if ((char *)pCE != szBuf)
{
LocalFree((HLOCAL)pCE);
}
}
}
return hr;
}
#ifdef DEBUG
void DebugDumpPidl(DWORD dwDumpFlag, LPTSTR pszOutputString, LPCITEMIDLIST pidl)
{
if (g_dwDumpFlags & dwDumpFlag)
{
TCHAR szPath[MAX_PATH];
LPTSTR lpsz;
if (pidl)
{
lpsz = szPath;
SHGetPathFromIDList(pidl, szPath);
}
else
{
lpsz = TEXT("(NULL)");
}
TraceMsg(TF_ALWAYS, "%s: \"%s\"", pszOutputString, lpsz);
}
}
#endif
// Variable argument version that ultimately call FormatMessageLiteW
BOOL __cdecl _FormatMessage(LPCWSTR szTemplate, LPWSTR szBuf, UINT cchBuf, ...)
{
BOOL fRet;
va_list ArgList;
va_start(ArgList, cchBuf);
fRet = FormatMessage(FORMAT_MESSAGE_FROM_STRING, szTemplate, 0, 0, szBuf, cchBuf, &ArgList);
va_end(ArgList);
return fRet;
}
// [msadek], On win9x we get the message thru a chain from explorer /iexplore (ANSI app.).
// and pass it to comctl32 (Unicode) so it will fail to match the hot key.
// the system sends the message with ANSI char and we treated it as Unicode.
// It looks like noone is affected with this bug (US, FE) since they have hot keys always in Latin.
// Bidi platforms are affected since they do have hot keys in native language.
WPARAM AnsiWparamToUnicode(WPARAM wParam)
{
char szCh[2];
WCHAR wszCh[2];
szCh[0] = (BYTE)wParam;
szCh[1] = '\0';
if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)szCh, ARRAYSIZE(szCh),
wszCh, ARRAYSIZE(wszCh)))
{
memcpy(&wParam, wszCh, sizeof(WCHAR));
}
return wParam;
}
void SHOutlineRect(HDC hdc, const RECT* prc, COLORREF cr)
{
RECT rc;
COLORREF clrSave = SetBkColor(hdc, cr);
//top
rc.left = prc->left;
rc.top = prc->top;
rc.right = prc->right;
rc.bottom = prc->top + 1;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
//left
rc.left = prc->left;
rc.top = prc->top;
rc.right = prc->left + 1;
rc.bottom = prc->bottom;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
//right
rc.left = prc->right - 1;
rc.top = prc->top;
rc.right = prc->right;
rc.bottom = prc->bottom;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
// bottom
rc.left = prc->left;
rc.top = prc->bottom - 1;
rc.right = prc->right;
rc.bottom = prc->bottom;
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
SetBkColor(hdc, clrSave);
}
HMONITOR GetPrimaryMonitor()
{
POINT pt = {0,0};
return MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
}
// Gets the Monitor's bounding or work rectangle, if the hMon is bad, return
// the primary monitor's bounding rectangle.
BOOL GetMonitorRects(HMONITOR hMon, LPRECT prc, BOOL bWork)
{
MONITORINFO mi;
mi.cbSize = sizeof(mi);
if (hMon && GetMonitorInfo(hMon, &mi))
{
if (!prc)
return TRUE;
else if (bWork)
CopyRect(prc, &mi.rcWork);
else
CopyRect(prc, &mi.rcMonitor);
return TRUE;
}
if (prc)
SetRect(prc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Utils to load background bitmap for toolbars etc.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// determine source name for bitmap, sift thru IE history....
HRESULT _GetBackBitmapLocation(LPTSTR psz, BOOL fInternet)
{
HRESULT hres = E_FAIL;
DWORD dwType;
DWORD dwcbData;
static const TCHAR c_szRegKeyCoolbar[] = TSZIEPATH TEXT("\\Toolbar");
// IE4 shipped back bitmap customization affecting both browser and shell.
// IE5 wants these to be separate customizations. But in the roaming
// case a customized IE4 customer shouldn't lose customization when going
// to the IE5 machine. So we might need to check twice:
//
if (fInternet)
{
// Try the IE5 internet location.
dwcbData = MAX_PATH * sizeof(TCHAR);
hres = SHGetValue(HKEY_CURRENT_USER, c_szRegKeyCoolbar, TEXT("BackBitmapIE5"), &dwType, psz, &dwcbData);
}
else
{
// Try the NT5 shell location.
dwcbData = MAX_PATH * sizeof(TCHAR);
hres = SHGetValue(HKEY_CURRENT_USER, c_szRegKeyCoolbar, TEXT("BackBitmapShell"), &dwType, psz, &dwcbData);
}
if (ERROR_SUCCESS != hres)
{
// Try the old combined internet/shell location
dwcbData = MAX_PATH * sizeof(TCHAR);
hres = SHGetValue(HKEY_CURRENT_USER, c_szRegKeyCoolbar, TEXT("BackBitmap"), &dwType, psz, &dwcbData);
}
return hres;
}
//------------------------------------------------------------------------
// determine background settings and source for toolbar,
// load bitmap (file/resource) and update cache
HBITMAP LoadToolbarBackBmp(LPTSTR * ppszBitmap, BMPCACHE * pbmpCache, BOOL fInternet)
{
HIGHCONTRAST hc;
HBITMAP hbmp = pbmpCache->hbmp;
COLORREF cr3D = GetSysColor(COLOR_3DFACE);
TCHAR szScratch[MAX_PATH];
LPTSTR pszBitmap = NULL;
BOOL fBitmapInvalid = FALSE;
ENTERCRITICAL;
// If the stashed hbmp's cr3D color changed, we need to mark invalid
if (pbmpCache->hbmp && pbmpCache->cr3D != cr3D)
fBitmapInvalid = TRUE;
// get the location spec for the bitmap
hc.cbSize = sizeof(HIGHCONTRAST);
if ((SystemParametersInfoA(SPI_GETHIGHCONTRAST, hc.cbSize, (LPVOID) &hc, FALSE)) &&
(hc.dwFlags & HCF_HIGHCONTRASTON))
{
// we have no bitmap in high contrast
}
else if (SUCCEEDED(_GetBackBitmapLocation(szScratch, fInternet)))
{
pszBitmap = szScratch;
}
// if they are removing the bitmap, we need to mark invalid
if (!pszBitmap && *ppszBitmap)
fBitmapInvalid = TRUE;
// or it's location has been changed, we need to mark invalid
if (pszBitmap && (!*ppszBitmap || lstrcmpi(pszBitmap, *ppszBitmap)))
fBitmapInvalid = TRUE;
if (fBitmapInvalid)
{
TraceMsg(DM_ITBAR, "LoadToolbarBackBmp: Loading Background Bitmap");
Str_SetPtr(ppszBitmap, pszBitmap);
hbmp=NULL;
if (*ppszBitmap)
{
if ((*ppszBitmap)[0])
{
hbmp = (HBITMAP) LoadImage(NULL, szScratch, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION | LR_LOADFROMFILE | LR_LOADMAP3DCOLORS );
}
if (!hbmp)
{
#ifdef OLD_SWIRLY_BACKDROP
if (SHGetCurColorRes() <= 8)
hbmp = (HBITMAP) LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDB_BACK), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS );
#endif
}
}
#ifdef OLD_LEGACY_BAD_COLOUR_CODE
if (hbmp)
{
// mapping needed ?
// DONTWORRYABOUTTHIS: this will be removed as soon as I get the new backdrop....
if ( /* cr3D != RGB(192,192,192) */ FALSE)
{
RGBQUAD rgbTable[256];
RGBQUAD rgbFace;
HDC dc;
HBITMAP hbmSave;
UINT i;
UINT n;
dc = CreateCompatibleDC(NULL);
hbmSave = (HBITMAP)SelectObject(dc, hbmp);
n = GetDIBColorTable(dc, 0, 256, rgbTable);
rgbFace.rgbRed = GetRValue(cr3D);
rgbFace.rgbGreen = GetGValue(cr3D);
rgbFace.rgbBlue = GetBValue(cr3D);
for (i = 0; i < n; i++)
{
if ( rgbTable[i].rgbRed == 192 && rgbTable[i].rgbGreen == 192 && rgbTable[i].rgbBlue == 192 )
{
rgbTable[i] = rgbFace;
}
else
{
rgbTable[i].rgbRed = (rgbTable[i].rgbRed * rgbFace.rgbRed ) / 192;
rgbTable[i].rgbGreen = (rgbTable[i].rgbGreen * rgbFace.rgbGreen) / 192;
rgbTable[i].rgbBlue = (rgbTable[i].rgbBlue * rgbFace.rgbBlue ) / 192;
}
}
SetDIBColorTable(dc, 0, n, rgbTable);
SelectObject(dc, hbmSave);
DeleteDC(dc);
}
}
#endif
if (pbmpCache->hbmp)
DeleteObject(pbmpCache->hbmp);
pbmpCache->hbmp = hbmp;
pbmpCache->cr3D = cr3D;
}
LEAVECRITICAL;
return hbmp;
}
VOID StripDecorations(PTSTR pszTitle, BOOL fStripAmp)
{
LPTSTR pszCleaned = pszTitle; // work in-place
LPCTSTR psz = pszTitle;
while (*psz && (*psz != TEXT('\t')))
{
if (*psz != TEXT('&') || !fStripAmp)
{
*pszCleaned = *psz;
pszCleaned++;
}
psz++;
}
*pszCleaned = TEXT('\0');
}
//------------------------------------------------------------------------
LPCTSTR UnescapeDoubleAmpersand(LPTSTR pszTitle)
{
LPTSTR pszCleaned = pszTitle; // work in-place
LPCTSTR psz = pszTitle;
bool fEscapedAmp = false;
while (*psz)
{
if (*psz != TEXT('&') || fEscapedAmp)
{
// copy character
*pszCleaned = *psz;
pszCleaned++;
fEscapedAmp = false;
}
else
{
LPCTSTR pszNext = psz + 1;
if (pszNext && (*pszNext == TEXT('&'))) {
fEscapedAmp = true; // keep next ampersand
}
}
psz++;
}
*pszCleaned = TEXT('\0');
return pszTitle;
}
UINT MapClsidToID(REFCLSID rclsid)
{
UINT nCmdID;
nCmdID = 0;
if (IsEqualCLSID(CLSID_SearchBand, rclsid))
nCmdID = FCIDM_VBBSEARCHBAND;
else if (IsEqualCLSID(CLSID_FavBand, rclsid))
nCmdID = FCIDM_VBBFAVORITESBAND;
else if (IsEqualCLSID(CLSID_HistBand, rclsid))
nCmdID = FCIDM_VBBHISTORYBAND;
else if (IsEqualCLSID(CLSID_ExplorerBand, rclsid))
nCmdID = FCIDM_VBBEXPLORERBAND;
else if (IsEqualCLSID(CLSID_FileSearchBand, rclsid))
nCmdID = FCIDM_VBBSEARCHBAND;
return nCmdID;
}
// Create mask from given bitmap, use color at pixel (x/y) as transparent color
HBITMAP CreateMaskBitmap(HDC hdc, int x, int y, HBITMAP hbmpImage)
{
ASSERT(hbmpImage);
BITMAP bm;
if (::GetObject(hbmpImage, sizeof(BITMAP), &bm) != sizeof(BITMAP)) {
return FALSE;
}
HDC hdcImg = NULL;
HDC hdcMask = NULL;
HBITMAP hbmpMask = NULL;
HBITMAP hbmpOldImg = NULL;
HBITMAP hbmpOldMsk = NULL;
COLORREF clrTransparent = 0;
hdcImg = ::CreateCompatibleDC(hdc);
if (hdcImg == NULL) goto _CMBcleanup;
hdcMask = ::CreateCompatibleDC(hdc);
if (hdcMask == NULL) goto _CMBcleanup;
hbmpMask = ::CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
if (hbmpMask == NULL) goto _CMBcleanup;
hbmpOldImg = (HBITMAP) ::SelectObject(hdcImg, hbmpImage);
hbmpOldMsk = (HBITMAP) ::SelectObject(hdcMask, hbmpMask);
clrTransparent = ::GetPixel(hdcImg, 0, 0);
::SetBkColor(hdcImg, clrTransparent);
::BitBlt(hdcMask, 0, 0, bm.bmWidth, bm.bmHeight, hdcImg, 0, 0, SRCCOPY);
_CMBcleanup:
if (hbmpOldImg && hdcImg)
SelectObject(hdcImg, hbmpOldImg);
if (hdcImg)
DeleteDC(hdcImg);
if (hbmpOldMsk && hdcMask)
SelectObject(hdcMask, hbmpOldMsk);
if (hdcMask)
DeleteDC(hdcMask);
return hbmpMask;
}
// draw bitmap transparently; on Win2K and up, one could use MaskBlt()
BOOL DrawTransparentBitmapPart(HDC hdc, int x, int y, int dx, int dy, HBITMAP hbmpImage, HBITMAP hbmpMask)
{
ASSERT(hbmpImage);
BITMAP bm;
if (::GetObject(hbmpImage, sizeof(BITMAP), &bm) != sizeof(BITMAP)) {
return FALSE;
}
HBITMAP hbmpTmpMask = NULL;
// create temporary mask bitmap if none supplied
if (hbmpMask == NULL) {
hbmpMask = hbmpTmpMask = CreateMaskBitmap(hdc, 0, 0, hbmpImage);
}
if (hbmpMask == NULL) {
return FALSE;
}
HDC hdcOffScr = NULL;
HBITMAP hbmOffScr = NULL;
HBITMAP hbmOldOffScr = NULL;
HDC hdcImage = NULL;
HBITMAP hbmOldImage = NULL;
HDC hdcMask = NULL;
HBITMAP hbmOldMask = NULL;
// draw.to offscreen bitmap
hdcOffScr = ::CreateCompatibleDC(hdc);
if (hdcOffScr == NULL) goto _DTBcleanup;
hbmOffScr = ::CreateBitmap(dx, dy,GetDeviceCaps(hdc, PLANES),
(BYTE)GetDeviceCaps(hdc, BITSPIXEL), NULL);
if (hbmOffScr == NULL) goto _DTBcleanup;
hbmOldOffScr = (HBITMAP)::SelectObject(hdcOffScr, hbmOffScr);
// Copy the image of the destination rectangle to the
// off-screen buffer DC, so we can play with it.
::BitBlt(hdcOffScr, 0, 0, dx, dy, hdc, x, y, SRCCOPY);
// prepare DCs for both image and mask
hdcImage = ::CreateCompatibleDC(hdc);
if (hdcImage == NULL) goto _DTBcleanup;
hbmOldImage = (HBITMAP)::SelectObject(hdcImage, hbmpImage);
hdcMask = ::CreateCompatibleDC(hdc);
if (hdcMask == NULL) goto _DTBcleanup;
hbmOldMask = (HBITMAP)::SelectObject(hdcMask, hbmpMask);
::SetBkColor(hdcOffScr, RGB(255,255,255));
::BitBlt(hdcOffScr, 0, 0, dx, dy, hdcImage, 0, 0, SRCINVERT);
::BitBlt(hdcOffScr, 0, 0, dx, dy, hdcMask, 0, 0, SRCAND);
::BitBlt(hdcOffScr, 0, 0, dx, dy, hdcImage, 0, 0, SRCINVERT);
// Copy the resultant image back to the screen DC.
::BitBlt(hdc, x, y, dx, dy, hdcOffScr, 0, 0, SRCCOPY);
_DTBcleanup:
if (hdcOffScr && hbmOldOffScr)
::SelectObject(hdcOffScr, hbmOldOffScr);
if (hdcOffScr)
::DeleteDC(hdcOffScr);
if (hbmOffScr)
::DeleteObject(hbmOffScr);
if (hdcImage && hbmOldImage)
::SelectObject(hdcImage, hbmOldImage);
if (hdcImage)
::DeleteDC(hdcImage);
if (hdcMask && hbmOldMask)
::SelectObject(hdcMask, hbmOldMask);
if (hdcMask)
::DeleteDC(hdcMask);
if (hbmpTmpMask)
::DeleteObject(hbmpTmpMask);
return TRUE;
}
// draw bitmap transparently; on Win2K and up, one could use MaskBlt()
BOOL DrawTransparentBitmap(HDC hdc, int x, int y, HBITMAP hbmpImage, HBITMAP hbmpMask)
{
ASSERT(hbmpImage);
BITMAP bm;
if (::GetObject(hbmpImage, sizeof(BITMAP), &bm) != sizeof(BITMAP)) {
return FALSE;
}
return DrawTransparentBitmapPart(hdc, x, y, bm.bmWidth, bm.bmHeight, hbmpImage, hbmpMask);
}
//------------------------------------------------------------------------
BOOL
DrawAlphaBitmap(HDC hdc, int x, int y, int dx, int dy, HBITMAP hbmpImage)
{
BLENDFUNCTION bf = {0};
HDC hdcImage = ::CreateCompatibleDC(hdc);
if (hdcImage == NULL) {
return false;
}
HBITMAP hbmOldImage = (HBITMAP)::SelectObject(hdcImage, hbmpImage);
bf.BlendOp = AC_SRC_OVER;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hdc, x, y, dx, dy, hdcImage, 0, 0, dx, dy, bf);
if (hbmOldImage) {
SelectObject(hdcImage, hbmOldImage);
}
DESTROY_OBJ_WITH_HANDLE(hdcImage, DeleteObject);
return true;
}
STDAPI_(IDeskBand *) FindBandByClsidBS(IBandSite *pbs, REFCLSID clsidToFind)
{
DWORD dwBandID;
for (int i = 0; SUCCEEDED(pbs->EnumBands(i, &dwBandID)); i++)
{
IDeskBand *pstb;
HRESULT hr = pbs->QueryBand(dwBandID, &pstb, NULL, NULL, 0);
if (SUCCEEDED(hr))
{
CLSID clsid;
hr = IUnknown_GetClassID(pstb, &clsid);
if (SUCCEEDED(hr) && IsEqualGUID(clsidToFind, clsid))
{
return pstb;
}
pstb->Release();
}
}
return NULL;
}
HIMAGELIST CreateImageList(HINSTANCE hi, LPCTSTR lpbmp, int cx, int cGrow, COLORREF crMask,
UINT uType, UINT uFlags, BOOL bUseNewMirroringSupport)
{
HBITMAP hbmImage;
HIMAGELIST piml = NULL;
BITMAP bm;
int cy, cInitial;
UINT flags;
hbmImage = (HBITMAP)LoadImage(hi, lpbmp, uType, 0, 0, uFlags);
if (hbmImage && (sizeof(bm) == GetObject(hbmImage, sizeof(bm), &bm)))
{
// If cx is not stated assume it is the same as cy.
// ASSERT(cx);
cy = bm.bmHeight;
if (cx == 0)
cx = cy;
cInitial = bm.bmWidth / cx;
ENTERCRITICAL;
if (bUseNewMirroringSupport)
{
flags = ILC_MIRROR | PrivateILC_PERITEMMIRROR;
}
else
{
flags = 0;
}
if (crMask != CLR_NONE)
flags |= ILC_MASK;
if (bm.bmBits)
flags |= (bm.bmBitsPixel & ILC_COLORMASK);
piml = ImageList_Create(cx, cy, flags, cInitial, cGrow);
if (piml)
{
int added;
if (crMask == CLR_NONE)
added = ImageList_Add(piml, hbmImage, NULL);
else
added = ImageList_AddMasked(piml, hbmImage, crMask);
if (added < 0)
{
ImageList_Destroy(piml);
piml = NULL;
}
}
LEAVECRITICAL;
}
if (hbmImage)
DeleteObject(hbmImage);
return piml;
}