windows-nt/Source/XPSP1/NT/com/ole32/stg/props/tnupdr.cxx
2020-09-26 16:20:57 +08:00

933 lines
24 KiB
C++

//+------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1998.
//
// File: tnupdr.cxx
//
//+------------------------------------------------------------------
#include "pch.cxx"
#include <filtntfy.h>
#include "tnupdr.hxx"
EXTERN_C const IID IID_IFlatStorage; /* b29d6138-b92f-11d1-83ee-00c04fc2c6d4 */
DECLARE_INFOLEVEL(updr)
#define DEB_INFO DEB_USER1
#define DEB_REGINFO DEB_USER2
#define MIME_TYPES_ROOT L"software\\classes\\Mime\\Database\\Content Type"
#define IMAGE_PREFIX L"image/"
#define IMAGE_PREFIX_LEN 6
extern "C" CLSID CLSID_ThumbnailFCNHandler;
extern "C" CLSID CLSID_ThumbnailUpdater;
class CThumbnailCF : public IClassFactory
{
public:
// Constructor
CThumbnailCF(): _cRefs(1) { }
// IUnknown methods
STDMETHOD (QueryInterface) (REFIID riid, void **ppv);
STDMETHOD_(ULONG,AddRef) (void);
STDMETHOD_(ULONG,Release) (void);
// IClassFactory methods
STDMETHOD (CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppv);
STDMETHOD (LockServer) (BOOL fLock) { return S_OK; }
private:
ULONG _cRefs; // CF reference count
};
//////////////////////////////////////////////////////////////
// Funtion to create a Class Factory Object.
// This is in form DoATClassCreate() wants.
//////////////////////////////////////////////////////////////
HRESULT GetCThumbnailCF(REFCLSID clsid, REFIID iid, void **ppv)
{
HRESULT hr=S_OK;
CThumbnailCF *pCF=NULL;
*ppv = 0;
if( ! IsEqualCLSID(clsid, CLSID_ThumbnailUpdater))
return CLASS_E_CLASSNOTAVAILABLE;
pCF = new CThumbnailCF();
if( NULL == pCF )
return E_OUTOFMEMORY;
hr = pCF->QueryInterface(iid, ppv);
pCF->Release();
return hr;
}
//////////////////////////////////////////////////////////////
// Hook from the DllgetClassObject routine.
// This is special because it must start in an Apartment.
//////////////////////////////////////////////////////////////
HRESULT CThumbnail_ApartmentDllGetClassObject(
REFCLSID clsid,
REFIID iid,
void **ppv)
{
HRESULT hr;
updrDebug((DEB_ITRACE,
"ApartmentDllGetClassObject(%I,%I,%x)\n",
&clsid, &iid, ppv));
if(IsEqualCLSID(clsid, CLSID_ThumbnailUpdater))
{
COleTls tls;
if ( (tls->dwFlags & OLETLS_INNEUTRALAPT)
|| !(tls->dwFlags & OLETLS_APARTMENTTHREADED))
{
//We need to switch to a single-threaded apartment.
hr = DoATClassCreate(GetCThumbnailCF, clsid, iid, (IUnknown **)ppv);
}
else
{
//This thread is in a single-threaded apartment.
hr = GetCThumbnailCF(clsid, iid, ppv);
}
}
else
{
hr = CLASS_E_CLASSNOTAVAILABLE;
}
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CThumbnailCF::AddRef, public
//
// Synopsis: Adds a reference to an interface
//
//--------------------------------------------------------------------
STDMETHODIMP_(ULONG) CThumbnailCF::AddRef(void)
{
return InterlockedIncrement((LONG *)&_cRefs);
}
//+-------------------------------------------------------------------
//
// Member: CThumbnailCF::Release, public
//
// Synopsis: Adds a reference to an interface
//
//--------------------------------------------------------------------
STDMETHODIMP_(ULONG) CThumbnailCF::Release(void)
{
ULONG cRefs = (ULONG) InterlockedDecrement((LONG *)&_cRefs);
if (cRefs == 0)
{
delete this;
}
return cRefs;
}
//+-------------------------------------------------------------------
//
// Member: CThumbnailCF::QueryInterface, public
//
// Synopsis: Returns a pointer to the requested interface.
//
//--------------------------------------------------------------------
STDMETHODIMP CThumbnailCF::QueryInterface(REFIID riid, void **ppv)
{
if (IsEqualIID(riid, IID_IClassFactory) ||
IsEqualIID(riid, IID_IUnknown))
{
*ppv = (IClassFactory *) this;
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
//////////////////////////////////////////////////////////////
// OLE32 Internal ClassFactory Entry Point
//////////////////////////////////////////////////////////////
HRESULT CThumbnailCF_CreateInstance(
IUnknown *pUnkOuter,
REFIID riid,
void** ppv)
{
HRESULT hr;
CTNUpdater * pact;
updrDebug(( DEB_IWARN, "Thumbnailer: CreateInstance called\n" ));
if(NULL != pUnkOuter)
{
return CLASS_E_NOAGGREGATION;
}
pact = new CTNUpdater;
if(NULL == pact)
{
return E_OUTOFMEMORY;
}
if(!pact->ObjectInit())
{
pact->Release();
return E_OUTOFMEMORY;
}
hr = pact->QueryInterface(riid, ppv);
pact->Release();
return hr;
}
//+-------------------------------------------------------------------
// Member: CThumbnailCF::CreateInstance, public
//--------------------------------------------------------------------
STDMETHODIMP CThumbnailCF::CreateInstance(IUnknown *pUnkOuter,
REFIID riid,
void **ppv)
{
return CThumbnailCF_CreateInstance( pUnkOuter, riid, ppv );
}
//////////////////////////////////////////////////////////////
// CTNUpdater Class
//////////////////////////////////////////////////////////////
//---------------------------------------------------------
// C++ Methods: Constructor
//---------------------------------------------------------
CTNUpdater::CTNUpdater(void)
{
m_cRef = 1;
m_pITE = NULL;
m_hEvRegNotify = NULL;
m_hkeyMime = NULL;
m_ppwszExtensions = NULL;
m_pTempList = NULL;
}
//---------------------------------------------------------
// C++ Methods: Destructor
//---------------------------------------------------------
CTNUpdater::~CTNUpdater(void)
{
if( NULL != m_pTempList )
FreeTempExtensionList();
if( NULL != m_pITE )
m_pITE->Release();
if( NULL != m_hkeyMime )
CloseHandle( m_hkeyMime );
if( NULL != m_hEvRegNotify )
CloseHandle( m_hEvRegNotify );
if( NULL != m_ppwszExtensions )
CoTaskMemFree( m_ppwszExtensions );
}
//---------------------------------------------------------
// non-interface Init routine
//---------------------------------------------------------
BOOL
CTNUpdater::ObjectInit()
{
HRESULT sc;
DWORD status;
//
// Create the event for waiting on registry changes.
//
status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
MIME_TYPES_ROOT,
0,
KEY_ALL_ACCESS,
&m_hkeyMime );
if( ERROR_SUCCESS != status )
updrErr( EH_Err, LAST_SCODE );
m_hEvRegNotify = CreateEvent( NULL,
TRUE, // Manual Reset
FALSE, // Init signaled
NULL );
if( NULL == m_hEvRegNotify )
updrErr( EH_Err, LAST_SCODE );
updrChk( GetImageFileExtensions() );
return TRUE;
EH_Err:
return FALSE;
}
//---------------------------------------------------------
// IUnknown::AddRef
//---------------------------------------------------------
ULONG
CTNUpdater::AddRef()
{
return InterlockedIncrement((LONG*)&m_cRef);
}
//---------------------------------------------------------
// IUnknown::Release
//---------------------------------------------------------
ULONG
CTNUpdater::Release()
{
LONG cRef;
cRef = InterlockedDecrement((LONG*)&m_cRef);
// ASSERT(cRef >= 0);
if(cRef <= 0)
{
delete this;
}
return cRef;
}
//---------------------------------------------------------
// IUnknown::QueryInterface
//---------------------------------------------------------
HRESULT
CTNUpdater::QueryInterface(
REFIID iid,
void **ppv)
{
HRESULT hr=S_OK;
IUnknown *pUnk=NULL;
if(IsEqualIID(IID_IUnknown, iid))
{
pUnk = (IFilterStatus*)this;
}
else if(IsEqualIID(IID_IFilterStatus, iid))
{
pUnk = (IFilterStatus*)this;
}
else if(IsEqualIID(IID_IOplockStorage, iid))
{
pUnk = (IOplockStorage*)this;
}
else
{
return E_NOINTERFACE;
}
pUnk->AddRef();
*ppv = pUnk;
return S_OK;
}
//---------------------------------------------------------
// IFilterStatus::Initialize
//---------------------------------------------------------
HRESULT
CTNUpdater::Initialize(
const WCHAR *pwszCatalogName,
const WCHAR *pwszCatalogPath)
{
return S_OK;
}
//---------------------------------------------------------
// IFilterStatus::FilterLoad
//---------------------------------------------------------
HRESULT
CTNUpdater::FilterLoad(
const WCHAR *pwszPath,
SCODE scFilterStatus)
{
return S_OK;
}
/*
//---------------------------------------------
// Test routine
//----------------------------------------
HRESULT
test_junk(IStorage *pstg)
{
HRESULT sc=S_OK;
IPropertySetStorage *ppss = NULL;
IPropertyStorage *pps = NULL;
PROPSPEC propSpec[1];
PROPVARIANT propVar[1];
updrChk( pstg->QueryInterface( IID_IPropertySetStorage, (void**)&ppss ) );
updrChk( ppss->Open( FMTID_SummaryInformation,
STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
&pps ) );
propSpec[0].ulKind = PRSPEC_PROPID;
propSpec[0].propid = PIDSI_THUMBNAIL;
updrChk( pps->ReadMultiple(1, propSpec, propVar) );
EH_Err:
// FreePropVariantArray(1, propVar);
if( NULL != ppss )
ppss->Release();
if( NULL != pps )
pps->Release();
return sc;
} */
//---------------------------------------------------------
// IFilterStatus::PreFilter
//
// This routine ALWAYS SUCCEEDS, unless we want to defer processing
// to a later time.
// If it returns any failure, CI will defer filtering the file
// and will submit it again later.
//---------------------------------------------------------
HRESULT
CTNUpdater::PreFilter(
WCHAR const * pwszPath)
{
HRESULT sc=S_OK;
IStorage *pstg=NULL;
IThumbnailExtractor *pITE=NULL;
ITimeAndNoticeControl *pITNC=NULL;
ULONG cStorageFinalReferences=0;
updrDebug(( DEB_INFO | DEB_TRACE,
"PreFilter(\"%ws\")\n", pwszPath ));
// Optimize, don't spend time on files that we don't expect to be
// Images. If it does not have a valid extension then we are done.
//
if( ! HasAnImageFileExtension(pwszPath) )
return S_FALSE;
// Open an Oplock'ed Storage. (w/ SuppressChanges turned on)
//
updrChk( OpenStorageEx(pwszPath,
STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
STGFMT_FILE,
0,
IID_IFlatStorage,
(void**)&pstg) );
if( NULL == m_pITE )
{
updrChk( CoCreateInstance(CLSID_ThumbnailFCNHandler,
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
IID_IThumbnailExtractor,
(void**)&pITE) );
m_pITE = pITE;
pITE = NULL;
}
updrChk( m_pITE->OnFileUpdated(pstg) );
EH_Err:
if(NULL != pITNC)
pITNC->Release();
if(NULL != pITE)
pITE->Release();
if(NULL != pstg)
cStorageFinalReferences = pstg->Release();
updrAssert( 0 == cStorageFinalReferences );
updrDebug(( DEB_TRACE, "PreFilter() returned %x\n", sc ));
switch( sc )
{
case STG_E_SHAREVIOLATION: // Someone else has it open when we started.
case STG_E_LOCKVIOLATION:
case STG_E_REVERTED: // Someone else opened if after we started.
break;
// All other problems are likely permanent so don't error.
// all errors tell CI to re-try the file.
default:
if( FAILED( sc ) )
sc = S_FALSE;
else
sc = S_OK;
break;
}
return sc;
}
//---------------------------------------------------------
// IFilterStatus::PostFilter
//---------------------------------------------------------
HRESULT
CTNUpdater::PostFilter(
WCHAR const * pwszPath,
HRESULT hrStatus)
{
return S_OK;
}
//---------------------------------------------------------
// IOplockStorage::CreateStorageEx
//---------------------------------------------------------
HRESULT
CTNUpdater::CreateStorageEx(
LPCWSTR pwcsName,
DWORD grfMode,
DWORD stgfmt,
DWORD grfAttrs,
REFIID riid,
void ** ppstgOpen)
{
return STG_E_INVALIDPARAMETER;
}
//---------------------------------------------------------
// IOplockStorage::OpenStorageEx
//---------------------------------------------------------
HRESULT
CTNUpdater::OpenStorageEx(
LPCWSTR pwcsName,
DWORD grfMode,
DWORD stgfmt,
DWORD grfAttrs,
REFIID riid,
void ** ppstgOpen)
{
HRESULT sc=S_OK;
IStorage *pstg=NULL;
if(0 != (grfMode & STGM_OPLOCKS_DONT_WORK))
return STG_E_INVALIDFLAG;
if( STGFMT_NATIVE == stgfmt
|| STGFMT_DOCFILE == stgfmt
|| STGFMT_STORAGE == stgfmt)
{
updrErr(EH_Err, STG_E_INVALIDPARAMETER);
}
if( STGFMT_ANY == stgfmt )
updrErr(EH_Err, STG_E_INVALIDPARAMETER);
if( STGFMT_FILE == stgfmt )
{
updrChk( NFFOpen( pwcsName, // PathName
grfMode, // Storage Mode
// Special NFF flags
NFFOPEN_OPLOCK | NFFOPEN_SUPPRESS_CHANGES,
FALSE, // Create API?
riid, // Interface ID
(void**)&pstg ) ); // [out] interface pointer
}
*ppstgOpen = pstg;
pstg = NULL;
sc = S_OK;
EH_Err:
RELEASE_INTERFACE( pstg );
return(sc);
}
//---------------------------------------------------------
// HasAnImageFileExtention, private method.
//---------------------------------------------------------
BOOL
CTNUpdater::HasAnImageFileExtension(
WCHAR const * pwszPath)
{
LPCWSTR pwszDot;
LPWSTR* ppwszExt;
HRESULT sc=S_OK;
if(NULL == pwszPath)
return FALSE;
//
// Scan forward through the string looking for the last dot that
// was not followed by a WHACK or a SPACE.
//
for (pwszDot = NULL; *pwszPath; pwszPath++)
{
switch (*pwszPath) {
case L'.':
pwszDot = pwszPath; // remember the last dot
break;
case L'\\':
case L' ': // extensions can't have spaces
pwszDot = NULL; // forget last dot, it was in a directory
break;
}
}
// If there is no extension fail.
//
if(NULL == pwszDot)
return FALSE;
// If the registry change event is signaled then read a new list.
//
if( WAIT_OBJECT_0 == WaitForSingleObject( m_hEvRegNotify, 0 ) )
updrChk( GetImageFileExtensions() );
for( ppwszExt=m_ppwszExtensions; NULL!=*ppwszExt; ++ppwszExt)
{
// Do a case insensitive compare for a known extension.
if( 0 == _wcsicmp( pwszDot, *ppwszExt ) )
return TRUE;
}
EH_Err:
return FALSE;
}
HRESULT
CTNUpdater::GetImageFileExtensions()
{
DWORD idx, ckeys;
LPWSTR pwszKeyName = NULL;
LPWSTR pwszExtension;
ULONG ccName, ccMaxNameLength;
DWORD status;
HRESULT sc;
FILETIME ftLastTime;
int cszTotalStrs=0; // Count of Extension Strings
int ccTotalChars=0; // Count of total Extension characters w/ NULLs
int cbTotalSize=0; // Total memory to allocate.
TEMPEXTLIST* pExt=NULL; // Src Pointer to the current Extension.
LPWSTR* ppwszPtr=NULL; // Des Pointer into string pointer table.
WCHAR* pwszBuf=NULL; // Des Pointer into character buffer table.
LPWSTR* ppwszTable=NULL;
if( NULL != m_ppwszExtensions )
{
CoTaskMemFree( m_ppwszExtensions );
m_ppwszExtensions = NULL;
}
//
// Ask for notification if this part of the registry changes.
// Set the notify before reading, that way if a change is made while
// we are reading and we miss it, we will pick it up on the next round.
//
status = RegNotifyChangeKeyValue( m_hkeyMime,
TRUE, // watch subtree
REG_NOTIFY_CHANGE_LAST_SET
|REG_NOTIFY_CHANGE_NAME,
m_hEvRegNotify,
TRUE );
if( ERROR_SUCCESS != status )
{
updrDebug(( DEB_ERROR,
"Can't set RegNotifyChangeKeyValue() %x\n",
GetLastError() ));
}
//
// Get the size of the maximum entry.
// Then allocate the buffer for the key name.
//
status = RegQueryInfoKey( m_hkeyMime,
NULL, NULL, NULL,
&ckeys,
&ccMaxNameLength,
NULL, NULL, NULL,
NULL, NULL, NULL );
if(ERROR_SUCCESS != status)
updrErr( EH_Err, LAST_SCODE );
ccMaxNameLength += 1; // Add one for the NULL
updrMem( pwszKeyName = new WCHAR[ ccMaxNameLength * sizeof(WCHAR)] );
//
// Enumerate through all the format types, looking for
// the image types. (they start with "image/")
//
for(idx=0; idx<ckeys; idx++)
{
ccName = ccMaxNameLength;
status = RegEnumKeyEx( m_hkeyMime,
idx,
pwszKeyName,
&ccName,
0,
NULL,
NULL,
&ftLastTime );
if(ERROR_SUCCESS != status)
{
updrDebug(( DEB_REGINFO,
"Enum of Mime Image types failed %x)\n",
status));
continue;
}
//
// If it is an image format then get the value of the
// "Extension" subkey (if any).
//
if( 0 == _wcsnicmp( pwszKeyName, IMAGE_PREFIX, IMAGE_PREFIX_LEN ) )
{
//
// The extension string is allocated by "GetAFileNameExtension()"
// and given away to "AddToTempExtensionList()".
//
sc = GetAFileNameExtension( pwszKeyName,
&pwszExtension );
if(!FAILED(sc))
{
AddToTempExtensionList( pwszExtension );
pwszExtension = NULL;
}
}
}
//
// Build a table of extensions from the temporary linked list.
//
// Count the size.
//
for(pExt=m_pTempList; pExt!=NULL; pExt=pExt->pNext)
{
cszTotalStrs += 1;
ccTotalChars += wcslen(pExt->pwszExtension) + 1;
}
// Allocate the memory
//
cbTotalSize = (cszTotalStrs+1) * sizeof (WCHAR*);
cbTotalSize += ccTotalChars * sizeof(WCHAR*);
ppwszTable = (LPWSTR*) CoTaskMemAlloc( cbTotalSize );
// Set the pointers to the start of the pointer table and
// the start of the character buffer space.
//
ppwszPtr = ppwszTable;
pwszBuf = (WCHAR*) (ppwszTable + cszTotalStrs+1);
for(pExt=m_pTempList; pExt!=NULL; pExt=pExt->pNext)
{
// Copy the string into the buffer space.
//
wcscpy(pwszBuf, pExt->pwszExtension);
// Record the enrty to point at it.
//
*ppwszPtr = pwszBuf;
// Advance the pointers.
//
ppwszPtr += 1; // Advance one string pointer.
pwszBuf += wcslen(pwszBuf) +1; // Advance one string.
}
// Place a NULL in the last entry, to terminate the list.
//
*ppwszPtr = NULL;
FreeTempExtensionList();
// Success. Copy results out.
//
m_ppwszExtensions = ppwszTable;
ppwszTable = NULL;
EH_Err:
if( NULL != ppwszTable )
CoTaskMemFree( ppwszTable );
if( NULL != pwszKeyName )
delete[] pwszKeyName;
return S_OK;
}
//
// Search the existing list for an identical extension. If you don't find
// one, then push a new one onto the front of the list.
// The string is given to us. If we don't want it, then delete it.
//
HRESULT
CTNUpdater::AddToTempExtensionList(
WCHAR* pwszExtension)
{
TEMPEXTLIST* pExt;
for(pExt=m_pTempList; pExt!=NULL; pExt=pExt->pNext)
{
if( 0 == _wcsicmp(pwszExtension, pExt->pwszExtension) )
break;
}
if(NULL == pExt)
{ // If we didn't find it, save it on the list.
pExt = new TEMPEXTLIST;
pExt->pwszExtension = pwszExtension;
pExt->pNext = m_pTempList;
m_pTempList = pExt;
}
else
{ // If we already have one, then delete this string.
CoTaskMemFree(pwszExtension);
}
return S_OK;
}
HRESULT
CTNUpdater::FreeTempExtensionList()
{
TEMPEXTLIST* pExt;
TEMPEXTLIST* pExtNext;
pExt=m_pTempList;
while(NULL != pExt)
{
pExtNext = pExt->pNext;
CoTaskMemFree( pExt->pwszExtension );
delete pExt;
pExt = pExtNext;
}
m_pTempList = NULL;
return S_OK;
}
HRESULT
CTNUpdater::GetAFileNameExtension(
WCHAR* pwszKeyName,
WCHAR** ppwszExtension)
{
DWORD status, dwType;
HKEY hkeyImageFormat=NULL;
DWORD cbExtBuf=0;
HRESULT sc=S_OK;
WCHAR *pwszExt=NULL;
status = RegOpenKeyEx( m_hkeyMime,
pwszKeyName,
0,
KEY_ALL_ACCESS,
&hkeyImageFormat );
if(ERROR_SUCCESS != status)
{
updrDebug(( DEB_ERROR,
"Error, could not open image format key '%ws' err=%d\n",
pwszKeyName, status ));
updrErr( EH_Err, LAST_SCODE );
}
// Get the Size of the extension string.
//
status = RegQueryValueEx( hkeyImageFormat,
L"Extension",
0,
&dwType,
NULL,
&cbExtBuf );
if(ERROR_SUCCESS != status)
{
if(ERROR_FILE_NOT_FOUND != status)
{
updrDebug(( DEB_REGINFO,
"Error, reading Extension for '%ws' err=%d\n",
pwszKeyName, status ));
}
sc = E_FAIL;
goto EH_Err;
}
if(REG_SZ != dwType)
{
updrDebug(( DEB_REGINFO,
"Error, Extension for '%ws' not REG_SZ type.\n",
pwszKeyName ));
sc = E_FAIL;
goto EH_Err;
}
// Allocate the buffer and read the extension string.
//
updrMem( pwszExt = (WCHAR*)CoTaskMemAlloc(cbExtBuf) );
status = RegQueryValueEx( hkeyImageFormat,
L"Extension",
0,
&dwType,
(BYTE*)pwszExt,
&cbExtBuf );
if(ERROR_SUCCESS != status)
updrErr( EH_Err, LAST_SCODE );
*ppwszExtension = pwszExt;
pwszExt = NULL;
EH_Err:
if(NULL != pwszExt)
CoTaskMemFree(pwszExt);
if( NULL != hkeyImageFormat )
RegCloseKey(hkeyImageFormat);
return sc;
}