|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 1999
//
// File: helpdoc.cpp
//
//--------------------------------------------------------------------------
/*
* There are two ways by which help collection is recognized dirty. First is if a snapin * is added/removed or extension is enabled/disabled, but this is only for this instance * of console file. * Second is if the modification time of console file is different from modification time * of collection. This is because an author may have added/removed a snapin without bringing * up help and saves console file. So the modification time on console file is later than * collection. Next time he/she opens console file and brings help, the help collection is * regenerated. */
// mmchelp.cpp : implmentation of the HelpTopics class
//
#include "stdafx.h"
#include "strings.h"
#include "helpdoc.h"
#include "nodemgr.h"
#include "regutil.h"
#include "scopndcb.h"
#ifdef DBG
CTraceTag tagHelpCollection (_T("Help"), _T(".COL construction")); #endif
SC ScGetBaseFileName(LPCWSTR pszFilePath, LPWSTR pszBaseName, int cchBaseName); BOOL MatchFileTimes(FILETIME& ftime1, FILETIME& ftime2);
HRESULT CHelpDoc::Initialize(HELPDOCINFO* pDocInfo) { ASSERT(pDocInfo != NULL); m_pDocInfo = pDocInfo;
return BuildFilePath(); }
HRESULT CHelpDoc::BuildFilePath() { DECLARE_SC(sc, TEXT("CHelpDoc::BuildFilePath")); USES_CONVERSION;
do // false loop
{ // Get temp directory
DWORD dwRet = GetTempPath(countof(m_szFilePath), m_szFilePath); if (dwRet == 0 || dwRet > MAX_PATH) break;
// Make sure that the temp path exists and it is a dir
dwRet = GetFileAttributes(m_szFilePath); if ( (0xFFFFFFFF == dwRet) || !(FILE_ATTRIBUTE_DIRECTORY & dwRet) ) break;
// Get base name of console file (if no name use "default")
WCHAR szBaseName[MAX_PATH]; int cchBaseName = MAX_PATH;
if (m_pDocInfo->m_pszFileName && m_pDocInfo->m_pszFileName[0]) { TCHAR szShortFileName[MAX_PATH] = {0}; if ( 0 == GetShortPathName( OLE2CT( m_pDocInfo->m_pszFileName ), szShortFileName, countof(szShortFileName)) ) { sc = StringCchCopyW(szBaseName, cchBaseName, L"default"); // Does not need to be localized
if(sc) return sc.ToHr(); } else sc = ScGetBaseFileName( T2CW(szShortFileName), szBaseName, countof(szBaseName)); if(sc) return sc.ToHr(); } else { sc = StringCchCopyW(szBaseName, cchBaseName, L"default"); // Does not need to be localized
if(sc) return sc.ToHr(); }
TCHAR* pszBaseName = OLE2T(szBaseName);
// construct help file path
sc = StringCchCat(m_szFilePath, countof(m_szFilePath), pszBaseName); if(sc) return sc.ToHr();
sc = StringCchCat(m_szFilePath, countof(m_szFilePath), _T(".col")); if(sc) return sc.ToHr();
return S_OK;
} while (0);
// clear path on failure
m_szFilePath[0] = 0;
return E_FAIL; }
bool entry_title_comp(EntryPair* pE1, EntryPair* pE2) { return pE1->second < pE2->second; }
//------------------------------------------------------------------------------
// Enumerate the snapins in the snap-in cache. Call AddSnapInToList for each one.
// Open the snap-ins registry key for AddSnapInToList to use. When all the
// snap-ins have been added, sort the resulting entries by snap-in name.
//------------------------------------------------------------------------------
HRESULT CHelpDoc::CreateSnapInList() { DECLARE_SC(sc, TEXT("CHelpDoc::CreateSnapInList"));
CSnapInsCache* pSnapInsCache = theApp.GetSnapInsCache(); ASSERT(pSnapInsCache != NULL);
m_entryMap.clear(); m_entryList.clear();
// open MMC\Snapins key
sc = ScFromWin32 ( m_keySnapIns.Open(HKEY_LOCAL_MACHINE, SNAPINS_KEY, KEY_READ) ); if (sc) return sc.ToHr();
// mark all snapins which have external references
sc = pSnapInsCache->ScMarkExternallyReferencedSnapins(); if (sc) return sc.ToHr();
// Add each snap-in and its static extensions to the list
CSnapInsCache::iterator c_it; for (c_it = pSnapInsCache->begin(); c_it != pSnapInsCache->end(); ++c_it) { const CSnapIn *pSnapin = c_it->second; if (!pSnapin) return (sc = E_UNEXPECTED).ToHr();
bool bIsExternallyReferenced = false; sc = pSnapin->ScTempState_IsExternallyReferenced( bIsExternallyReferenced ); if (sc) return sc.ToHr();
// skip if snapin is not externally referenced
if ( !bIsExternallyReferenced ) continue;
AddSnapInToList(pSnapin->GetSnapInCLSID());
// we do not need to add extensions, since they are in cache anyway
// and must be marked as externally referenced, (so will be added by the code above)
// but it is worth to assert that
#ifdef DBG
{ CExtSI* pExt = pSnapin->GetExtensionSnapIn(); while (pExt != NULL) { CSnapIn *pSnapin = pExt->GetSnapIn(); sc = ScCheckPointers( pSnapin, E_UNEXPECTED ); if (sc) { sc.TraceAndClear(); break; }
bool bExtensionExternallyReferenced = false; sc = pSnapin->ScTempState_IsExternallyReferenced( bExtensionExternallyReferenced ); if (sc) { sc.TraceAndClear(); break; }
// assert it is in the cache and is marked properly
CSnapInPtr spSnapin; ASSERT( SC(S_OK) == pSnapInsCache->ScFindSnapIn( pExt->GetCLSID(), &spSnapin ) ); ASSERT( bExtensionExternallyReferenced );
pExt = pExt->Next(); } }
#endif // DBG
}
m_keySnapIns.Close();
// our snap-in set is now up to date
pSnapInsCache->SetHelpCollectionDirty(false);
// copy items from map to list container so they can be sorted
EntryMap::iterator it; for (it = m_entryMap.begin(); it != m_entryMap.end(); it++ ) { m_entryList.push_back(&(*it)); }
sort(m_entryList.begin(), m_entryList.end(), entry_title_comp);
return sc.ToHr(); }
//-----------------------------------------------------------------
// Add an entry to the snap-in list for the specified snap-in CLSID.
// Then recursively add any dynamic-only extensions that are registered
// to extend this snap-in. This list is indexed by snap-in CLSID to
// speed up checking for duplicate snap-ins.
//-----------------------------------------------------------------
void CHelpDoc::AddSnapInToList(const CLSID& rclsid) { DECLARE_SC(sc, TEXT("CHelpDoc::AddSnapInToList"));
// check if already included
if (m_entryMap.find(rclsid) != m_entryMap.end()) return;
// open the snap-in key
OLECHAR szCLSID[40]; int iRet = StringFromGUID2(rclsid, szCLSID, countof(szCLSID)); ASSERT(iRet != 0);
USES_CONVERSION;
CRegKeyEx keyItem; LONG lStat = keyItem.Open(m_keySnapIns, OLE2T(szCLSID), KEY_READ); if (lStat != ERROR_SUCCESS) return;
// get the snap-in name
WTL::CString strName; sc = ScGetSnapinNameFromRegistry (keyItem, strName); #ifdef DBG
if (sc) { USES_CONVERSION; sc.SetSnapinName(W2T (szCLSID)); // only guid is valid ...
TraceSnapinError(_T("Failure reading \"NameString\" value from registry"), sc); sc.Clear(); } #endif // DBG
// Add to snap-in list
if (lStat == ERROR_SUCCESS) { wstring s(T2COLE(strName)); m_entryMap[rclsid] = s; }
// Get list of registered extensions
CExtensionsCache ExtCache; HRESULT hr = MMCGetExtensionsForSnapIn(rclsid, ExtCache); ASSERT(SUCCEEDED(hr)); if (hr != S_OK) return;
// Pass each dynamic extension to AddSnapInToList
// Note that the EXT_TYPE_DYNAMIC flag will be set for any extension
// that is dynamic-only for at least one nodetype. It may also be a
// static extension for another node type, so we don't check that the
// EXT_TYPE_STATIC flag is not set.
CExtensionsCacheIterator ExtIter(ExtCache); for (; ExtIter.IsEnd() == FALSE; ExtIter.Advance()) { if (ExtIter.GetValue() & CExtSI::EXT_TYPE_DYNAMIC) { CLSID clsid = ExtIter.GetKey(); AddSnapInToList(clsid); } } }
//----------------------------------------------------------------------
// Add a single file to a help collection. The file is added as a title
// and if bAddFolder is specified a folder is also added.
//----------------------------------------------------------------------
HRESULT CHelpDoc::AddFileToCollection( LPCWSTR pszTitle, LPCWSTR pszFilePath, BOOL bAddFolder ) { DECLARE_SC (sc, _T("CHelpDoc::AddFileToCollection"));
/*
* redirect the help file to the user's UI language, if necessary */ WTL::CString strFilePath = pszFilePath; LANGID langid = ENGLANGID; sc = ScRedirectHelpFile (strFilePath, langid); if (sc) return (sc.ToHr());
Trace (tagHelpCollection, _T("AddFileToCollection: %s - %s (langid=0x%04x)"), pszTitle, (LPCTSTR) strFilePath, langid);
USES_CONVERSION; pszFilePath = T2CW (strFilePath);
DWORD dwError = 0; m_spCollection->AddTitle (pszTitle, pszFilePath, pszFilePath, L"", L"", langid, FALSE, NULL, &dwError, TRUE, L""); if (dwError != 0) return ((sc = E_FAIL).ToHr());
if (bAddFolder) { // Folder ID parameter has the form "=title"
WCHAR szTitleEq[MAX_PATH+1]; szTitleEq[0] = L'='; sc = StringCchCopyW(szTitleEq+1, countof(szTitleEq) -1, pszTitle); if(sc) return sc.ToHr();
m_spCollection->AddFolder(szTitleEq, 1, &dwError, langid); if (dwError != 0) return ((sc = E_FAIL).ToHr()); }
return (sc.ToHr()); }
/*+-------------------------------------------------------------------------*
* CHelpDoc::ScRedirectHelpFile * * This method is for MUI support. On MUI systems, where the user's UI * language is not US English, we will attempt to redirect the help file to * * <dir>\mui\<langid>\<helpfile> * * <dir> Takes one of two values: If the helpfile passed in is fully * qualified, <dir> is the supplied directory. If the helpfile * passed in is unqualified, then <dir> is %SystemRoot%\Help. * <langid> The langid of the user's UI language, formatted as %04x * <helpfile> The name of the original .chm file. * * This function returns: * * S_OK if the helpfile was successfully redirected * S_FALSE if the helpfile wasn't redirected *--------------------------------------------------------------------------*/
SC CHelpDoc::ScRedirectHelpFile ( WTL::CString& strHelpFile, /* I/O:help file (maybe redirected) */ LANGID& langid) /* O:language ID of output help file */ { DECLARE_SC (sc, _T("CHelpDoc::ScRedirectHelpFile"));
typedef LANGID (WINAPI* GetUILangFunc)(void); static GetUILangFunc GetUserDefaultUILanguage_ = NULL; static GetUILangFunc GetSystemDefaultUILanguage_ = NULL; static bool fAttemptedGetProcAddress = false;
/*
* validate input */ if (strHelpFile.IsEmpty()) return (sc = E_FAIL);
/*
* assume no redirection is required */ sc = S_FALSE; langid = ENGLANGID; Trace (tagHelpCollection, _T("Checking for redirection of %s"), (LPCTSTR) strHelpFile);
/*
* GetUser/SystemDefaultUILanguage are unsupported on systems < Win2K, * so load them dynamically */ if (!fAttemptedGetProcAddress) { fAttemptedGetProcAddress = true;
HMODULE hMod = GetModuleHandle (_T("kernel32.dll"));
if (hMod) { GetUserDefaultUILanguage_ = (GetUILangFunc) GetProcAddress (hMod, "GetUserDefaultUILanguage"); GetSystemDefaultUILanguage_ = (GetUILangFunc) GetProcAddress (hMod, "GetSystemDefaultUILanguage"); } }
/*
* if we couldn't load the MUI APIs, don't redirect */ if ((GetUserDefaultUILanguage_ == NULL) || (GetSystemDefaultUILanguage_ == NULL)) { Trace (tagHelpCollection, _T("Couldn't load GetUser/SystemDefaultUILanguage, not redirecting")); return (sc); }
/*
* find out what languages the system and user are using */ const LANGID langidUser = GetUserDefaultUILanguage_(); const LANGID langidSystem = GetSystemDefaultUILanguage_();
/*
* we only redirect if we're running on MUI and MUI is always hosted on * the US English release, so if the system UI language isn't US English, * don't redirect */ if (langidSystem != ENGLANGID) { langid = langidSystem; Trace (tagHelpCollection, _T("System UI language is not US English (0x%04x), not redirecting"), langidSystem); return (sc); }
/*
* if the user's language is US English, don't redirect */ if (langidUser == ENGLANGID) { Trace (tagHelpCollection, _T("User's UI language is US English, not redirecting")); return (sc); }
/*
* the user's language is different from the default, see if we can * find a help file that matches the user's UI langugae */ ASSERT (langidUser != langidSystem); WTL::CString strName; WTL::CString strPathPrefix;
/*
* look for a path seperator to see if this is a fully qualified filename */ int iLastSep = strHelpFile.ReverseFind (_T('\\'));
/*
* if it's fully qualified, construct a MUI directory name, e.g. * * <path>\mui\<langid>\<filename> */ if (iLastSep != -1) { strName = strHelpFile.Mid (iLastSep+1); strPathPrefix = strHelpFile.Left (iLastSep); }
/*
* otherwise, it's not fully qualified, default to %SystemRoot%\Help, e.g. * * %SystemRoot%\Help\mui\<langid>\<filename> */ else { strName = strHelpFile; UINT cchBuffer = MAX_PATH; LPTSTR szBuffer = strPathPrefix.GetBuffer(cchBuffer);
if(!szBuffer) return (sc = E_FAIL);
ExpandEnvironmentStrings (_T("%SystemRoot%\\Help"), szBuffer, cchBuffer); strPathPrefix.ReleaseBuffer(); }
WTL::CString strRedirectedHelpFile; strRedirectedHelpFile.Format (_T("%s\\mui\\%04x\\%s"), (LPCTSTR) strPathPrefix, langidUser, (LPCTSTR) strName);
/*
* see if the redirected help file exists */ DWORD dwAttr = GetFileAttributes (strRedirectedHelpFile);
if ((dwAttr == 0xFFFFFFFF) || (dwAttr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_OFFLINE))) { #ifdef DBG
Trace (tagHelpCollection, _T("Attempting redirection to %s, %s"), (LPCTSTR) strRedirectedHelpFile, (dwAttr == 0xFFFFFFFF) ? _T("not found") : (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? _T("found as directory") : _T("file offline")); #endif
return (sc); }
/*
* if we get here, we've found a help file that matches the user's UI * language; return it and the UI language ID */ Trace (tagHelpCollection, _T("Help redirected to %s"), (LPCTSTR) strRedirectedHelpFile); strHelpFile = strRedirectedHelpFile; langid = langidUser;
/*
* we redirected, return S_OK */ return (sc = S_OK); }
//-------------------------------------------------------------------------------
// Delete the current help file collection. First delete it as a collection, then
// delete the file itself. It is possible that the file doesn't exist when this
// is called, so it's not a failure if it can't be deleted.
//-------------------------------------------------------------------------------
void CHelpDoc::DeleteHelpFile() { // Delete existing help file
HANDLE hFile = ::CreateFile(m_szFilePath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { ::CloseHandle(hFile);
IHHCollectionWrapperPtr spOldCollection; spOldCollection.CreateInstance(CLSID_HHCollectionWrapper);
USES_CONVERSION;
WCHAR* pszFilePath = T2OLE(m_szFilePath); DWORD dwError = spOldCollection->Open(pszFilePath); if (dwError == 0) spOldCollection->RemoveCollection(FALSE);
::DeleteFile(m_szFilePath); } }
//----------------------------------------------------------------------------
// Create a new help doc file for the current MMC console. This function
// enumerates all of the snap-in's used in the console and all their possible
// extension snap-ins. It queries each snap-in for a single help topic file and
// any linked help files. These files are added to a collection file which
// is then saved with the same base file name, creation time, and modification
// time as the console file.
//-----------------------------------------------------------------------------
HRESULT CHelpDoc::CreateHelpFile() { DECLARE_SC(sc, TEXT("CHelpDoc::CreateHelpFile")); USES_CONVERSION;
HelpCollectionEntrySet HelpFiles; DWORD dwError;
ASSERT(m_spCollection == NULL); m_spCollection.CreateInstance(CLSID_HHCollectionWrapper); ASSERT(m_spCollection != NULL);
if (m_spCollection == NULL) return E_FAIL;
HRESULT hr = CreateSnapInList(); if (hr != S_OK) return hr;
IMallocPtr spIMalloc; hr = CoGetMalloc(MEMCTX_TASK, &spIMalloc); ASSERT(hr == S_OK); if (hr != S_OK) return hr;
// Delete existing file before rebuilding it, or help files will
// be appended to the existing files
DeleteHelpFile();
// open new collection file
WCHAR* pszFilePath = T2OLE(m_szFilePath); dwError = m_spCollection->Open(pszFilePath); ASSERT(dwError == 0); if (dwError != 0) return E_FAIL;
// Have collection automatically find linked files
m_spCollection->SetFindMergedCHMS(TRUE);
AddFileToCollection(L"mmc", T2CW(SC::GetHelpFile()), TRUE);
/*
* Build a set of unique help files provided by the snap-ins */ EntryPtrList::iterator it; for (it = m_entryList.begin(); it != m_entryList.end(); ++it) { TRACE(_T("Help snap-in: %s\n"), (*it)->second.c_str());
USES_CONVERSION; HRESULT hr;
OLECHAR szHelpFilePath[MAX_PATH]; const CLSID& clsid = (*it)->first;
// Create an instance of the snap-in to query
IUnknownPtr spIUnknown; hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC, IID_IUnknown, (void**)&spIUnknown); if (FAILED(hr)) continue;
// use either ISnapinHelp or ISnapinHelp2 to get the main topic file
ISnapinHelpPtr spIHelp = spIUnknown; ISnapinHelp2Ptr spIHelp2 = spIUnknown;
if (spIHelp == NULL && spIHelp2 == NULL) continue;
LPWSTR pszHelpFile = NULL;
hr = (spIHelp2 != NULL) ? spIHelp2->GetHelpTopic(&pszHelpFile) : spIHelp->GetHelpTopic(&pszHelpFile);
if (hr == S_OK) { /*
* Put this help file in the collection entry set. The * set will prevent duplicating help file names. */ HelpFiles.insert (CHelpCollectionEntry (pszHelpFile, clsid)); spIMalloc->Free(pszHelpFile);
// if IsnapinHelp2, query for additional help files
pszHelpFile = NULL; if (spIHelp2 == NULL || spIHelp2->GetLinkedTopics(&pszHelpFile) != S_OK || pszHelpFile == NULL) continue;
// There may be multiple names separated by ';'s
// Add each as a separate title.
// Note: there is no call to AddFolder because linked files
// do not appear in the TOC.
WCHAR *pchStart = wcstok(pszHelpFile, L";"); while (pchStart != NULL) { // Must use base file name as title ID
WCHAR szTitleID[MAX_PATH];
sc = ScGetBaseFileName(pchStart, szTitleID, countof(szTitleID)); if(!sc.IsError()) { AddFileToCollection(szTitleID, pchStart, FALSE); }
// position to start of next string
pchStart = wcstok(NULL, L";"); }
spIMalloc->Free(pszHelpFile); } }
/*
* Put all of the help files provided by the snap-ins in the help collection. */ HelpCollectionEntrySet::iterator itHelpFile; for (itHelpFile = HelpFiles.begin(); itHelpFile != HelpFiles.end(); ++itHelpFile) { const CHelpCollectionEntry& file = *itHelpFile;
AddFileToCollection(file.m_strCLSID.c_str(), file.m_strHelpFile.c_str(), TRUE); }
dwError = m_spCollection->Save(); ASSERT(dwError == 0);
dwError = m_spCollection->Close(); ASSERT(dwError == 0);
// Force creation/modify times to match the console file
HANDLE hFile = ::CreateFile(m_szFilePath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ASSERT(hFile != INVALID_HANDLE_VALUE);
if (hFile != INVALID_HANDLE_VALUE) { BOOL bStat = ::SetFileTime(hFile, &m_pDocInfo->m_ftimeCreate, NULL, &m_pDocInfo->m_ftimeModify); ASSERT(bStat);
::CloseHandle(hFile);
ASSERT(IsHelpFileValid()); }
return S_OK; }
//-----------------------------------------------------------------------------
// Determine if the current help doc file is valid. A help file is valid if it
// has the base file name, creation time, and modification time as the MMC
// console doc file.
//-----------------------------------------------------------------------------
BOOL CHelpDoc::IsHelpFileValid() { // Try to open the help file
HANDLE hFile = ::CreateFile(m_szFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
// Check file creation and modification times
FILETIME ftimeCreate; FILETIME ftimeModify;
BOOL bStat = ::GetFileTime(hFile, &ftimeCreate, NULL, &ftimeModify); ASSERT(bStat);
::CloseHandle(hFile);
return MatchFileTimes(ftimeCreate,m_pDocInfo->m_ftimeCreate) && MatchFileTimes(ftimeModify,m_pDocInfo->m_ftimeModify); }
//--------------------------------------------------------------------------
// If the current help doc file is valid then update its creation and
// modification times to match the new doc info.
//--------------------------------------------------------------------------
HRESULT CHelpDoc::UpdateHelpFile(HELPDOCINFO* pNewDocInfo) { if (IsHelpFileValid()) { HANDLE hFile = ::CreateFile(m_szFilePath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return E_FAIL;
BOOL bStat = ::SetFileTime(hFile, &pNewDocInfo->m_ftimeCreate, NULL, &pNewDocInfo->m_ftimeModify); ASSERT(bStat);
::CloseHandle(hFile); }
return S_OK; }
//------------------------------------------------------------------------
// Delete the current help doc file
//------------------------------------------------------------------------
HRESULT CNodeCallback::OnDeleteHelpDoc(HELPDOCINFO* pCurDocInfo) { CHelpDoc HelpDoc;
HRESULT hr = HelpDoc.Initialize(pCurDocInfo); if (FAILED(hr)) return hr;
HelpDoc.DeleteHelpFile();
return S_OK; }
CHelpCollectionEntry::CHelpCollectionEntry(LPOLESTR pwzHelpFile, const CLSID& clsid) { if (!IsPartOfString (m_strHelpFile, pwzHelpFile)) m_strHelpFile.erase(); // see KB Q172398
m_strHelpFile = pwzHelpFile;
WCHAR szCLSID[40]; StringFromGUID2 (clsid, szCLSID, countof(szCLSID));
m_strCLSID.erase(); // see KB Q172398
m_strCLSID = szCLSID; }
// ----------------------------------------------------------------------
// CNodeCallack method implementation
// ----------------------------------------------------------------------
//------------------------------------------------------------------------
// Get the pathname of the help doc for an MMC console doc. If the current
// help doc is valid and there are no snap-in changes, return the current
// doc. Otherwise, create a new help doc and return it.
//------------------------------------------------------------------------
HRESULT CNodeCallback::OnGetHelpDoc(HELPDOCINFO* pHelpInfo, LPOLESTR* ppszHelpFile) { DECLARE_SC(sc, TEXT("CNodeCallback::OnGetHelpDoc")); CHelpDoc HelpDoc;
sc = HelpDoc.Initialize(pHelpInfo); if (sc) return sc.ToHr();
CSnapInsCache* pSnapInsCache = theApp.GetSnapInsCache(); sc = ScCheckPointers(pSnapInsCache); if(sc) return sc.ToHr();
// Rebuild file if snap-in set changed or current file is not up to date
if (pSnapInsCache->IsHelpCollectionDirty() || !HelpDoc.IsHelpFileValid()) { sc = HelpDoc.CreateHelpFile(); if(sc) return sc.ToHr(); }
// if ok, allocate and return file path string (OLESTR)
LPCTSTR szHelpDoc = HelpDoc.GetFilePath(); sc = ScCheckPointers(szHelpDoc); if(sc) return sc.ToHr();
int cchHelpFile = lstrlen(szHelpDoc) + 1; *ppszHelpFile = reinterpret_cast<LPOLESTR> (CoTaskMemAlloc(cchHelpFile * sizeof(wchar_t))); sc = ScCheckPointers(*ppszHelpFile, E_OUTOFMEMORY); if (sc) return sc.ToHr();
USES_CONVERSION; sc = StringCchCopyW(*ppszHelpFile, cchHelpFile, T2COLE(HelpDoc.GetFilePath())); if(sc) return sc.ToHr();
return sc.ToHr(); }
//+-------------------------------------------------------------------
//
// Member: CNodeCallback::DoesStandardSnapinHelpExist
//
// Synopsis: Given the selection context, see if Standard MMC style help
// exists (snapin implements ISnapinHelp[2] interface.
// If not we wantto put "Help On <Snapin> which is MMC1.0 legacy
// help mechanism.
//
// Arguments: [hNode] - [in] the node selection context.
// [bStandardHelpExists] - [out] Does standard help exists or not?
//
// Returns: HRESULT
//
//--------------------------------------------------------------------
HRESULT CNodeCallback::DoesStandardSnapinHelpExist(HNODE hNode, bool& bStandardHelpExists) { DECLARE_SC(sc, TEXT("CNodeCallback::OnHasHelpDoc")); sc = ScCheckPointers( (void*) hNode); if (sc) return sc.ToHr();
CNode *pNode = CNode::FromHandle(hNode); sc = ScCheckPointers(pNode, E_UNEXPECTED); if (sc) return sc.ToHr();
bStandardHelpExists = false;
// QI ComponentData for ISnapinHelp
CMTNode* pMTNode = pNode->GetMTNode(); sc = ScCheckPointers(pMTNode, E_UNEXPECTED); if(sc) return sc.ToHr();
CComponentData* pCD = pMTNode->GetPrimaryComponentData(); sc = ScCheckPointers(pCD, E_UNEXPECTED); if(sc) return sc.ToHr();
IComponentData *pIComponentData = pCD->GetIComponentData(); sc = ScCheckPointers(pIComponentData, E_UNEXPECTED); if (sc) return sc.ToHr();
ISnapinHelp* pIHelp = NULL; sc = pIComponentData->QueryInterface(IID_ISnapinHelp, (void**)&pIHelp);
// if no ISnapinHelp, try ISnapinHelp2
if (sc) { sc = pIComponentData->QueryInterface(IID_ISnapinHelp2, (void**)&pIHelp); if (sc) { // no ISnapinHelp2 either
sc.Clear(); // not an error.
return sc.ToHr(); } }
// make sure we got a valid pointer
sc = ScCheckPointers(pIHelp, E_UNEXPECTED); if(sc) { sc.Clear(); return sc.ToHr(); }
bStandardHelpExists = true;
pIHelp->Release();
return (sc).ToHr(); }
//-----------------------------------------------------------------------
// Update the current help doc file to match the new MMC console doc
//-----------------------------------------------------------------------
HRESULT CNodeCallback::OnUpdateHelpDoc(HELPDOCINFO* pCurDocInfo, HELPDOCINFO* pNewDocInfo) { CHelpDoc HelpDoc;
HRESULT hr = HelpDoc.Initialize(pCurDocInfo); if (FAILED(hr)) return hr;
return HelpDoc.UpdateHelpFile(pNewDocInfo); }
SC ScGetBaseFileName(LPCWSTR pszFilePath, LPWSTR pszBaseName, int cBaseName) { DECLARE_SC(sc, TEXT("GetBaseFileName"));
sc = ScCheckPointers(pszFilePath, pszBaseName); if(sc) return sc;
// Find last '\'
LPCWSTR pszTemp = wcsrchr(pszFilePath, L'\\');
// if no '\' found, find drive letter terminator':'
if (pszTemp == NULL) pszTemp = wcsrchr(pszFilePath, L':');
// if neither found, there is no path
// else skip over last char of path
if (pszTemp == NULL) pszTemp = pszFilePath; else pszTemp++;
// find last '.' (assume that extension follows)
WCHAR *pchExtn = wcsrchr(pszTemp, L'.');
// How many chars excluding extension ?
int cCnt = pchExtn ? (pchExtn - pszTemp) : wcslen(pszTemp); ASSERT(cBaseName > cCnt); if (cBaseName <= cCnt) return (sc = E_FAIL);
// Copy to output buffer
memcpy(pszBaseName, pszTemp, cCnt * sizeof(WCHAR)); pszBaseName[cCnt] = L'\0';
return sc; }
//
// Compare two file times. Two file times are a match if they
// differ by no more than 2 seconds. This difference is allowed
// because a FAT file system stores times with a 2 sec resolution.
//
inline BOOL MatchFileTimes(FILETIME& ftime1, FILETIME& ftime2) { // file system time resolution (2 sec) in 100's of nanosecs
const static LONGLONG FileTimeResolution = 20000000;
LONGLONG& ll1 = *(LONGLONG*)&ftime1; LONGLONG& ll2 = *(LONGLONG*)&ftime2;
return (abs(ll1 - ll2) <= FileTimeResolution); }
|