2044 lines
44 KiB
C++
2044 lines
44 KiB
C++
/*
|
|
* urlprop.cpp - Implementation for URLProp class.
|
|
*/
|
|
|
|
|
|
#include "priv.h"
|
|
#include "ishcut.h"
|
|
|
|
STDAPI_(LPITEMIDLIST) IEILCreate(UINT cbSize);
|
|
|
|
#define MAX_BUF_INT (1 + 10 + 1) // -2147483647
|
|
|
|
const TCHAR c_szIntshcut[] = ISHCUT_INISTRING_SECTION;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
BOOL IsValidPCURLProp(PCURLProp pcurlprop)
|
|
{
|
|
return (IS_VALID_READ_PTR(pcurlprop, CURLProp) &&
|
|
(NULL == pcurlprop->m_hstg ||
|
|
IS_VALID_HANDLE(pcurlprop->m_hstg, PROPSTG)));
|
|
}
|
|
|
|
|
|
BOOL IsValidPCIntshcutProp(PCIntshcutProp pcisprop)
|
|
{
|
|
return (IS_VALID_READ_PTR(pcisprop, CIntshcutProp) &&
|
|
IS_VALID_STRUCT_PTR(pcisprop, CURLProp));
|
|
}
|
|
|
|
BOOL IsValidPCIntsiteProp(PCIntsiteProp pcisprop)
|
|
{
|
|
return (IS_VALID_READ_PTR(pcisprop, CIntsiteProp) &&
|
|
IS_VALID_STRUCT_PTR(pcisprop, CURLProp));
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
BOOL AnyMeatW(LPCWSTR pcsz)
|
|
{
|
|
ASSERT(! pcsz || IS_VALID_STRING_PTRW(pcsz, -1));
|
|
|
|
return(pcsz ? StrSpnW(pcsz, L" \t") < lstrlenW(pcsz) : FALSE);
|
|
}
|
|
|
|
|
|
BOOL AnyMeatA(LPCSTR pcsz)
|
|
{
|
|
ASSERT(! pcsz || IS_VALID_STRING_PTRA(pcsz, -1));
|
|
|
|
return(pcsz ? StrSpnA(pcsz, " \t") < lstrlenA(pcsz) : FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Read an arbitrary named string from the .ini file.
|
|
|
|
Returns: S_OK if the name exists
|
|
S_FALSE if it doesn't
|
|
|
|
E_OUTOFMEMORY
|
|
*/
|
|
HRESULT ReadStringFromFile(IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
OUT LPWSTR * ppwsz,
|
|
IN CHAR * pszBuf)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszName, -1));
|
|
ASSERT(IS_VALID_WRITE_PTR(ppwsz, PWSTR));
|
|
|
|
*ppwsz = (LPWSTR)LocalAlloc(LPTR, SIZEOF(WCHAR) * INTERNET_MAX_URL_LENGTH);
|
|
if (*ppwsz)
|
|
{
|
|
DWORD cch;
|
|
|
|
hres = S_OK;
|
|
|
|
cch = SHGetIniString(pszSectionName, pszName,
|
|
*ppwsz, INTERNET_MAX_URL_LENGTH, pszFile);
|
|
if (0 == cch)
|
|
{
|
|
hres = S_FALSE;
|
|
LocalFree(*ppwsz);
|
|
*ppwsz = NULL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*
|
|
Purpose: Read an arbitrary named string from the .ini file.
|
|
Return a BSTR
|
|
|
|
Returns: S_OK if the name exists
|
|
S_FALSE if it doesn't
|
|
|
|
E_OUTOFMEMORY
|
|
*/
|
|
HRESULT ReadBStrFromFile(IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
OUT BSTR * pBStr)
|
|
{
|
|
CHAR szTempBuf[INTERNET_MAX_URL_LENGTH];
|
|
WCHAR *pwsz;
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
*pBStr = NULL;
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszName, -1));
|
|
ASSERT(IS_VALID_WRITE_PTR(pBStr, PWSTR));
|
|
|
|
// (Pass in an empty string so we can determine from the return
|
|
// value whether there is any text associated with this name.)
|
|
hres = ReadStringFromFile(pszFile, pszSectionName, pszName, &pwsz, szTempBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
*pBStr = SysAllocString(pwsz);
|
|
LocalFree(pwsz);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*
|
|
Purpose: read an arbitrary named unsigend int from the .ini file. note in order to implement
|
|
ReadSignedFromFile one'll need to use ReadStringFromFile and then StrToIntEx. this is
|
|
because GetPrivateProfileInt can't return a negative.
|
|
|
|
Returns: S_OK if the name exists
|
|
S_FALSE if it doesn't
|
|
|
|
E_OUTOFMEMORY
|
|
*/
|
|
HRESULT
|
|
ReadUnsignedFromFile(
|
|
IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
IN LPDWORD pdwVal)
|
|
{
|
|
HRESULT hr;
|
|
int iValue;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszSectionName, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszName, -1));
|
|
|
|
if (NULL == pdwVal)
|
|
return E_INVALIDARG;
|
|
*pdwVal = 0;
|
|
|
|
hr = S_OK;
|
|
iValue = GetPrivateProfileInt(pszSectionName, pszName, 1, pszFile);
|
|
if (1 == iValue) {
|
|
iValue = GetPrivateProfileInt(pszSectionName, pszName, 2, pszFile);
|
|
hr = (2 != iValue) ? S_OK : S_FALSE;
|
|
ASSERT(S_FALSE == hr || 1 == iValue);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
*pdwVal = (DWORD)iValue;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
Purpose: Write number to URL (ini) file
|
|
|
|
*/
|
|
HRESULT WriteSignedToFile(IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
IN int nVal)
|
|
{
|
|
HRESULT hres;
|
|
TCHAR szVal[MAX_BUF_INT];
|
|
int cch;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszName, -1));
|
|
|
|
cch = wnsprintf(szVal, ARRAYSIZE(szVal), TEXT("%d"), nVal);
|
|
ASSERT(cch > 0);
|
|
ASSERT(cch < SIZECHARS(szVal));
|
|
ASSERT(cch == lstrlen(szVal)); // Dude, talk about anal...
|
|
|
|
hres = WritePrivateProfileString(pszSectionName, pszName, szVal,
|
|
pszFile) ? S_OK : E_FAIL;
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Write number to URL (ini) file
|
|
|
|
*/
|
|
HRESULT WriteUnsignedToFile(IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
IN DWORD nVal)
|
|
{
|
|
HRESULT hres;
|
|
TCHAR szVal[MAX_BUF_INT];
|
|
int cch;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszName, -1));
|
|
|
|
cch = wnsprintf(szVal, ARRAYSIZE(szVal), TEXT("%u"), nVal);
|
|
ASSERT(cch > 0);
|
|
ASSERT(cch < SIZECHARS(szVal));
|
|
ASSERT(cch == lstrlen(szVal)); // Dude, talk about anal...
|
|
|
|
hres = WritePrivateProfileString(pszSectionName, pszName, szVal,
|
|
pszFile) ? S_OK : E_FAIL;
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Write binary data to URL (ini) file
|
|
|
|
*/
|
|
HRESULT WriteBinaryToFile(IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
IN LPVOID pvData,
|
|
IN DWORD cbSize)
|
|
{
|
|
HRESULT hres;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszName, -1));
|
|
|
|
hres = (WritePrivateProfileStruct(pszSectionName, pszName, pvData, cbSize, pszFile))
|
|
? S_OK : E_FAIL;
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Read the hotkey from the URL (ini) file
|
|
|
|
*/
|
|
HRESULT ReadBinaryFromFile(IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
IN LPVOID pvData,
|
|
IN DWORD cbData)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
|
|
memset(pvData, 0, cbData);
|
|
|
|
if (GetPrivateProfileStruct(pszSectionName, pszName, pvData, cbData, pszFile))
|
|
hres = S_OK;
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Real the URL from the URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
ReadURLFromFile(
|
|
IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
OUT LPTSTR * ppsz)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
|
|
*ppsz = (LPTSTR)LocalAlloc(LPTR, SIZEOF(TCHAR) * INTERNET_MAX_URL_LENGTH);
|
|
if (*ppsz)
|
|
{
|
|
DWORD cch;
|
|
|
|
cch = SHGetIniString(pszSectionName, ISHCUT_INISTRING_URL,
|
|
*ppsz, INTERNET_MAX_URL_LENGTH, pszFile);
|
|
if (0 != cch)
|
|
{
|
|
PathRemoveBlanks(*ppsz);
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
LocalFree(*ppsz);
|
|
*ppsz = NULL;
|
|
hres = S_FALSE;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Read the icon location from the URL (ini) file
|
|
|
|
Returns: S_OK value was obtained from file
|
|
S_FALSE value wasn't in file
|
|
|
|
E_OUTOFMEMORY
|
|
*/
|
|
HRESULT
|
|
ReadIconLocation(
|
|
IN LPCTSTR pszFile,
|
|
OUT LPWSTR * ppwsz,
|
|
OUT int * pniIcon,
|
|
IN CHAR * pszBuf)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
DWORD cch;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_WRITE_PTR(ppwsz, PTSTR));
|
|
ASSERT(IS_VALID_WRITE_PTR(pniIcon, INT));
|
|
|
|
*ppwsz = NULL;
|
|
*pniIcon = 0;
|
|
|
|
*ppwsz = (LPWSTR)LocalAlloc(LPTR, SIZEOF(WCHAR) * MAX_PATH);
|
|
if (*ppwsz)
|
|
{
|
|
hres = S_FALSE; // assume no value exists in the file
|
|
|
|
cch = SHGetIniString(c_szIntshcut, ISHCUT_INISTRING_ICONFILE, *ppwsz, MAX_PATH, pszFile);
|
|
if (0 != cch)
|
|
{
|
|
TCHAR szIndex[MAX_BUF_INT];
|
|
// The icon index is all ASCII so don't need SHGetIniString
|
|
cch = GetPrivateProfileString(c_szIntshcut, ISHCUT_INISTRING_ICONINDEX, c_szNULL, szIndex, SIZECHARS(szIndex), pszFile);
|
|
if (0 != cch)
|
|
{
|
|
if (StrToIntEx(szIndex, 0, pniIcon))
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
if (S_OK != hres)
|
|
{
|
|
LocalFree(*ppwsz);
|
|
*ppwsz = NULL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Write icon location to URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
WriteIconFile(
|
|
IN LPCTSTR pszFile,
|
|
IN LPCWSTR pszIconFile)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(! pszIconFile ||
|
|
IS_VALID_STRING_PTRW(pszIconFile, -1));
|
|
|
|
if (*pszFile)
|
|
{
|
|
if (AnyMeatW(pszIconFile))
|
|
{
|
|
hres = SHSetIniString(c_szIntshcut, ISHCUT_INISTRING_ICONFILE, pszIconFile,
|
|
pszFile) ? S_OK : E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
// NOTE: since this function removes both the file and the index
|
|
// values, then this function must be called *after* any call
|
|
// to WriteIconIndex. One way to do this is make sure
|
|
// PID_IS_ICONINDEX < PID_IS_ICONFILE, since the index will
|
|
// be enumerated first.
|
|
|
|
hres = (SHDeleteIniString(c_szIntshcut, ISHCUT_INISTRING_ICONFILE,
|
|
pszFile) &&
|
|
DeletePrivateProfileString(c_szIntshcut, ISHCUT_INISTRING_ICONINDEX,
|
|
pszFile))
|
|
? S_OK : E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Write icon index to URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
WriteIconIndex(
|
|
IN LPCTSTR pszFile,
|
|
IN int niIcon)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if (*pszFile)
|
|
hres = WriteSignedToFile(pszFile, c_szIntshcut, ISHCUT_INISTRING_ICONINDEX, niIcon);
|
|
else
|
|
hres = S_FALSE;
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Read the hotkey from the URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
ReadHotkey(
|
|
IN LPCTSTR pszFile,
|
|
IN WORD * pwHotkey)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
TCHAR szHotkey[MAX_BUF_INT];
|
|
DWORD cch;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_WRITE_PTR(pwHotkey, WORD));
|
|
|
|
*pwHotkey = 0;
|
|
|
|
cch = GetPrivateProfileString(c_szIntshcut, TEXT("Hotkey"), c_szNULL, szHotkey, SIZECHARS(szHotkey), pszFile);
|
|
if (0 != cch)
|
|
{
|
|
int nVal;
|
|
|
|
if (StrToIntEx(szHotkey, 0, &nVal))
|
|
{
|
|
*pwHotkey = nVal;
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Write hotkey to URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
WriteHotkey(
|
|
IN LPCTSTR pszFile,
|
|
IN WORD wHotkey)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
|
|
if (*pszFile)
|
|
{
|
|
if (wHotkey)
|
|
{
|
|
hres = WriteUnsignedToFile(pszFile, c_szIntshcut, TEXT("Hotkey"), wHotkey);
|
|
}
|
|
else
|
|
{
|
|
hres = DeletePrivateProfileString(c_szIntshcut, TEXT("Hotkey"), pszFile)
|
|
? S_OK
|
|
: E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Read the working directory from the URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
ReadWorkingDirectory(
|
|
IN LPCTSTR pszFile,
|
|
OUT LPWSTR * ppwsz)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
TCHAR szPath[MAX_PATH];
|
|
DWORD cch;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_WRITE_PTR(ppwsz, PWSTR));
|
|
|
|
*ppwsz = NULL;
|
|
|
|
*ppwsz = (LPWSTR)LocalAlloc(LPTR, SIZEOF(WCHAR) * MAX_PATH);
|
|
if (*ppwsz)
|
|
{
|
|
hres = S_FALSE;
|
|
|
|
cch = SHGetIniString(c_szIntshcut,
|
|
ISHCUT_INISTRING_WORKINGDIR,
|
|
szPath, SIZECHARS(szPath), pszFile);
|
|
if (0 != cch)
|
|
{
|
|
TCHAR szFullPath[MAX_PATH];
|
|
PTSTR pszFileName;
|
|
|
|
if (0 < GetFullPathName(szPath, SIZECHARS(szFullPath), szFullPath,
|
|
&pszFileName))
|
|
{
|
|
SHTCharToUnicode(szFullPath, *ppwsz, MAX_PATH);
|
|
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
if (S_OK != hres)
|
|
{
|
|
LocalFree(*ppwsz);
|
|
*ppwsz = NULL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Write the working directory to the URL (ini) file.
|
|
|
|
*/
|
|
HRESULT
|
|
WriteGenericString(
|
|
IN LPCTSTR pszFile,
|
|
IN LPCTSTR pszSectionName,
|
|
IN LPCTSTR pszName,
|
|
IN LPCWSTR pwsz) OPTIONAL
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_STRING_PTR(pszName, -1));
|
|
ASSERT(! pwsz || IS_VALID_STRING_PTRW(pwsz, -1));
|
|
|
|
if (*pszFile)
|
|
{
|
|
if (AnyMeatW(pwsz))
|
|
{
|
|
hres = (SHSetIniString(pszSectionName, pszName, pwsz,
|
|
pszFile)) ? S_OK : E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hres = (SHDeleteIniString(pszSectionName, pszName, pszFile))
|
|
? S_OK : E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Read the show-command flag from the URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
ReadShowCmd(
|
|
IN LPCTSTR pszFile,
|
|
OUT PINT pnShowCmd)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
TCHAR szT[MAX_BUF_INT];
|
|
DWORD cch;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
ASSERT(IS_VALID_WRITE_PTR(pnShowCmd, INT));
|
|
|
|
*pnShowCmd = SW_NORMAL;
|
|
|
|
cch = GetPrivateProfileString(c_szIntshcut, TEXT("ShowCommand"), c_szNULL, szT, SIZECHARS(szT), pszFile);
|
|
if (0 != cch)
|
|
{
|
|
if (StrToIntEx(szT, 0, pnShowCmd))
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Write showcmd to URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
WriteShowCmd(
|
|
IN LPCTSTR pszFile,
|
|
IN int nShowCmd)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
|
|
if (*pszFile)
|
|
{
|
|
if (SW_NORMAL != nShowCmd)
|
|
{
|
|
hres = WriteSignedToFile(pszFile, c_szIntshcut, TEXT("ShowCommand"), nShowCmd);
|
|
}
|
|
else
|
|
{
|
|
hres = DeletePrivateProfileString(c_szIntshcut, TEXT("ShowCommand"), pszFile)
|
|
? S_OK
|
|
: E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Purpose: Read the IDList from the URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
ReadIDList(
|
|
IN LPCTSTR pszFile,
|
|
OUT LPITEMIDLIST *ppidl)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
ULONG cb;
|
|
|
|
ASSERT(ppidl);
|
|
|
|
// Delete the old one if any.
|
|
if (*ppidl)
|
|
{
|
|
ILFree(*ppidl);
|
|
*ppidl = NULL;
|
|
}
|
|
|
|
// Read the size of the IDLIST
|
|
cb = GetPrivateProfileInt(c_szIntshcut, TEXT("ILSize"), 0, pszFile);
|
|
if (cb)
|
|
{
|
|
// Create a IDLIST
|
|
LPITEMIDLIST pidl = IEILCreate(cb);
|
|
if (pidl)
|
|
{
|
|
// Read its contents
|
|
if (GetPrivateProfileStruct(c_szIntshcut, TEXT("IDList"), (LPVOID)pidl, cb, pszFile))
|
|
{
|
|
*ppidl = pidl;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
ILFree(pidl);
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT
|
|
WriteStream(
|
|
IN LPCTSTR pszFile,
|
|
IN IStream *pStream,
|
|
IN LPCTSTR pszStreamName,
|
|
IN LPCTSTR pszSizeName)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
ULARGE_INTEGER li = {0};
|
|
|
|
if(pStream)
|
|
IStream_Size(pStream, &li);
|
|
|
|
if (li.LowPart)
|
|
{
|
|
ASSERT(!li.HighPart);
|
|
LPVOID pv = LocalAlloc(LPTR, li.LowPart);
|
|
|
|
if (pv && SUCCEEDED(hr = IStream_Read(pStream, pv, li.LowPart)))
|
|
{
|
|
// we have loaded the data properly, time to write it out
|
|
|
|
if (SUCCEEDED(hr = WriteUnsignedToFile(pszFile, c_szIntshcut, pszSizeName, li.LowPart)))
|
|
hr = WriteBinaryToFile(pszFile, c_szIntshcut, pszStreamName, pv, li.LowPart);
|
|
}
|
|
|
|
if (pv)
|
|
LocalFree(pv);
|
|
}
|
|
else
|
|
{
|
|
// delete the keys if
|
|
// 1. pStream is NULL, or
|
|
// 2. pStream in empty (cbPidl == 0).
|
|
if (DeletePrivateProfileString(c_szIntshcut, pszSizeName, pszFile) &&
|
|
DeletePrivateProfileString(c_szIntshcut, pszStreamName, pszFile))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
Purpose: Write IDList to URL (ini) file
|
|
|
|
*/
|
|
HRESULT
|
|
WriteIDList(
|
|
IN LPCTSTR pszFile,
|
|
IN IStream *pStream)
|
|
{
|
|
return WriteStream(pszFile, pStream, TEXT("IDList"), TEXT("ILSize"));
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** Methods ***/
|
|
|
|
|
|
|
|
|
|
// URLProp class implementation
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
Purpose: Dump the properties in this object
|
|
|
|
*/
|
|
STDMETHODIMP_(void) URLProp::Dump(void)
|
|
{
|
|
if (IsFlagSet(g_dwDumpFlags, DF_URLPROP))
|
|
{
|
|
PropStg_Dump(m_hstg, 0);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
Purpose: Constructor for URLProp
|
|
|
|
*/
|
|
URLProp::URLProp(void) : m_cRef(1)
|
|
{
|
|
// Don't validate this until after construction.
|
|
|
|
m_hstg = NULL;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CURLProp));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Destructor for URLProp
|
|
|
|
*/
|
|
URLProp::~URLProp(void)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CURLProp));
|
|
|
|
if (m_hstg)
|
|
{
|
|
PropStg_Destroy(m_hstg);
|
|
m_hstg = NULL;
|
|
}
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CURLProp));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) URLProp::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) URLProp::Release()
|
|
{
|
|
m_cRef--;
|
|
if (m_cRef > 0)
|
|
return m_cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Purpose: IUnknown::QueryInterface method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP URLProp::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
if (IsEqualIID(riid, IID_IUnknown) ||
|
|
IsEqualIID(riid, IID_IPropertyStorage))
|
|
{
|
|
*ppvObj = SAFECAST(this, IPropertyStorage *);
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
Purpose: Initialize the object
|
|
|
|
Returns: S_OK
|
|
E_OUTOFMEMORY
|
|
*/
|
|
STDMETHODIMP URLProp::Init(void)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
// Don't stomp on ourselves if this has already been initialized
|
|
if (NULL == m_hstg)
|
|
{
|
|
hres = PropStg_Create(&m_hstg, PSTGF_DEFAULT);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that retrieves the string property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::GetProp(
|
|
IN PROPID pid,
|
|
IN LPTSTR pszBuf,
|
|
IN int cchBuf)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
ASSERT(pszBuf);
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
*pszBuf = TEXT('\0');
|
|
|
|
hres = ReadMultiple(1, &propspec, &propvar);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (VT_LPWSTR == propvar.vt)
|
|
{
|
|
OleStrToStrN(pszBuf, cchBuf, propvar.pwszVal, -1);
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
if (VT_EMPTY != propvar.vt && VT_ILLEGAL != propvar.vt)
|
|
TraceMsg(TF_WARNING, "URLProp::GetProp: expected propid %#lx to be VT_LPWSTR, but is %s", pid, Dbg_GetVTName(propvar.vt));
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
PropVariantClear(&propvar);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that retrieves the word property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::GetProp(
|
|
IN PROPID pid,
|
|
IN int * piVal)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
ASSERT(piVal);
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
*piVal = 0;
|
|
|
|
hres = ReadMultiple(1, &propspec, &propvar);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (VT_I4 == propvar.vt)
|
|
{
|
|
*piVal = propvar.lVal;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
if (VT_EMPTY != propvar.vt && VT_ILLEGAL != propvar.vt)
|
|
TraceMsg(TF_WARNING, "URLProp::GetProp: expected propid %#lx to be VT_I4, but is %s", pid, Dbg_GetVTName(propvar.vt));
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
PropVariantClear(&propvar);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that retrieves the word property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::GetProp(
|
|
IN PROPID pid,
|
|
IN LPDWORD pdwVal)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
ASSERT(pdwVal);
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
*pdwVal = 0;
|
|
|
|
hres = ReadMultiple(1, &propspec, &propvar);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (VT_UI4 == propvar.vt)
|
|
{
|
|
*pdwVal = propvar.ulVal;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
if (VT_EMPTY != propvar.vt && VT_ILLEGAL != propvar.vt)
|
|
TraceMsg(TF_WARNING, "URLProp::GetProp: expected propid %#lx to be VT_UI4, but is %s", pid, Dbg_GetVTName(propvar.vt));
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
PropVariantClear(&propvar);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that retrieves the word property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::GetProp(
|
|
IN PROPID pid,
|
|
IN WORD * pwVal)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
ASSERT(pwVal);
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
*pwVal = 0;
|
|
|
|
hres = ReadMultiple(1, &propspec, &propvar);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (VT_UI2 == propvar.vt)
|
|
{
|
|
*pwVal = propvar.uiVal;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
if (VT_EMPTY != propvar.vt && VT_ILLEGAL != propvar.vt)
|
|
TraceMsg(TF_WARNING, "URLProp::GetProp: expected propid %#lx to be VT_UI2, but is %s", pid, Dbg_GetVTName(propvar.vt));
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
PropVariantClear(&propvar);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that retrieves the IStream property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::GetProp(
|
|
IN PROPID pid,
|
|
IN IStream **ppStream)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
ASSERT(ppStream);
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
*ppStream = 0;
|
|
|
|
hres = ReadMultiple(1, &propspec, &propvar);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (VT_STREAM == propvar.vt)
|
|
{
|
|
*ppStream = propvar.pStream;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
if (VT_EMPTY != propvar.vt && VT_ILLEGAL != propvar.vt && propvar.lVal != 0)
|
|
TraceMsg(TF_WARNING, "URLProp::GetProp: expected propid %#lx to be VT_STREAM, but is %s", pid, Dbg_GetVTName(propvar.vt));
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
// Do not PropVariantClear(&propvar), because it will call pStream->Release().
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that sets the string property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::SetProp(
|
|
IN PROPID pid,
|
|
IN LPCTSTR psz) OPTIONAL
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
// WARNING:: this function gets called as part of ShellExecute which can be
|
|
// called by 16 bit apps so don't put mondo strings on stack...
|
|
WCHAR *pwsz = NULL;
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
if (psz && *psz)
|
|
{
|
|
SHStrDup(psz, &pwsz);
|
|
propvar.vt = VT_LPWSTR;
|
|
propvar.pwszVal = pwsz;
|
|
}
|
|
else
|
|
propvar.vt = VT_EMPTY;
|
|
|
|
hres = WriteMultiple(1, &propspec, &propvar, 0);
|
|
|
|
if (pwsz)
|
|
CoTaskMemFree(pwsz);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that sets the int property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::SetProp(
|
|
IN PROPID pid,
|
|
IN int iVal)
|
|
{
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
propvar.vt = VT_I4;
|
|
propvar.lVal = iVal;
|
|
|
|
return WriteMultiple(1, &propspec, &propvar, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that sets the dword property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::SetProp(
|
|
IN PROPID pid,
|
|
IN DWORD dwVal)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
propvar.vt = VT_UI4;
|
|
propvar.ulVal = dwVal;
|
|
|
|
hres = WriteMultiple(1, &propspec, &propvar, 0);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that sets the word property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::SetProp(
|
|
IN PROPID pid,
|
|
IN WORD wVal)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
propvar.vt = VT_UI2;
|
|
propvar.uiVal = wVal;
|
|
|
|
hres = WriteMultiple(1, &propspec, &propvar, 0);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that sets the IStream* property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::SetProp(
|
|
IN PROPID pid,
|
|
IN IStream *pStream)
|
|
{
|
|
HRESULT hres;
|
|
PROPSPEC propspec;
|
|
PROPVARIANT propvar;
|
|
|
|
propspec.ulKind = PRSPEC_PROPID;
|
|
propspec.propid = pid;
|
|
|
|
propvar.vt = VT_STREAM;
|
|
propvar.pStream = pStream;
|
|
|
|
hres = WriteMultiple(1, &propspec, &propvar, 0);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP URLProp::IsDirty(void)
|
|
{
|
|
return PropStg_IsDirty(m_hstg);
|
|
}
|
|
|
|
|
|
STDMETHODIMP URLProp::ReadMultiple(IN ULONG cpspec,
|
|
IN const PROPSPEC rgpropspec[],
|
|
IN PROPVARIANT rgpropvar[])
|
|
{
|
|
HRESULT hres = PropStg_ReadMultiple(m_hstg, cpspec, rgpropspec, rgpropvar);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Set the accessed time
|
|
SYSTEMTIME st;
|
|
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, &m_ftAccessed);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP URLProp::WriteMultiple(IN ULONG cpspec,
|
|
IN const PROPSPEC rgpropspec[],
|
|
IN const PROPVARIANT rgpropvar[],
|
|
IN PROPID propidFirst)
|
|
{
|
|
HRESULT hres = PropStg_WriteMultiple(m_hstg, cpspec, rgpropspec,
|
|
rgpropvar, propidFirst);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Set the modified time
|
|
SYSTEMTIME st;
|
|
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, &m_ftModified);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP URLProp::DeleteMultiple(ULONG cpspec, const PROPSPEC rgpropspec[])
|
|
{
|
|
return PropStg_DeleteMultiple(m_hstg, cpspec, rgpropspec);
|
|
}
|
|
|
|
|
|
STDMETHODIMP URLProp::ReadPropertyNames(ULONG cpropid, const PROPID rgpropid[], LPWSTR rgpwszName[])
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP URLProp::WritePropertyNames(ULONG cpropid, const PROPID rgpropid[], const LPWSTR rgpwszName[])
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::DeletePropertyNames method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::DeletePropertyNames(
|
|
IN ULONG cpropid,
|
|
IN const PROPID rgpropid[])
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::SetClass method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::SetClass(
|
|
IN REFCLSID rclsid)
|
|
{
|
|
CopyMemory(&m_clsid, &rclsid, SIZEOF(m_clsid));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::Commit method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::Commit(
|
|
IN DWORD dwFlags)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::Revert method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP URLProp::Revert(void)
|
|
{
|
|
#ifdef DEBUG
|
|
Dump();
|
|
#endif
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::Enum method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP URLProp::Enum(IEnumSTATPROPSTG ** ppenum)
|
|
{
|
|
*ppenum = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::Stat method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::Stat(
|
|
IN STATPROPSETSTG * pstat)
|
|
{
|
|
HRESULT hres = STG_E_INVALIDPARAMETER;
|
|
|
|
if (IS_VALID_WRITE_PTR(pstat, STATPROPSETSTG))
|
|
{
|
|
pstat->fmtid = m_fmtid;
|
|
pstat->clsid = m_clsid;
|
|
pstat->grfFlags = m_grfFlags;
|
|
pstat->mtime = m_ftModified;
|
|
pstat->ctime = m_ftCreated;
|
|
pstat->atime = m_ftAccessed;
|
|
|
|
hres = S_OK;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::SetTimes method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
URLProp::SetTimes(
|
|
IN const FILETIME * pftModified, OPTIONAL
|
|
IN const FILETIME * pftCreated, OPTIONAL
|
|
IN const FILETIME * pftAccessed) OPTIONAL
|
|
{
|
|
HRESULT hres;
|
|
|
|
if (pftModified && !IS_VALID_READ_PTR(pftModified, FILETIME) ||
|
|
pftCreated && !IS_VALID_READ_PTR(pftCreated, FILETIME) ||
|
|
pftAccessed && !IS_VALID_READ_PTR(pftAccessed, FILETIME))
|
|
{
|
|
hres = STG_E_INVALIDPARAMETER;
|
|
}
|
|
else
|
|
{
|
|
if (pftModified)
|
|
m_ftModified = *pftModified;
|
|
|
|
if (pftCreated)
|
|
m_ftCreated = *pftCreated;
|
|
|
|
if (pftAccessed)
|
|
m_ftAccessed = *pftAccessed;
|
|
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
STDMETHODIMP_(void) IntshcutProp::Dump(void)
|
|
{
|
|
if (IsFlagSet(g_dwDumpFlags, DF_URLPROP))
|
|
{
|
|
TraceMsg(TF_ALWAYS, " IntshcutProp obj: %s", m_szFile);
|
|
URLProp::Dump();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
IntshcutProp::IntshcutProp(void)
|
|
{
|
|
// Don't validate this until after construction.
|
|
|
|
*m_szFile = 0;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcutProp));
|
|
}
|
|
|
|
IntshcutProp::~IntshcutProp(void)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcutProp));
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcutProp));
|
|
}
|
|
|
|
|
|
// (These are not related to PID_IS_*)
|
|
#define IPROP_ICONINDEX 0
|
|
#define IPROP_ICONFILE 1
|
|
#define IPROP_HOTKEY 2
|
|
#define IPROP_WORKINGDIR 3
|
|
#define IPROP_SHOWCMD 4
|
|
#define IPROP_WHATSNEW 5
|
|
#define IPROP_AUTHOR 6
|
|
#define IPROP_DESC 7
|
|
#define IPROP_COMMENT 8
|
|
#define IPROP_URL 9 // these two must be the last
|
|
#define IPROP_SCHEME 10 // in this list. See LoadFromFile.
|
|
#define CPROP_INTSHCUT 11 // Count of properties
|
|
|
|
// (we don't write the URL or the scheme in the massive write sweep)
|
|
#define CPROP_INTSHCUT_WRITE (CPROP_INTSHCUT - 2)
|
|
|
|
/*
|
|
Purpose: Load the basic property info like URL.
|
|
|
|
Returns:
|
|
Cond: --
|
|
*/
|
|
STDMETHODIMP IntshcutProp::LoadFromFile(LPCTSTR pszFile)
|
|
{
|
|
HRESULT hres;
|
|
LPWSTR pwszBuf;
|
|
LPTSTR pszBuf;
|
|
CHAR *pszTempBuf;
|
|
static const PROPSPEC rgpropspec[CPROP_INTSHCUT] =
|
|
{
|
|
// This must be initialized in the same order as how the
|
|
// IPROP_* values were defined.
|
|
{ PRSPEC_PROPID, PID_IS_ICONINDEX },
|
|
{ PRSPEC_PROPID, PID_IS_ICONFILE },
|
|
{ PRSPEC_PROPID, PID_IS_HOTKEY },
|
|
{ PRSPEC_PROPID, PID_IS_WORKINGDIR },
|
|
{ PRSPEC_PROPID, PID_IS_SHOWCMD },
|
|
{ PRSPEC_PROPID, PID_IS_WHATSNEW },
|
|
{ PRSPEC_PROPID, PID_IS_AUTHOR },
|
|
{ PRSPEC_PROPID, PID_IS_DESCRIPTION },
|
|
{ PRSPEC_PROPID, PID_IS_COMMENT },
|
|
{ PRSPEC_PROPID, PID_IS_URL },
|
|
{ PRSPEC_PROPID, PID_IS_SCHEME },
|
|
};
|
|
PROPVARIANT rgpropvar[CPROP_INTSHCUT] = { 0 };
|
|
|
|
ASSERT(pszFile);
|
|
|
|
// try to allocate a temporary buffer, don't put on stack as this may be called
|
|
// by 16 bit apps through the shellexecute thunk
|
|
pszTempBuf = (CHAR*)LocalAlloc(LMEM_FIXED, INTERNET_MAX_URL_LENGTH * sizeof(CHAR));
|
|
if (!pszTempBuf)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (!g_fRunningOnNT)
|
|
{
|
|
// Flush the cache first to encourage Win95 kernel to zero-out
|
|
// its buffer. Kernel GP-faults with hundreds of writes made to
|
|
// ini files.
|
|
WritePrivateProfileString(NULL, NULL, NULL, pszFile);
|
|
}
|
|
|
|
// Get the URL
|
|
hres = ReadURLFromFile(pszFile, c_szIntshcut, &pszBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
// Call this method because it does more work before
|
|
// setting the property
|
|
SetURLProp(pszBuf, (IURL_SETURL_FL_GUESS_PROTOCOL | IURL_SETURL_FL_USE_DEFAULT_PROTOCOL));
|
|
|
|
LocalFree(pszBuf);
|
|
}
|
|
|
|
// Get the IDList
|
|
LPITEMIDLIST pidl = NULL;
|
|
hres = ReadIDList(pszFile, &pidl);
|
|
if (S_OK == hres)
|
|
{
|
|
// Call this method because it does more work before
|
|
// setting the property
|
|
SetIDListProp(pidl);
|
|
|
|
ILFree(pidl);
|
|
}
|
|
|
|
#ifndef UNIX
|
|
|
|
// Get icon location
|
|
int nVal;
|
|
hres = ReadIconLocation(pszFile, &pwszBuf, &nVal, pszTempBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
rgpropvar[IPROP_ICONFILE].vt = VT_LPWSTR;
|
|
rgpropvar[IPROP_ICONFILE].pwszVal = pwszBuf;
|
|
|
|
rgpropvar[IPROP_ICONINDEX].vt = VT_I4;
|
|
rgpropvar[IPROP_ICONINDEX].lVal = nVal;
|
|
}
|
|
|
|
// Get the hotkey
|
|
WORD wHotkey;
|
|
hres = ReadHotkey(pszFile, &wHotkey);
|
|
if (S_OK == hres)
|
|
{
|
|
rgpropvar[IPROP_HOTKEY].vt = VT_UI2;
|
|
rgpropvar[IPROP_HOTKEY].uiVal = wHotkey;
|
|
}
|
|
|
|
// Get the working directory
|
|
hres = ReadWorkingDirectory(pszFile, &pwszBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
rgpropvar[IPROP_WORKINGDIR].vt = VT_LPWSTR;
|
|
rgpropvar[IPROP_WORKINGDIR].pwszVal = pwszBuf;
|
|
}
|
|
|
|
// Get the showcmd flag
|
|
hres = ReadShowCmd(pszFile, &nVal);
|
|
rgpropvar[IPROP_SHOWCMD].vt = VT_I4;
|
|
if (S_OK == hres)
|
|
rgpropvar[IPROP_SHOWCMD].lVal = nVal;
|
|
else
|
|
rgpropvar[IPROP_SHOWCMD].lVal = SW_NORMAL;
|
|
|
|
|
|
// Get the What's New bulletin
|
|
hres = ReadStringFromFile(pszFile, c_szIntshcut, ISHCUT_INISTRING_WHATSNEW, &pwszBuf, pszTempBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
rgpropvar[IPROP_WHATSNEW].vt = VT_LPWSTR;
|
|
rgpropvar[IPROP_WHATSNEW].pwszVal = pwszBuf;
|
|
}
|
|
|
|
// Get the Author
|
|
hres = ReadStringFromFile(pszFile, c_szIntshcut, ISHCUT_INISTRING_AUTHOR, &pwszBuf, pszTempBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
rgpropvar[IPROP_AUTHOR].vt = VT_LPWSTR;
|
|
rgpropvar[IPROP_AUTHOR].pwszVal = pwszBuf;
|
|
}
|
|
|
|
// Get the Description
|
|
hres = ReadStringFromFile(pszFile, c_szIntshcut, ISHCUT_INISTRING_DESC, &pwszBuf, pszTempBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
rgpropvar[IPROP_DESC].vt = VT_LPWSTR;
|
|
rgpropvar[IPROP_DESC].pwszVal = pwszBuf;
|
|
}
|
|
|
|
// Get the Comment
|
|
hres = ReadStringFromFile(pszFile, c_szIntshcut, ISHCUT_INISTRING_COMMENT, &pwszBuf, pszTempBuf);
|
|
if (S_OK == hres)
|
|
{
|
|
rgpropvar[IPROP_COMMENT].vt = VT_LPWSTR;
|
|
rgpropvar[IPROP_COMMENT].pwszVal = pwszBuf;
|
|
}
|
|
|
|
#endif /* !UNIX */
|
|
|
|
// Write it all out to our in-memory storage. Note we're using
|
|
// CPROP_INTSHCUT_WRITE, which should be the size of the array minus the
|
|
// url and scheme propids, since they were written separately
|
|
// above.
|
|
hres = WriteMultiple(CPROP_INTSHCUT_WRITE, (PROPSPEC *)rgpropspec, rgpropvar, 0);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Unmark *all* these properties, since we're initializing from
|
|
// the file
|
|
PropStg_DirtyMultiple(m_hstg, ARRAYSIZE(rgpropspec), rgpropspec, FALSE);
|
|
}
|
|
|
|
// Get the times. We don't support the Accessed time for internet
|
|
// shortcuts updating this field would cause the shortcut to be
|
|
// constantly written to disk to record the Accessed time simply
|
|
// when a property is read. A huge perf hit!
|
|
|
|
ZeroMemory(&m_ftAccessed, sizeof(m_ftAccessed));
|
|
|
|
DWORD cbData = SIZEOF(m_ftModified);
|
|
ReadBinaryFromFile(pszFile, c_szIntshcut, ISHCUT_INISTRING_MODIFIED, &m_ftModified, cbData);
|
|
|
|
// Free up the buffers that we allocated
|
|
int cprops;
|
|
PROPVARIANT * ppropvar;
|
|
for (cprops = ARRAYSIZE(rgpropvar), ppropvar = rgpropvar; 0 < cprops; cprops--)
|
|
{
|
|
if (VT_LPWSTR == ppropvar->vt)
|
|
{
|
|
ASSERT(ppropvar->pwszVal);
|
|
LocalFree(ppropvar->pwszVal);
|
|
}
|
|
ppropvar++;
|
|
}
|
|
|
|
LocalFree((HLOCAL)pszTempBuf);
|
|
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP IntshcutProp::Init(void)
|
|
{
|
|
return URLProp::Init();
|
|
}
|
|
|
|
STDMETHODIMP IntshcutProp::InitFromFile(LPCTSTR pszFile)
|
|
{
|
|
// Initialize the in-memory property storage from the file
|
|
// and database
|
|
HRESULT hres = Init();
|
|
if (SUCCEEDED(hres) && pszFile)
|
|
{
|
|
StrCpyN(m_szFile, pszFile, SIZECHARS(m_szFile));
|
|
hres = LoadFromFile(m_szFile);
|
|
}
|
|
else
|
|
m_szFile[0] = 0;
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
typedef struct
|
|
{
|
|
LPTSTR pszFile;
|
|
} COMMITISDATA;
|
|
|
|
/*
|
|
Purpose: Commit the values for any known properties to the file
|
|
|
|
Note this callback is called only for dirty values.
|
|
|
|
Returns: S_OK if alright
|
|
S_FALSE to skip this value
|
|
error to stop
|
|
|
|
*/
|
|
STDAPI CommitISProp(
|
|
IN PROPID propid,
|
|
IN PROPVARIANT * ppropvar,
|
|
IN LPARAM lParam)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
COMMITISDATA * pcd = (COMMITISDATA *)lParam;
|
|
|
|
ASSERT(ppropvar);
|
|
ASSERT(pcd);
|
|
|
|
LPWSTR pwsz;
|
|
USHORT uiVal;
|
|
LONG lVal;
|
|
IStream *pStream;
|
|
|
|
switch (propid)
|
|
{
|
|
case PID_IS_URL:
|
|
case PID_IS_ICONFILE:
|
|
case PID_IS_WORKINGDIR:
|
|
case PID_IS_WHATSNEW:
|
|
case PID_IS_AUTHOR:
|
|
case PID_IS_DESCRIPTION:
|
|
case PID_IS_COMMENT:
|
|
if (VT_LPWSTR == ppropvar->vt)
|
|
pwsz = ppropvar->pwszVal;
|
|
else
|
|
pwsz = NULL;
|
|
|
|
switch (propid)
|
|
{
|
|
case PID_IS_URL:
|
|
hres = WriteGenericString(pcd->pszFile, c_szIntshcut, ISHCUT_INISTRING_URL, pwsz);
|
|
break;
|
|
|
|
case PID_IS_ICONFILE:
|
|
hres = WriteIconFile(pcd->pszFile, pwsz);
|
|
break;
|
|
|
|
case PID_IS_WORKINGDIR:
|
|
hres = WriteGenericString(pcd->pszFile, c_szIntshcut, ISHCUT_INISTRING_WORKINGDIR, pwsz);
|
|
break;
|
|
|
|
case PID_IS_WHATSNEW:
|
|
hres = WriteGenericString(pcd->pszFile, c_szIntshcut, ISHCUT_INISTRING_WHATSNEW, pwsz);
|
|
break;
|
|
|
|
case PID_IS_AUTHOR:
|
|
hres = WriteGenericString(pcd->pszFile, c_szIntshcut, ISHCUT_INISTRING_AUTHOR, pwsz);
|
|
break;
|
|
|
|
case PID_IS_DESCRIPTION:
|
|
hres = WriteGenericString(pcd->pszFile, c_szIntshcut, ISHCUT_INISTRING_DESC, pwsz);
|
|
break;
|
|
|
|
case PID_IS_COMMENT:
|
|
hres = WriteGenericString(pcd->pszFile, c_szIntshcut, ISHCUT_INISTRING_COMMENT, pwsz);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0); // should never get here
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PID_IS_ICONINDEX:
|
|
if (VT_I4 == ppropvar->vt)
|
|
hres = WriteIconIndex(pcd->pszFile, ppropvar->lVal);
|
|
break;
|
|
|
|
case PID_IS_HOTKEY:
|
|
if (VT_UI2 == ppropvar->vt)
|
|
uiVal = ppropvar->uiVal;
|
|
else
|
|
uiVal = 0;
|
|
|
|
hres = WriteHotkey(pcd->pszFile, uiVal);
|
|
break;
|
|
|
|
case PID_IS_SHOWCMD:
|
|
if (VT_I4 == ppropvar->vt)
|
|
lVal = ppropvar->lVal;
|
|
else
|
|
lVal = SW_NORMAL;
|
|
|
|
hres = WriteShowCmd(pcd->pszFile, lVal);
|
|
break;
|
|
|
|
case PID_IS_SCHEME:
|
|
// Don't write this one out
|
|
break;
|
|
|
|
case PID_IS_IDLIST:
|
|
if (VT_STREAM == ppropvar->vt)
|
|
pStream = ppropvar->pStream;
|
|
else
|
|
pStream = NULL;
|
|
|
|
hres = WriteIDList(pcd->pszFile, pStream);
|
|
break;
|
|
|
|
|
|
default:
|
|
TraceMsg(TF_WARNING, "Don't know how to commit url property (%#lx)", propid);
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (FAILED(hres))
|
|
TraceMsg(TF_WARNING, "Failed to save url property (%#lx) to file %s", propid, pcd->pszFile);
|
|
#endif
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: IPropertyStorage::Commit method for URLProp
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
IntshcutProp::Commit(
|
|
IN DWORD dwFlags)
|
|
{
|
|
HRESULT hres;
|
|
COMMITISDATA cd;
|
|
|
|
TraceMsg(TF_INTSHCUT, "Writing properties to \"%s\"", m_szFile);
|
|
|
|
cd.pszFile = m_szFile;
|
|
|
|
// Enumerate thru the dirty property values that get saved to the
|
|
// file
|
|
hres = PropStg_Enum(m_hstg, PSTGEF_DIRTY, CommitISProp, (LPARAM)&cd);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Now mark everything clean
|
|
PropStg_DirtyAll(m_hstg, FALSE);
|
|
|
|
// Save the times. Don't write out the Accessed time for perf.
|
|
// See LoadFromFile.
|
|
EVAL(SUCCEEDED(WriteBinaryToFile(m_szFile, c_szIntshcut, ISHCUT_INISTRING_MODIFIED, &m_ftModified,
|
|
SIZEOF(m_ftModified))));
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
Dump();
|
|
#endif
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function to set the file name.
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
IntshcutProp::SetFileName(
|
|
IN LPCTSTR pszFile)
|
|
{
|
|
if(pszFile)
|
|
{
|
|
ASSERT(IS_VALID_STRING_PTR(pszFile, -1));
|
|
StrCpyN(m_szFile, pszFile, SIZECHARS(m_szFile));
|
|
}
|
|
else
|
|
{
|
|
*m_szFile = TEXT('\0');;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that sets the URL.
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
IntshcutProp::SetIDListProp(
|
|
LPCITEMIDLIST pcidl)
|
|
{
|
|
HRESULT hres;
|
|
IStream *pstmPidl;
|
|
|
|
if (pcidl)
|
|
{
|
|
// ???
|
|
// PERF: This loads OLE. Is this OK?
|
|
|
|
hres = CreateStreamOnHGlobal(NULL, TRUE, &pstmPidl);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = ILSaveToStream(pstmPidl, pcidl);
|
|
|
|
if (SUCCEEDED(hres))
|
|
hres = SetProp(PID_IS_IDLIST, pstmPidl);
|
|
|
|
pstmPidl->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = SetProp(PID_IS_IDLIST, NULL);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*
|
|
Purpose: Helper function that sets the URL. This function
|
|
optionally canonicalizes the string as well.
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
IntshcutProp::SetURLProp(
|
|
IN LPCTSTR pszURL, OPTIONAL
|
|
IN DWORD dwFlags)
|
|
{
|
|
HRESULT hres;
|
|
|
|
// Warning this function can be called as part of shellexecute which can be
|
|
// thunked up to by a 16 bit app, so be carefull what you put on stack...
|
|
|
|
BOOL bChanged;
|
|
|
|
struct tbufs
|
|
{
|
|
TCHAR szUrl[INTERNET_MAX_URL_LENGTH];
|
|
TCHAR szUrlT[INTERNET_MAX_URL_LENGTH];
|
|
};
|
|
|
|
struct tbufs *ptbufs;
|
|
|
|
ptbufs = (struct tbufs *)LocalAlloc(LMEM_FIXED, sizeof(struct tbufs));
|
|
if (!ptbufs)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hres = GetProp(PID_IS_URL, ptbufs->szUrl, INTERNET_MAX_URL_LENGTH);
|
|
|
|
bChanged = !(( !pszURL && S_OK != hres) ||
|
|
(pszURL && S_OK == hres && 0 == StrCmp(pszURL, ptbufs->szUrl)));
|
|
|
|
hres = S_OK;
|
|
if (bChanged)
|
|
{
|
|
if (NULL == pszURL)
|
|
{
|
|
hres = SetProp(PID_IS_URL, pszURL);
|
|
if (S_OK == hres)
|
|
hres = SetProp(PID_IS_SCHEME, URL_SCHEME_UNKNOWN);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwFlagsT = UQF_CANONICALIZE;
|
|
|
|
// Translate the URL
|
|
|
|
if (IsFlagSet(dwFlags, IURL_SETURL_FL_GUESS_PROTOCOL))
|
|
SetFlag(dwFlagsT, UQF_GUESS_PROTOCOL);
|
|
|
|
if (IsFlagSet(dwFlags, IURL_SETURL_FL_USE_DEFAULT_PROTOCOL))
|
|
SetFlag(dwFlagsT, UQF_USE_DEFAULT_PROTOCOL);
|
|
|
|
// Translate the URL
|
|
hres = IURLQualify(pszURL, dwFlagsT, ptbufs->szUrlT, NULL, NULL);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Is the URL different after being translated?
|
|
bChanged = (0 != StrCmp(ptbufs->szUrlT, ptbufs->szUrl));
|
|
|
|
hres = S_OK;
|
|
if (bChanged)
|
|
{
|
|
// Yes; validate and get the scheme
|
|
PARSEDURL pu;
|
|
|
|
pu.cbSize = SIZEOF(pu);
|
|
hres = ParseURL(ptbufs->szUrlT, &pu);
|
|
|
|
if (S_OK == hres)
|
|
hres = SetProp(PID_IS_URL, ptbufs->szUrlT);
|
|
|
|
if (S_OK == hres)
|
|
hres = SetProp(PID_IS_SCHEME, (DWORD)pu.nScheme);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree((HLOCAL)ptbufs);
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*
|
|
Purpose: Helper function that sets the string property
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
IntshcutProp::SetProp(
|
|
IN PROPID pid,
|
|
IN LPCTSTR psz) OPTIONAL
|
|
{
|
|
HRESULT hr;
|
|
|
|
// WARNING:: this function gets called as part of ShellExecute which can be
|
|
// called by 16 bit apps so don't put mondo strings on stack...
|
|
LPCWSTR pszUrl = psz;
|
|
LPWSTR pszTemp = NULL;
|
|
|
|
// For URLs, we need to check for security spoofs
|
|
if (PID_IS_URL == pid && psz && IsSpecialUrl((LPWSTR)psz)) //bugbug: remove cast
|
|
{
|
|
SHStrDup(psz, &pszTemp);
|
|
|
|
if (NULL != pszTemp)
|
|
{
|
|
// Unescape the url and look for a security context delimitor
|
|
hr = WrapSpecialUrlFlat(pszTemp, lstrlen(pszTemp)+1);
|
|
if (E_ACCESSDENIED == hr)
|
|
{
|
|
// Security delimitor found, so wack it off
|
|
SHRemoveURLTurd(pszTemp);
|
|
pszUrl = pszTemp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
hr = super::SetProp(pid, pszUrl);
|
|
|
|
if (pszTemp)
|
|
{
|
|
CoTaskMemFree(pszTemp);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDAPI CIntshcutProp_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
|
|
{
|
|
HRESULT hres;
|
|
|
|
*ppvOut = NULL;
|
|
|
|
if (punkOuter)
|
|
{
|
|
// No
|
|
hres = CLASS_E_NOAGGREGATION;
|
|
}
|
|
else
|
|
{
|
|
IUnknown * piunk = (IUnknown *)(IPropertyStorage *)new IntshcutProp;
|
|
if ( !piunk )
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
hres = piunk->QueryInterface(riid, ppvOut);
|
|
piunk->Release();
|
|
}
|
|
}
|
|
|
|
return hres; // S_OK or E_NOINTERFACE
|
|
}
|