// Toolset.cpp - Classes to manage the tools menu. // // Copyright (c) 1998-1999 Microsoft Corporation #include #include "StdAfx.h" #include "Toolset.h" #include "DataObj.h" #include "CompData.h" #include #include "Resource.h" #include "resrc1.h" #include "msicab.h" #ifdef _UNICODE #define _tspawnl _wspawnl #else #define _tspawnl _spawnl #endif const unsigned KEY_SIZE = MAX_PATH; const unsigned CToolset::MAXIMUM_TOOLS = 256; LPCTSTR cszRoot = _T("Software\\Microsoft\\Shared Tools\\MSInfo"); LPCTSTR cszToolsetKey = _T("ToolSets"); LPCTSTR cszMSInfoRoot = _T("Software\\Microsoft\\Shared Tools\\MSInfo\\ToolSets"); LPCTSTR cszMSInfoCommandKey = _T("command"); LPCTSTR cszMSInfoParamKey = _T("param"); LPCTSTR cszMSInfoDescriptionKey = _T("description"); LPCTSTR cszSystemPolicyKey = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"); LPCTSTR cszRunKey = _T("NoRun"); LPCTSTR cszDefaultToolsetKey = _T("MSInfo"); LPCTSTR cszExplorerSubPath = _T("\\explorer.exe"); LPCTSTR cszAppletExtension = _T(".cpl"); LPCTSTR cszSnapinExtension = _T(".msc"); /* * CTool - Construct from the registry. * * History: a-jsari 11/11/97 Initial version. */ CTool::CTool(CRegKey * pKeyTool) : m_strName(_T("")), m_strPath(_T("")), m_strParam(_T("")), m_strDescription(_T("")), m_fValid(TRUE) { if (pKeyTool == NULL) return; TCHAR szBuffer[MAX_PATH]; DWORD dwSize; dwSize = MAX_PATH; if (pKeyTool->QueryValue(szBuffer, NULL, &dwSize) == ERROR_SUCCESS) m_strName = szBuffer; dwSize = MAX_PATH; if (pKeyTool->QueryValue(szBuffer, cszMSInfoCommandKey, &dwSize) == ERROR_SUCCESS) m_strPath = szBuffer; dwSize = MAX_PATH; if (pKeyTool->QueryValue(szBuffer, cszMSInfoParamKey, &dwSize) == ERROR_SUCCESS) m_strParam = szBuffer; dwSize = MAX_PATH; if (pKeyTool->QueryValue(szBuffer, cszMSInfoDescriptionKey, &dwSize) == ERROR_SUCCESS) m_strDescription = szBuffer; m_fValid = PathExists(); } /* * operator= - Assignment operator, used to assign CTools to the CToolset array. * * History: a-jsari 11/11/97 Initial version */ const CTool &CTool::operator=(const CTool &toolCopy) { if (&toolCopy != this) { m_strName = toolCopy.m_strName; m_strPath = toolCopy.m_strPath; m_strDescription = toolCopy.m_strDescription; m_fValid = toolCopy.m_fValid; } return *this; } //----------------------------------------------------------------------------- // PathExists - Return a flag specifying whether we can access the path // to our executable, excluding any parameters. //----------------------------------------------------------------------------- BOOL CTool::PathExists() const { // First, we'll look for the command, to see if that file exists. if (::_taccess((LPCTSTR)m_strPath, A_READ) != 0) return FALSE; // Also, the parameter value might contain a file (like a control // panel or an MSC snap-in file) we should check for. We need to get // a little bit kludgy here - if the param part ends with a CPL or // MSC extension, then back up in the string, so we can check for // the existence of that file. if (m_strParam.Right(4).CompareNoCase(cszAppletExtension) == 0 || m_strParam.Right(4).CompareNoCase(cszSnapinExtension) == 0) { LPCTSTR szFile = ::_tcsrchr((LPCTSTR)m_strParam, (TCHAR)' '); if (szFile) szFile++; // advance past the space else szFile = (LPCTSTR)m_strParam; if (szFile && ::_taccess(szFile, A_READ) != 0) return FALSE; } return TRUE; } //----------------------------------------------------------------------------- // RunTool - Spawn a process executing the current tool. //----------------------------------------------------------------------------- HRESULT CTool::RunTool() { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); if (PolicyPermitRun() == FALSE) { CString strMessage; strMessage.LoadString(IDS_POLICYFORBIDSRUN); return S_OK; } LPCTSTR cszPath = GetPath(); LPCTSTR cszParam = GetParam(); if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::TOOL, _T("TOOL \"%s\"\r\n"), m_strName); intptr_t hProcess; if (cszParam && *cszParam) hProcess = ::_tspawnl(_P_NOWAIT, cszPath, cszPath, cszParam, NULL); else hProcess = ::_tspawnl(_P_NOWAIT, cszPath, cszPath, NULL); if (hProcess < 0) { CString strMessage; strMessage.Format(IDS_NOPATH, cszPath); return E_UNEXPECTED; } return S_OK; } /* * PolicyPermitRun - returns a BOOLEAN value which determines whether * * History: ericflo 11/21/97 Boilerplate version * a-jsari 11/21/97 Initial edits */ BOOL CTool::PolicyPermitRun() { HKEY hKeyPolicy; BOOL bReturn = TRUE; DWORD dwSize, dwData, dwType; // Explorer\\NoRun == 1 do { if (RegOpenKeyEx (HKEY_CURRENT_USER, cszSystemPolicyKey, 0, KEY_READ, &hKeyPolicy) == ERROR_SUCCESS) { dwSize = sizeof(dwData); if (RegQueryValueEx(hKeyPolicy, cszRunKey, NULL, &dwType, (LPBYTE) &dwData, &dwSize) != ERROR_SUCCESS) break; RegCloseKey (hKeyPolicy); // I'm not sure which type this value is, so make an assumption. switch (dwType) { case REG_DWORD: if (dwData == 1) bReturn = FALSE; break; default: // We are assuming the wrong value type. ASSERT(FALSE); break; } } } while (FALSE); return bReturn; } /* * CCabTool - Construct the Cab explosion item. * * History: a-jsari 3/25/98 Initial version. */ CCabTool::CCabTool(CSystemInfoScope *pScope) :m_pScope(pScope) { TCHAR szBuffer[MAX_PATH + 1]; UINT uSize = MAX_PATH; AFX_MANAGE_STATE(::AfxGetStaticModuleState()); m_strName.LoadString(IDS_CAB_NAME); m_strDescription.LoadString(IDS_CAB_DESCRIPTION); VERIFY(GetWindowsDirectory(szBuffer, uSize)); m_strPath = szBuffer; m_strPath += cszExplorerSubPath; } /* * IsValid - Determine whether we have opened a CAB to explode. * * History: a-jsari 3/25/98 Initial version. */ BOOL CCabTool::IsValid() const { if (m_pScope == NULL || m_pScope->pSource() == NULL || m_pScope->pSource()->GetType() == CDataSource::GATHERER) return FALSE; if (!PathExists()) return FALSE; return reinterpret_cast(m_pScope->pSource())->HasCAB(); } /* * GetPath - Return the path to explorer with the CAB directory. * * History: a-jsari 3/25/98 Initial version. */ const CString &CCabTool::GetPath() { TCHAR szBuffer[MAX_PATH + 1]; UINT uSize = MAX_PATH; VERIFY(GetWindowsDirectory(szBuffer, uSize)); m_strPath = (LPTSTR)szBuffer; m_strPath += cszExplorerSubPath; return m_strPath; } const CString &CCabTool::GetParam() { if (m_strParam.IsEmpty()) ::GetCABExplodeDir(m_strParam, FALSE, CString(_T(""))); return m_strParam; } BOOL CToolset::s_fCabAdded = FALSE; /* * CToolset - Construct a toolset, reading the tools from the Registry key. * * History: a-jsari 11/6/97 Initial version */ CToolset::CToolset(CSystemInfoScope *pScope, CRegKey *pKeySet, CString *pstrName) :m_pPopup(NULL) { CRegKey keySub; if (pKeySet) { long lResult; do { if (pstrName != NULL) { // szName represents the name of a subkey to open. lResult = keySub.Open(*pKeySet, *pstrName, KEY_READ); ASSERT(lResult == ERROR_SUCCESS); if (lResult != ERROR_SUCCESS) break; } DWORD dwSize; TCHAR szBuffer[KEY_SIZE]; CRegKey keyTool; FILETIME keyTime; dwSize = sizeof(szBuffer); keySub.QueryValue(szBuffer, NULL, &dwSize); m_strName = szBuffer; for (DWORD iTool = 0 ; ; ++iTool) { //dwSize = sizeof(szBuffer); dwSize = sizeof(szBuffer) / sizeof(TCHAR); lResult = RegEnumKeyEx(keySub, iTool, szBuffer, &dwSize, NULL /* reserved */, NULL /* class */, NULL /* class size */, &keyTime); if (lResult == ERROR_NO_MORE_ITEMS) break; // Hard limit of 256 tools per set. if (iTool == MAXIMUM_TOOLS) break; ASSERT(lResult == ERROR_SUCCESS); if (lResult != ERROR_SUCCESS) break; lResult = keyTool.Open(keySub, szBuffer, KEY_QUERY_VALUE); if (lResult != ERROR_SUCCESS) break; CTool *toolNew = new CTool(&keyTool); m_Tools.SetAtGrow(iTool, toolNew); } if (!s_fCabAdded) { CTool *pTool = new CCabTool(pScope); s_fCabAdded = TRUE; m_Tools.SetAtGrow(iTool, pTool); } } while (FALSE); } } /* * ~CToolset - Delete our objects. * * History: a-jsari 11/6/97 Initial version */ CToolset::~CToolset() { delete m_pPopup; unsigned iTool = (unsigned)m_Tools.GetSize(); while (iTool--) { delete m_Tools[iTool]; } } /* * operator= - Set one toolset equal to another (for list insertion). * * History: a-jsari 11/6/97 Initial version */ const CToolset &CToolset::operator=(const CToolset &tCopy) { if (this != &tCopy) { m_strName = tCopy.m_strName; m_Tools.Copy(tCopy.m_Tools); } return *this; } /* * AddToMenu - Add an item to the specified menu; set the CommandID to allow us to find * the correct menu item. * * History: a-jsari 11/6/97 Initial version */ HRESULT CToolset::AddToMenu(unsigned long iSet, CMenu *pMenu) { m_pPopup = new CMenu; if (m_pPopup == NULL) ::AfxThrowMemoryException(); m_pPopup->CreatePopupMenu(); UINT iTool = GetToolCount(); UINT iSetCount = 0; while (iTool) { // Decrement the tool after we have set the command, so that the command ID // is equal to the set or'd with the Tool + 1. UINT wCommand = iSet | (iTool--); // Don't add a menu item for a if (m_Tools[iTool]->IsValid()) { ++iSetCount; VERIFY(m_pPopup->InsertMenu(0, MF_BYPOSITION | MF_STRING, wCommand, m_Tools[iTool]->GetName())); } else { ; // Log an error message! } } // Only add the sub-menu if there was at least one valid menu item. if (iSetCount > 0) pMenu->AppendMenu(MF_POPUP | MF_STRING, (UINT_PTR)m_pPopup->GetSafeHmenu(), m_strName); return S_OK; } /* * CToolList - Read the list of tools from the registry. * * History: a-jsari 11/6/97 Initial version. */ CToolList::CToolList(CSystemInfoScope *pScope) :m_pMainPopup(NULL) { CRegKey crkToolRoot; TCHAR szToolsetName[MAX_PATH]; CString szObject; DWORD dwSize; long lResult; FILETIME keyTime; lResult = crkToolRoot.Open(HKEY_LOCAL_MACHINE, cszMSInfoRoot, KEY_READ); if (lResult != ERROR_SUCCESS) { lResult = Register(TRUE); if (lResult != ERROR_SUCCESS) return; lResult = crkToolRoot.Open(HKEY_LOCAL_MACHINE, cszMSInfoRoot, KEY_READ); ASSERT(lResult == ERROR_SUCCESS); if (lResult != ERROR_SUCCESS) return; } for (DWORD iKey = 0 ; ; ++iKey) { //dwSize = sizeof(szToolsetName); dwSize = sizeof(szToolsetName) / sizeof(TCHAR); lResult = RegEnumKeyEx(crkToolRoot, iKey, szToolsetName, &dwSize, NULL /* reserved */, NULL /* class */, NULL /* class size */, &keyTime); if (lResult == ERROR_NO_MORE_ITEMS) break; szObject = szToolsetName; CToolset *setNew = new CToolset(pScope, &crkToolRoot, &szObject); Add(setNew); } } /* * ~CToolList - Delete our saved pop-up menu. * * History: a-jsari 11/6/97 Initial version */ CToolList::~CToolList() { delete m_pMainPopup; unsigned iToolset = (unsigned)m_InternalList.GetSize(); while (iToolset--) { delete m_InternalList[iToolset]; } } /* * Add - Add an element to the end of the internal list. * * History: a-jsari 11/11/97 Initial version */ void CToolList::Add(CToolset *toolSet) { m_InternalList.Add(toolSet); } /* * AddToMenu - Create our popup menu and add all of the sub-menus of each toolset * to it. * * History: a-jsari 11/6/97 Initial version. */ HRESULT CToolList::AddToMenu(CMenu *pMenu) { // HRESULT hResult; // CToolset eSet; unsigned int ulMask; // To identify Toolset from the list for the CommandID. unsigned cList; m_pMainPopup = new CMenu; if (m_pMainPopup == NULL) ::AfxThrowMemoryException(); m_pMainPopup->CreatePopupMenu(); cList = (unsigned)m_InternalList.GetSize(); for (ulMask = 0 ; ulMask < cList ; ++ulMask) { // ulMask << 16 is or'd into the lCommand to represent the Toolset number. HRESULT hResult = m_InternalList[ulMask]->AddToMenu(ulMask << 8, m_pMainPopup); if (FAILED(hResult)) return hResult; } pMenu->AppendMenu(MF_POPUP | MF_STRING, (UINT_PTR) m_pMainPopup->GetSafeHmenu(), _T("")); return S_OK; } //----------------------------------------------------------------------------- // Register (or unregister) the tools which show up in the MSInfo tools menu. // The tools are stored in a string resource, which we should process here // to create the registry entries. // // Under the toolset registry entry, each tool will have a key. Values under // the key will be: // // Name of the tool, to appear in menu (may be localized). // commnd Command line for tool. // param Parameter for the tool. //----------------------------------------------------------------------------- long CToolList::Register(BOOL fRegister) { long lResult; CRegKey crkToolsetRoot; // If unregistering - recursively delete the ToolSets key under // HKEY_LOCAL_MACHINE\Software\Microsoft\Shared Tools\MSInfo. if (!fRegister) { lResult = crkToolsetRoot.Open(HKEY_LOCAL_MACHINE, cszRoot); if (lResult != ERROR_SUCCESS) return lResult; return crkToolsetRoot.RecurseDeleteKey(cszToolsetKey); } AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CRegKey crkNewToolset, crkTool; CString strKeyValue; // Create ToolSets key. lResult = crkToolsetRoot.Create(HKEY_LOCAL_MACHINE, cszMSInfoRoot); if (lResult != ERROR_SUCCESS) return lResult; // Delete anything which might already be under the MSInfo subkey. lResult = crkToolsetRoot.Open(HKEY_LOCAL_MACHINE, cszMSInfoRoot); if (lResult == ERROR_SUCCESS) crkToolsetRoot.RecurseDeleteKey(cszDefaultToolsetKey); lResult = crkNewToolset.Create(crkToolsetRoot, cszDefaultToolsetKey); if (lResult != ERROR_SUCCESS) return lResult; VERIFY(strKeyValue.LoadString(IDS_MSINFOTOOLSET)); lResult = crkNewToolset.SetValue(strKeyValue); if (lResult != ERROR_SUCCESS) return lResult; // Get the windows directory and system32 directory. TCHAR szDirectory[MAX_PATH]; CString strSystemDirectory; if (::GetSystemDirectory(szDirectory, MAX_PATH)) strSystemDirectory = szDirectory; CString strWindowsDirectory; if (::GetWindowsDirectory(szDirectory, MAX_PATH)) strWindowsDirectory = szDirectory; // Load the string containing the tools to add to the registry. CString strTools; strTools.LoadString(IDS_DEFAULT_TOOLS); CString strKeyName, strName, strCommand, strParam; int iDelim; while (!strTools.IsEmpty()) { // First, get the different values we're going to add to the registry. iDelim = strTools.Find((TCHAR) '|'); if (iDelim >= 0) { strKeyName = strTools.Left(iDelim); strTools = strTools.Right(strTools.GetLength() - (iDelim + 1)); } iDelim = strTools.Find((TCHAR) '|'); if (iDelim >= 0) { strName = strTools.Left(iDelim); strTools = strTools.Right(strTools.GetLength() - (iDelim + 1)); } iDelim = strTools.Find((TCHAR) '|'); if (iDelim >= 0) { strCommand = strTools.Left(iDelim); strTools = strTools.Right(strTools.GetLength() - (iDelim + 1)); } iDelim = strTools.Find((TCHAR) '|'); if (iDelim >= 0) { strParam = strTools.Left(iDelim); strTools = strTools.Right(strTools.GetLength() - (iDelim + 1)); } // Now we need to convert the sequences in the string which contain // references to the windows directory, etc. ReplaceString(strCommand, _T("%sys32%"), strSystemDirectory); ReplaceString(strCommand, _T("%win%"), strWindowsDirectory); ReplaceString(strParam, _T("%sys32%"), strSystemDirectory); ReplaceString(strParam, _T("%win%"), strWindowsDirectory); // Finally, create the registry entries. lResult = crkTool.Create(crkNewToolset, strKeyName); if (lResult != ERROR_SUCCESS) continue; crkTool.SetValue(strName); crkTool.SetValue(strCommand, cszMSInfoCommandKey); crkTool.SetValue(strParam, cszMSInfoParamKey); } return ERROR_SUCCESS; } //----------------------------------------------------------------------------- // Look for instances of strFind in strString, and replace them with // strReplace. There's a method to do this in CString in version 6. //----------------------------------------------------------------------------- void CToolList::ReplaceString(CString & strString, const CString & strFind, const CString & strReplace) { int iStart = strString.Find(strFind); while (iStart != -1) { CString strTemp = strString.Left(iStart); strTemp += strReplace; strTemp += strString.Right(strString.GetLength() - iStart - strFind.GetLength()); strString = strTemp; iStart = strString.Find(strFind); } } /* operator[] - Index our internal list. * * History: a-jsari 11/11/97 Initial version. */ CToolset *CToolList::operator[](int iSet) const { return m_InternalList[iSet]; }