|
|
#include "shellprv.h"
#include "intshcut.h"
#include "ids.h"
#include <ntquery.h> // defines some values used for fmtid and pid
#include <sddl.h> // For ConvertSidToStringSid()
#include "prop.h" // SCID_ stuff
#include "netview.h" // SHWNetGetConnection
#include "clsobj.h"
HRESULT ReadProperty(IPropertySetStorage *ppss, REFFMTID fmtid, PROPID pid, VARIANT *pVar) { VariantInit(pVar);
IPropertyStorage *pps; HRESULT hr = ppss->Open(fmtid, STGM_READ | STGM_SHARE_EXCLUSIVE, &pps); if (SUCCEEDED(hr)) { PROPSPEC PropSpec; PROPVARIANT PropVar = {0};
PropSpec.ulKind = PRSPEC_PROPID; PropSpec.propid = pid;
hr = SHPropStgReadMultiple( pps, 0, 1, &PropSpec, &PropVar ); if (SUCCEEDED(hr)) { hr = PropVariantToVariant(&PropVar, pVar); PropVariantClear(&PropVar); } pps->Release(); } return hr; }
BOOL IsSlowProperty(IPropertySetStorage *ppss, REFFMTID fmtid, PROPID pid) { IPropertyStorage *pps; BOOL bRet = FALSE;
if (SUCCEEDED(ppss->Open(fmtid, STGM_READ | STGM_SHARE_EXCLUSIVE, &pps))) { IQueryPropertyFlags *pqsp; if (SUCCEEDED(pps->QueryInterface(IID_PPV_ARG(IQueryPropertyFlags, &pqsp)))) { PROPSPEC PropSpec; PROPVARIANT PropVar = {0};
PropSpec.ulKind = PRSPEC_PROPID; PropSpec.propid = pid;
SHCOLSTATEF csFlags; if (SUCCEEDED(pqsp->GetFlags(&PropSpec, &csFlags))) { bRet = ((csFlags & SHCOLSTATE_SLOW) == SHCOLSTATE_SLOW); }
// If the property isn't part of this property set, IsSlowProperty will return fairlure,
// which we'll treat as a fast property.
pqsp->Release(); }
pps->Release(); } return bRet; }
class CBaseColumnProvider : public IPersist, public IColumnProvider { // IUnknown methods
public: STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) { static const QITAB qit[] = { QITABENT(CBaseColumnProvider, IColumnProvider), // IID_IColumnProvider
QITABENT(CBaseColumnProvider, IPersist), // IID_IPersist
{ 0 }, }; return QISearch(this, qit, riid, ppv); }; STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); };
STDMETHODIMP_(ULONG) Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; };
// IPersist
STDMETHODIMP GetClassID(CLSID *pClassID) { *pClassID = *_pclsid; return S_OK; };
// IColumnProvider
STDMETHODIMP Initialize(LPCSHCOLUMNINIT psci) { return S_OK ; } STDMETHODIMP GetColumnInfo(DWORD dwIndex, LPSHCOLUMNINFO psci);
CBaseColumnProvider(const CLSID *pclsid, const COLUMN_INFO rgColMap[], int iCount, const LPCWSTR rgExts[]) : _cRef(1), _pclsid(pclsid), _rgColumns(rgColMap), _iCount(iCount), _rgExts(rgExts) { DllAddRef(); }
protected: virtual ~CBaseColumnProvider() { DllRelease(); }
BOOL _IsHandled(LPCWSTR pszExt); int _iCount; const COLUMN_INFO *_rgColumns;
private: long _cRef; const CLSID * _pclsid; const LPCWSTR *_rgExts; };
// the index is an arbitrary zero based index used for enumeration
STDMETHODIMP CBaseColumnProvider::GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO *psci) { ZeroMemory(psci, sizeof(*psci));
if (dwIndex < (UINT) _iCount) { psci->scid = *_rgColumns[dwIndex].pscid; psci->cChars = _rgColumns[dwIndex].cChars; psci->vt = _rgColumns[dwIndex].vt; psci->fmt = _rgColumns[dwIndex].fmt; psci->csFlags = _rgColumns[dwIndex].csFlags;
TCHAR szTemp[MAX_COLUMN_NAME_LEN]; LoadString(HINST_THISDLL, _rgColumns[dwIndex].idTitle, szTemp, ARRAYSIZE(szTemp)); SHTCharToUnicode(szTemp, psci->wszTitle, ARRAYSIZE(psci->wszTitle));
return S_OK; } return S_FALSE; }
// see if this file type is one we are interested in
BOOL CBaseColumnProvider::_IsHandled(LPCWSTR pszExt) { if (_rgExts) { for (int i = 0; _rgExts[i]; i++) { if (0 == StrCmpIW(pszExt, _rgExts[i])) return TRUE; } return FALSE; } return TRUE; }
// col handler that works over IPropertySetStorage handlers
const COLUMN_INFO c_rgDocObjColumns[] = { DEFINE_COL_STR_ENTRY(SCID_Author, 20, IDS_EXCOL_AUTHOR), DEFINE_COL_STR_ENTRY(SCID_Title, 20, IDS_EXCOL_TITLE), DEFINE_COL_STR_DLG_ENTRY(SCID_Subject, 20, IDS_EXCOL_SUBJECT), DEFINE_COL_STR_DLG_ENTRY(SCID_Category, 20, IDS_EXCOL_CATEGORY), DEFINE_COL_INT_DLG_ENTRY(SCID_PageCount, 10, IDS_EXCOL_PAGECOUNT), DEFINE_COL_STR_ENTRY(SCID_Comment, 30, IDS_EXCOL_COMMENT), DEFINE_COL_STR_DLG_ENTRY(SCID_Copyright, 30, IDS_EXCOL_COPYRIGHT), DEFINE_COL_STR_ENTRY(SCID_MUSIC_Artist, 15, IDS_EXCOL_ARTIST), DEFINE_COL_STR_ENTRY(SCID_MUSIC_Album, 15, IDS_EXCOL_ALBUM), DEFINE_COL_STR_ENTRY(SCID_MUSIC_Year, 10, IDS_EXCOL_YEAR), DEFINE_COL_INT_ENTRY(SCID_MUSIC_Track, 5, IDS_EXCOL_TRACK), DEFINE_COL_STR_ENTRY(SCID_MUSIC_Genre, 20, IDS_EXCOL_GENRE), DEFINE_COL_STR_ENTRY(SCID_AUDIO_Duration, 15, IDS_EXCOL_DURATION), DEFINE_COL_STR_ENTRY(SCID_AUDIO_Bitrate, 15, IDS_EXCOL_BITRATE), DEFINE_COL_STR_ENTRY(SCID_DRM_Protected, 10, IDS_EXCOL_PROTECTED), DEFINE_COL_STR_ENTRY(SCID_CameraModel, 20, IDS_EXCOL_CAMERAMODEL), DEFINE_COL_STR_ENTRY(SCID_WhenTaken, 20, IDS_EXCOL_WHENTAKEN),
DEFINE_COL_STR_ENTRY(SCID_ImageDimensions, 20, IDS_EXCOL_DIMENSIONS), DEFINE_COL_INT_HIDDEN_ENTRY(SCID_ImageCX), DEFINE_COL_INT_HIDDEN_ENTRY(SCID_ImageCY),
DEFINE_COL_DATE_HIDDEN_ENTRY(SCID_DocCreated), };
class CPropStgColumns : public CBaseColumnProvider { STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
private: // help on initializing base classes: mk:@ivt:vclang/FB/DD/S44B5E.HTM
CPropStgColumns() : CBaseColumnProvider(&CLSID_DocFileColumnProvider, c_rgDocObjColumns, ARRAYSIZE(c_rgDocObjColumns), NULL) { ASSERT(_wszLastFile[0] == 0); ASSERT(_bSlowPropertiesCached == FALSE); }; ~CPropStgColumns() { _FreeCache(); } // for the cache
VARIANT _rgvCache[ARRAYSIZE(c_rgDocObjColumns)]; // zero'ing allocator will fill with VT_EMPTY
BOOL _rgbSlow[ARRAYSIZE(c_rgDocObjColumns)]; // Store if each property is "slow".
WCHAR _wszLastFile[MAX_PATH]; HRESULT _hrCache; BOOL _bSlowPropertiesCached;
#ifdef DEBUG
int deb_dwTotal, deb_dwMiss; #endif
void _FreeCache();
friend HRESULT CDocFileColumns_CreateInstance(IUnknown *punk, REFIID riid, void **ppv); };
void CPropStgColumns::_FreeCache() { for (int i = 0; i < ARRAYSIZE(_rgvCache); i++) VariantClear(&_rgvCache[i]);
_hrCache = S_OK; }
STDMETHODIMP CPropStgColumns::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData) { HRESULT hr;
// VariantCopy requires input to be initialized, and we handle failure case
VariantInit(pvarData);
// is this even a property we support?
for (int iProp = 0; iProp < _iCount; iProp++) { if (IsEqualSCID(*_rgColumns[iProp].pscid, *pscid)) { goto found; } }
// Unknown property
return S_FALSE;
found:
#ifdef DEBUG
deb_dwTotal++; #endif
// Three cases here:
// 1) We need to update the cache. Fetch the properties again (and only get fast props if we asked for a fast prop)
// 2) We've only cached fast properties so far, and we asked for a slow property, so now we need to get slow props.
// 3) The property we want is cached.
if ((pscd->dwFlags & SHCDF_UPDATEITEM) || (StrCmpW(_wszLastFile, pscd->wszFile) != 0)) { // 1) Cache is no good - item has been updated, or this is a different file.
// SHCDF_UPDATEITEM flag is a hint
// that the file for which we are getting data has changed since the last call. This flag
// is only passed once per filename, not once per column per filename so update the entire
// cache if this flag is set.
// sanity check our caching. If the shell thread pool is > 1, we will thrash like mad, and should change this
#ifdef DEBUG
deb_dwMiss++; if ((deb_dwTotal > 3) && (deb_dwTotal / deb_dwMiss <= 3)) TraceMsg(TF_DEFVIEW, "Column data caching is ineffective (%d misses for %d access)", deb_dwMiss, deb_dwTotal); #endif
_FreeCache();
hr = StringCchCopy(_wszLastFile, ARRAYSIZE(_wszLastFile), pscd->wszFile); if (SUCCEEDED(hr)) { IPropertySetStorage *ppss; hr = SHFileSysBindToStorage(pscd->wszFile, pscd->dwFileAttributes, STGM_READ | STGM_SHARE_DENY_WRITE, 0, IID_PPV_ARG(IPropertySetStorage, &ppss));
_hrCache = hr;
if (SUCCEEDED(hr)) { // Did we ask for a slow property?
BOOL bSlowProperty = IsSlowProperty(ppss, _rgColumns[iProp].pscid->fmtid, _rgColumns[iProp].pscid->pid);
hr = E_INVALIDARG; // normally overwritten by hrT below
for (int i = 0; i < _iCount; i++) { // For every property, take note if it is "slow"
_rgbSlow[i] = IsSlowProperty(ppss, _rgColumns[i].pscid->fmtid, _rgColumns[i].pscid->pid);
// Only retrieve a value right now if we asked for a slow property, or this is not a slow property.
if (bSlowProperty || (!_rgbSlow[i])) { // it would be slightly more efficient, but more code, to set up the propid array to call ReadMultiple
HRESULT hrT = ReadProperty(ppss, _rgColumns[i].pscid->fmtid, _rgColumns[i].pscid->pid, &_rgvCache[i]); if (i == iProp) { hr = (SUCCEEDED(hrT) ? VariantCopy(pvarData, &_rgvCache[i]) : hrT); } } }
ppss->Release(); _bSlowPropertiesCached = bSlowProperty; } } } else if (_rgbSlow[iProp] && !_bSlowPropertiesCached) { // 2) We asked for a slow property, but slow properties haven't been cached yet.
// Bind to the storage a second time. This is a perf hit, but should be
// minor compared to getting slow properties.
IPropertySetStorage *ppss; hr = SHFileSysBindToStorage(pscd->wszFile, pscd->dwFileAttributes, STGM_READ | STGM_SHARE_DENY_WRITE, 0, IID_PPV_ARG(IPropertySetStorage, &ppss));
_hrCache = hr;
if (SUCCEEDED(hr)) { hr = E_INVALIDARG; // normally overwritten by hrT below
for (int i = 0; i < _iCount; i++) { if (_rgbSlow[i]) // If it's slow, get it.
{ ASSERT(_rgvCache[i].vt == VT_EMPTY); // Because we haven't retrieved it yet.
HRESULT hrT = ReadProperty(ppss, _rgColumns[i].pscid->fmtid, _rgColumns[i].pscid->pid, &_rgvCache[i]); if (i == iProp) { hr = (SUCCEEDED(hrT) ? VariantCopy(pvarData, &_rgvCache[i]) : hrT); } } } ppss->Release();
_bSlowPropertiesCached = TRUE; }
} else { // 3) It's not a slow property, or slow properties are already cached.
ASSERT(!_rgbSlow[iProp] || _bSlowPropertiesCached);
hr = S_FALSE; // assume we don't have it
if (SUCCEEDED(_hrCache)) { if (_rgvCache[iProp].vt != VT_EMPTY) { hr = VariantCopy(pvarData, &_rgvCache[iProp]); } } }
return hr; }
STDAPI CDocFileColumns_CreateInstance(IUnknown *punk, REFIID riid, void **ppv) { HRESULT hr; CPropStgColumns *pdocp = new CPropStgColumns; if (pdocp) { hr = pdocp->QueryInterface(riid, ppv); pdocp->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; }
return hr; }
// Shortcut handler
// W because pidl is always converted to widechar filename
const LPCWSTR c_szURLExtensions[] = { L".URL", L".LNK", NULL };
const COLUMN_INFO c_rgURLColumns[] = { DEFINE_COL_STR_ENTRY(SCID_Author, 20, IDS_EXCOL_AUTHOR), DEFINE_COL_STR_ENTRY(SCID_Title, 20, IDS_EXCOL_TITLE), DEFINE_COL_STR_ENTRY(SCID_Comment, 30, IDS_EXCOL_COMMENT), };
class CLinkColumnProvider : public CBaseColumnProvider { STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
private: // help on initializing base classes: mk:@ivt:vclang/FB/DD/S44B5E.HTM
CLinkColumnProvider() : CBaseColumnProvider(&CLSID_LinkColumnProvider, c_rgURLColumns, ARRAYSIZE(c_rgURLColumns), c_szURLExtensions) {};
// friends
friend HRESULT CLinkColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv); };
const struct { DWORD dwSummaryPid; DWORD dwURLPid; } c_URLMap[] = { { PIDSI_AUTHOR, PID_INTSITE_AUTHOR }, { PIDSI_TITLE, PID_INTSITE_TITLE }, { PIDSI_COMMENTS, PID_INTSITE_COMMENT }, };
DWORD _MapSummaryToSitePID(DWORD pid) { for (int i = 0; i < ARRAYSIZE(c_URLMap); i++) { if (c_URLMap[i].dwSummaryPid == pid) return c_URLMap[i].dwURLPid; } return -1; }
STDMETHODIMP CLinkColumnProvider::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData) { HRESULT hr; const CLSID *pclsidLink = &CLSID_ShellLink;
// Some of the code-paths below assume pvarData is initialized
VariantInit(pvarData);
// should we match against a list of known extensions, or always try to open?
if (FILE_ATTRIBUTE_DIRECTORY & pscd->dwFileAttributes) { if (PathIsShortcut(pscd->wszFile, pscd->dwFileAttributes)) { pclsidLink = &CLSID_FolderShortcut; // we are dealing with a folder shortcut now
} else { return S_FALSE; } } else { if (!_IsHandled(pscd->pwszExt)) { return S_FALSE; } }
if (StrCmpIW(pscd->pwszExt, L".URL") == 0) { //
// its a .URL so lets handle it by creating the Internet Shortcut object, loading
// the file and then reading the properties from it.
//
IPropertySetStorage *ppss; hr = LoadFromFile(CLSID_InternetShortcut, pscd->wszFile, IID_PPV_ARG(IPropertySetStorage, &ppss)); if (SUCCEEDED(hr)) { UINT pid; GUID fmtid;
if (IsEqualGUID(pscid->fmtid, FMTID_SummaryInformation)) { fmtid = FMTID_InternetSite; pid = _MapSummaryToSitePID(pscid->pid); } else { fmtid = pscid->fmtid; pid = pscid->pid; }
hr = ReadProperty(ppss, fmtid, pid, pvarData); ppss->Release(); } } else { //
// open the .LNK file, load it and then read the description for it. we then
// return this a the comment for this object.
//
if (IsEqualSCID(*pscid, SCID_Comment)) { IShellLink *psl; hr = LoadFromFile(*pclsidLink, pscd->wszFile, IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { TCHAR szBuffer[MAX_PATH];
hr = psl->GetDescription(szBuffer, ARRAYSIZE(szBuffer)); if (SUCCEEDED(hr) && szBuffer[0]) { hr = InitVariantFromStr(pvarData, szBuffer); } else { IQueryInfo *pqi; if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IQueryInfo, &pqi)))) { WCHAR *pwszTip;
if (SUCCEEDED(pqi->GetInfoTip(0, &pwszTip)) && pwszTip) { hr = InitVariantFromStr(pvarData, pwszTip); SHFree(pwszTip); } pqi->Release(); } }
psl->Release(); } } else hr = S_FALSE; }
return hr; }
STDAPI CLinkColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv) { HRESULT hr; CLinkColumnProvider *pdocp = new CLinkColumnProvider; if (pdocp) { hr = pdocp->QueryInterface(riid, ppv); pdocp->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; }
return hr; }
const COLUMN_INFO c_rgFileSysColumns[] = { DEFINE_COL_STR_ENTRY(SCID_OWNER, 20, IDS_EXCOL_OWNER), };
class COwnerColumnProvider : public CBaseColumnProvider { STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
private: COwnerColumnProvider() : CBaseColumnProvider(&CLSID_FileSysColumnProvider, c_rgFileSysColumns, ARRAYSIZE(c_rgFileSysColumns), NULL) { ASSERT(_wszLastFile[0] == 0); ASSERT(_psid==NULL && _pwszName==NULL && _psd==NULL); LoadString(HINST_THISDLL, IDS_BUILTIN_DOMAIN, _szBuiltin, ARRAYSIZE(_szBuiltin)); };
~COwnerColumnProvider() { _CacheSidName(NULL, NULL, NULL); }
WCHAR _wszLastFile[MAX_PATH];
// Since we typically get pinged for files all in the same folder,
// cache the "folder to server" mapping to avoid calling
// WNetGetConnection five million times.
//
// Since files in the same directory tend to have the same owner,
// we cache the SID/Name mapping.
//
// Column providers do not have to support multithreaded clients,
// so we won't take any critical sections.
//
HRESULT _LookupOwnerName(LPCTSTR pszFile, VARIANT *pvar); void _CacheSidName(PSECURITY_DESCRIPTOR psd, void *psid, LPCWSTR pwszName);
void *_psid; LPWSTR _pwszName; PSECURITY_DESCRIPTOR _psd; // _psid points into here
int _iCachedDrive; // What drive letter is cached in _pszServer?
LPTSTR _pszServer; // What server to use (NULL = local machine)
TCHAR _szBuiltin[MAX_COMPUTERNAME_LENGTH + 1];
friend HRESULT CFileSysColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv); };
//
// _CacheSidName takes ownership of the psd. (psid points into the psd)
//
void COwnerColumnProvider::_CacheSidName(PSECURITY_DESCRIPTOR psd, void *psid, LPCWSTR pwszName) { LocalFree(_psd); _psd = psd; _psid = psid;
Str_SetPtrW(&_pwszName, pwszName); }
//
// Given a string of the form \\server\share\blah\blah, stomps the
// inner backslash (if necessary) and returns a pointer to "server".
//
STDAPI_(LPTSTR) PathExtractServer(LPTSTR pszUNC) { if (PathIsUNC(pszUNC)) { pszUNC += 2; // Skip over the two leading backslashes
LPTSTR pszEnd = StrChr(pszUNC, TEXT('\\')); if (pszEnd) *pszEnd = TEXT('\0'); // nuke the backslash
} else { pszUNC = NULL; } return pszUNC; }
HRESULT COwnerColumnProvider::_LookupOwnerName(LPCTSTR pszFile, VARIANT *pvar) { pvar->vt = VT_BSTR; pvar->bstrVal = NULL;
PSECURITY_DESCRIPTOR psd; void *psid;
DWORD err = GetNamedSecurityInfo(const_cast<LPTSTR>(pszFile), SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &psid, NULL, NULL, NULL, &psd); if (err == ERROR_SUCCESS) { if (_psid && EqualSid(psid, _psid) && _pwszName) { pvar->bstrVal = SysAllocString(_pwszName); LocalFree(psd); err = ERROR_SUCCESS; } else { LPTSTR pszServer; TCHAR szServer[MAX_PATH];
//
// Now go figure out which server to resolve the SID against.
//
if (PathIsUNC(pszFile)) { // don't care if it is truncated or not.
StringCchCopy(szServer, ARRAYSIZE(szServer), pszFile); pszServer = PathExtractServer(szServer); } else if (pszFile[0] == _iCachedDrive) { // Local drive letter already in cache -- use it
pszServer = _pszServer; } else { // Local drive not cached -- cache it
_iCachedDrive = pszFile[0]; DWORD cch = ARRAYSIZE(szServer); if (SHWNetGetConnection(pszFile, szServer, &cch) == NO_ERROR) pszServer = PathExtractServer(szServer); else pszServer = NULL; Str_SetPtr(&_pszServer, pszServer); }
TCHAR szName[MAX_PATH]; DWORD cchName = ARRAYSIZE(szName); TCHAR szDomain[MAX_COMPUTERNAME_LENGTH + 1]; DWORD cchDomain = ARRAYSIZE(szDomain); SID_NAME_USE snu; LPTSTR pszName; BOOL fFreeName = FALSE; // Do we need to LocalFree(pszName)?
if (LookupAccountSid(pszServer, psid, szName, &cchName, szDomain, &cchDomain, &snu)) { //
// If the domain is the bogus "BUILTIN" or we don't have a domain
// at all, then just use the name. Otherwise, use domain\userid.
//
if (!szDomain[0] || StrCmpC(szDomain, _szBuiltin) == 0) { pszName = szName; } else { // Borrow szServer as a scratch buffer
StringCchPrintf(szServer, ARRAYSIZE(szServer), TEXT("%s\\%s"), szDomain, szName); pszName = szServer; } err = ERROR_SUCCESS; } else { err = GetLastError();
// Couldn't map the SID to a name. Use the horrid raw version
// if available.
if (ConvertSidToStringSid(psid, &pszName)) { fFreeName = TRUE; err = ERROR_SUCCESS; } else pszName = NULL; }
// Even on error, cache the result so we don't keep trying over and over
// on the same SID.
_CacheSidName(psd, psid, pszName); pvar->bstrVal = SysAllocString(pszName);
if (fFreeName) LocalFree(pszName); } }
if (err == ERROR_SUCCESS && pvar->bstrVal == NULL) err = ERROR_OUTOFMEMORY;
return HRESULT_FROM_WIN32(err); }
STDMETHODIMP COwnerColumnProvider::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData) { HRESULT hr = S_FALSE; // return S_FALSE on failure
VariantInit(pvarData);
if (IsEqualSCID(SCID_OWNER, *pscid)) { hr = _LookupOwnerName(pscd->wszFile, pvarData); }
return hr; }
STDAPI CFileSysColumnProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv) { HRESULT hr; COwnerColumnProvider *pfcp = new COwnerColumnProvider; if (pfcp) { hr = pfcp->QueryInterface(riid, ppv); pfcp->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; } return hr; }
// FMTID_ExeDllInformation,
//// {0CEF7D53-FA64-11d1-A203-0000F81FEDEE}
#define PSFMTID_VERSION { 0xcef7d53, 0xfa64, 0x11d1, 0xa2, 0x3, 0x0, 0x0, 0xf8, 0x1f, 0xed, 0xee }
#define PIDVSI_FileDescription 0x003
#define PIDVSI_FileVersion 0x004
#define PIDVSI_InternalName 0x005
#define PIDVSI_OriginalFileName 0x006
#define PIDVSI_ProductName 0x007
#define PIDVSI_ProductVersion 0x008
// Win32 PE (exe, dll) Version Information column identifier defs...
DEFINE_SCID(SCID_FileDescription, PSFMTID_VERSION, PIDVSI_FileDescription); DEFINE_SCID(SCID_FileVersion, PSFMTID_VERSION, PIDVSI_FileVersion); DEFINE_SCID(SCID_InternalName, PSFMTID_VERSION, PIDVSI_InternalName); DEFINE_SCID(SCID_OriginalFileName, PSFMTID_VERSION, PIDVSI_OriginalFileName); DEFINE_SCID(SCID_ProductName, PSFMTID_VERSION, PIDVSI_ProductName); DEFINE_SCID(SCID_ProductVersion, PSFMTID_VERSION, PIDVSI_ProductVersion);
const COLUMN_INFO c_rgExeDllColumns[] = { DEFINE_COL_STR_ENTRY(SCID_CompanyName, 30, IDS_VN_COMPANYNAME), DEFINE_COL_STR_ENTRY(SCID_FileDescription, 30, IDS_VN_FILEDESCRIPTION), DEFINE_COL_STR_ENTRY(SCID_FileVersion, 20, IDS_VN_FILEVERSION), DEFINE_COL_STR_MENU_ENTRY(SCID_ProductName, 30, IDS_VN_PRODUCTNAME), DEFINE_COL_STR_MENU_ENTRY(SCID_ProductVersion,20, IDS_VN_PRODUCTVERSION), };
class CVersionColProvider : public CBaseColumnProvider { STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData);
private: CVersionColProvider() : CBaseColumnProvider(&CLSID_VersionColProvider, c_rgExeDllColumns, ARRAYSIZE(c_rgExeDllColumns), NULL) { _pvAllTheInfo = NULL; _szFileCache[0] = 0; };
virtual ~CVersionColProvider() { _ClearCache(); }
FARPROC _GetVerProc(LPCSTR pszName); HRESULT _CacheFileVerInfo(LPCWSTR pszFile); void _ClearCache();
WCHAR _szFileCache[MAX_PATH]; void *_pvAllTheInfo; HRESULT _hrCache;
friend HRESULT CVerColProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv); };
void CVersionColProvider::_ClearCache() { if (_pvAllTheInfo) { delete [] _pvAllTheInfo; _pvAllTheInfo = NULL; } _szFileCache[0] = 0; }
HRESULT CVersionColProvider::_CacheFileVerInfo(LPCWSTR pszFile) { if (StrCmpW(_szFileCache, pszFile)) { HRESULT hr; _ClearCache();
DWORD dwVestigial; DWORD versionISize = GetFileVersionInfoSizeW((LPWSTR)pszFile, &dwVestigial); // cast for bad API design
if (versionISize) { _pvAllTheInfo = new BYTE[versionISize]; if (_pvAllTheInfo) { // read the data
if (GetFileVersionInfoW((LPWSTR)pszFile, dwVestigial, versionISize, _pvAllTheInfo)) { hr = S_OK; } else { _ClearCache(); hr = E_FAIL; } } else hr = E_OUTOFMEMORY; // error, out of memory.
} else hr = S_FALSE;
// don't care if it is truncated - will only cause a slight perf problem
// on network shares.
StringCchCopy(_szFileCache, ARRAYSIZE(_szFileCache), pszFile); _hrCache = hr; } return _hrCache; }
STDMETHODIMP CVersionColProvider::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData) { VariantInit(pvarData);
if (pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return S_FALSE;
HRESULT hr = _CacheFileVerInfo(pscd->wszFile); if (hr != S_OK) return hr;
TCHAR szString[128], *pszVersionInfo = NULL; //A pointer to the specific version info I am looking for
LPCTSTR pszVersionField = NULL;
switch (pscid->pid) { case PIDVSI_FileVersion: { VS_FIXEDFILEINFO *pffi; UINT uInfoSize; if (VerQueryValue(_pvAllTheInfo, TEXT("\\"), (void **)&pffi, &uInfoSize)) { StringCchPrintf(szString, ARRAYSIZE(szString), TEXT("%d.%d.%d.%d"), HIWORD(pffi->dwFileVersionMS), LOWORD(pffi->dwFileVersionMS), HIWORD(pffi->dwFileVersionLS), LOWORD(pffi->dwFileVersionLS));
pszVersionInfo = szString; } else pszVersionField = TEXT("FileVersion"); } break;
case PIDDSI_COMPANY: pszVersionField = TEXT("CompanyName"); break; case PIDVSI_FileDescription: pszVersionField = TEXT("FileDescription"); break; case PIDVSI_InternalName: pszVersionField = TEXT("InternalName"); break; case PIDVSI_OriginalFileName: pszVersionField = TEXT("OriginalFileName"); break; case PIDVSI_ProductName: pszVersionField = TEXT("ProductName"); break; case PIDVSI_ProductVersion: pszVersionField = TEXT("ProductVersion"); break; default: return E_FAIL; } //look for the intended language in the examined object.
if (pszVersionInfo == NULL) { struct _VERXLATE { WORD wLanguage; WORD wCodePage; } *pxlate; /* ptr to translations data */
//this is a fallthrough set of if statements.
//on a failure, it just tries the next one, until it runs out of tries.
UINT uInfoSize; if (VerQueryValue(_pvAllTheInfo, TEXT("\\VarFileInfo\\Translation"), (void **)&pxlate, &uInfoSize)) { TCHAR szVersionKey[60]; //a string to hold all the format string for VerQueryValue
StringCchPrintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\%04X%04X\\%s"), pxlate[0].wLanguage, pxlate[0].wCodePage, pszVersionField); if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize)) { StringCchPrintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\040904B0\\%s"), pszVersionField); if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize)) { StringCchPrintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\040904E4\\%s"), pszVersionField); if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize)) { StringCchPrintf(szVersionKey, ARRAYSIZE(szVersionKey), TEXT("\\StringFileInfo\\04090000\\%s"), pszVersionField); if (!VerQueryValue(_pvAllTheInfo, szVersionKey, (void **) &pszVersionInfo, &uInfoSize)) { pszVersionInfo = NULL; } } } } } } if (pszVersionInfo) { PathRemoveBlanks(pszVersionInfo); hr = InitVariantFromStr(pvarData, pszVersionInfo); } else { hr = E_FAIL; }
return hr; }
STDAPI CVerColProvider_CreateInstance(IUnknown *punk, REFIID riid, void **ppv) { HRESULT hr; CVersionColProvider *pvcp = new CVersionColProvider; if (pvcp) { hr = pvcp->QueryInterface(riid, ppv); pvcp->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; }
return hr; }
|