#include "stdafx.h" #include "PageStartup.h" #include "MSConfigState.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CPageStartup property page IMPLEMENT_DYNCREATE(CPageStartup, CPropertyPage) CPageStartup::CPageStartup() : CPropertyPage(CPageStartup::IDD) { //{{AFX_DATA_INIT(CPageStartup) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_fModified = FALSE; } CPageStartup::~CPageStartup() { } void CPageStartup::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); //{{AFX_DATA_MAP(CPageStartup) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CPageStartup, CPropertyPage) //{{AFX_MSG_MAP(CPageStartup) ON_WM_DESTROY() ON_NOTIFY(LVN_ITEMCHANGED, IDC_LISTSTARTUP, OnItemChangedList) ON_BN_CLICKED(IDC_BUTTONSUDISABLEALL, OnButtonDisableAll) ON_BN_CLICKED(IDC_BUTTONSUENABLEALL, OnButtonEnableAll) ON_NOTIFY(NM_SETFOCUS, IDC_LISTSTARTUP, OnSetFocusList) ON_BN_CLICKED(IDC_BUTTONSURESTORE, OnButtonRestore) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CPageStartup message handlers //----------------------------------------------------------------------------- // Load the list of startup items. //----------------------------------------------------------------------------- void CPageStartup::LoadStartupList() { m_fIgnoreListChanges = TRUE; EmptyList(FALSE); m_iNextPosition = 0; LoadStartupListLiveItems(); LoadStartupListDisabledItems(); m_fIgnoreListChanges = FALSE; } //----------------------------------------------------------------------------- // Load the list of items which are actually being started on this system. //----------------------------------------------------------------------------- void CPageStartup::LoadStartupListLiveItems() { LoadStartupListLiveItemsRunKey(); LoadStartupListLiveItemsStartup(); LoadStartupListLiveItemsWinIniKey(); } //----------------------------------------------------------------------------- // Look under the Run registry key for startup items. //----------------------------------------------------------------------------- void CPageStartup::LoadStartupListLiveItemsRunKey() { LPCTSTR szRunKey = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"); HKEY ahkey[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, NULL }; TCHAR szValueName[MAX_PATH], szValue[MAX_PATH]; DWORD dwSize; CRegKey regkey; for (int i = 0; ahkey[i] != NULL; i++) { // Try to open the Run registry key. if (ERROR_SUCCESS != regkey.Open(ahkey[i], szRunKey, KEY_READ)) continue; // Get the number of keys under the Run key and look at each one. DWORD dwValueCount; if (ERROR_SUCCESS != ::RegQueryInfoKey((HKEY)regkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwValueCount, NULL, NULL, NULL, NULL)) { regkey.Close(); continue; } for (DWORD dwKey = 0; dwKey < dwValueCount; dwKey++) { dwSize = MAX_PATH; if (ERROR_SUCCESS != ::RegEnumValue((HKEY)regkey, dwKey, szValueName, &dwSize, NULL, NULL, NULL, NULL)) continue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, szValueName, &dwSize)) continue; // We don't want to show MSConfig in the startup item list. CString strTemp(szValue); strTemp.MakeLower(); if (strTemp.Find(_T("msconfig.exe")) != -1) continue; // TBD - verify that the file exists? // To get the name of this startup item, we'll take the command and // strip off everything but the filename (without the extension). CString strName(szValue); GetCommandName(strName); // Create the startup item and insert it in the list. CStartupItemRegistry * pItem = new CStartupItemRegistry(ahkey[i], szRunKey, strName, szValueName, szValue); InsertStartupItem(pItem); } regkey.Close(); } } //----------------------------------------------------------------------------- // Look under the registry for the mapped win.ini file and check out its run // and load items. //----------------------------------------------------------------------------- void CPageStartup::LoadStartupListLiveItemsWinIniKey() { LPCTSTR aszValueNames[] = { _T("Run"), _T("Load"), NULL }; CRegKey regkey; TCHAR szValue[MAX_PATH * 4]; DWORD dwSize; LPCTSTR szKeyName = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows"); HKEY hkey = HKEY_CURRENT_USER; if (ERROR_SUCCESS != regkey.Open(hkey, szKeyName, KEY_READ)) return; for (int i = 0; aszValueNames[i] != NULL; i++) { dwSize = MAX_PATH * 4; if (ERROR_SUCCESS != regkey.QueryValue(szValue, aszValueNames[i], &dwSize)) continue; // The string we get back is a comma delimited list of programs. We need // to parse them into individual programs. CString strLine(szValue); while (!strLine.IsEmpty()) { CString strItem = strLine.SpanExcluding(_T(",")); if (!strItem.IsEmpty()) { // Create the startup item and insert it in the list. CString strCommandName(strItem); GetCommandName(strCommandName); CStartupItemRegistry * pItem = new CStartupItemRegistry(szKeyName, strCommandName, aszValueNames[i], strItem); InsertStartupItem(pItem); // Trim the item of the line. strLine = strLine.Mid(strItem.GetLength()); } strLine.TrimLeft(_T(" ,")); } } regkey.Close(); } //----------------------------------------------------------------------------- // Look in the startup folder for items. //----------------------------------------------------------------------------- void CPageStartup::LoadStartupListLiveItemsStartup() { LPCTSTR szRunKey = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); HKEY ahkey[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, NULL }; LPCTSTR aszRunValue[] = { _T("Common Startup"), _T("Startup"), NULL }; CRegKey regkey; TCHAR szStartupFolderDir[MAX_PATH]; TCHAR szStartupFileSpec[_MAX_PATH]; DWORD dwSize; for (int i = 0; ahkey[i] != NULL; i++) { // Try to open the registry key. if (ERROR_SUCCESS != regkey.Open(ahkey[i], szRunKey, KEY_READ)) continue; // Get the path for the startup item folder. dwSize = MAX_PATH; if (aszRunValue[i] == NULL || ERROR_SUCCESS != regkey.QueryValue(szStartupFolderDir, aszRunValue[i], &dwSize)) { regkey.Close(); continue; } regkey.Close(); // Append the filespec on the end of the directory. _tmakepath(szStartupFileSpec, NULL, szStartupFolderDir, _T("*.*"), NULL); // Examine all of the files in the directory. WIN32_FIND_DATA fd; HANDLE hFind = FindFirstFile(szStartupFileSpec, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { // We want to ignore the desktop.ini file which might appear in startup. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0 || _tcsicmp(fd.cFileName, _T("desktop.ini")) != 0) { // We only want to examine files which aren't directories. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { CStartupItemFolder * pItem = new CStartupItemFolder; if (pItem) { if (pItem->Create(fd, ahkey[i], szRunKey, aszRunValue[i], szStartupFolderDir)) this->InsertStartupItem(pItem); else delete pItem; } } } } while (FindNextFile(hFind, &fd)); FindClose(hFind); } } } //----------------------------------------------------------------------------- // Load the list of items which were being started on this system, but which // we've disabled. This list is maintained in the registry. //----------------------------------------------------------------------------- void CPageStartup::LoadStartupListDisabledItems() { CRegKey regkey; regkey.Attach(GetRegKey(_T("startupreg"))); if ((HKEY)regkey != NULL) { DWORD dwKeyCount, dwSize; TCHAR szKeyName[MAX_PATH]; if (ERROR_SUCCESS == ::RegQueryInfoKey((HKEY)regkey, NULL, NULL, NULL, &dwKeyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { for (DWORD dwKey = 0; dwKey < dwKeyCount; dwKey++) { dwSize = MAX_PATH; if (ERROR_SUCCESS != ::RegEnumKeyEx((HKEY)regkey, dwKey, szKeyName, &dwSize, NULL, NULL, NULL, NULL)) continue; CRegKey regkeyItem; if (ERROR_SUCCESS == regkeyItem.Open((HKEY)regkey, szKeyName, KEY_READ)) { CStartupItemRegistry * pItem = new CStartupItemRegistry; if (pItem->Create(szKeyName, (HKEY)regkeyItem)) InsertStartupItem(pItem); else delete pItem; regkeyItem.Close(); } } } regkey.Close(); } regkey.Attach(GetRegKey(_T("startupfolder"))); if ((HKEY)regkey != NULL) { DWORD dwKeyCount, dwSize; TCHAR szKeyName[MAX_PATH]; if (ERROR_SUCCESS == ::RegQueryInfoKey((HKEY)regkey, NULL, NULL, NULL, &dwKeyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { for (DWORD dwKey = 0; dwKey < dwKeyCount; dwKey++) { dwSize = MAX_PATH; if (ERROR_SUCCESS != ::RegEnumKeyEx((HKEY)regkey, dwKey, szKeyName, &dwSize, NULL, NULL, NULL, NULL)) continue; CRegKey regkeyItem; if (ERROR_SUCCESS == regkeyItem.Open((HKEY)regkey, szKeyName, KEY_READ)) { CStartupItemFolder * pItem = new CStartupItemFolder; if (pItem->Create(szKeyName, (HKEY)regkeyItem)) InsertStartupItem(pItem); else delete pItem; regkeyItem.Close(); } } } regkey.Close(); } } //----------------------------------------------------------------------------- // Take a command line and strip off everything except the command name. //----------------------------------------------------------------------------- void CPageStartup::GetCommandName(CString & strCommand) { // Strip off the path information. int iLastBackslash = strCommand.ReverseFind(_T('\\')); if (iLastBackslash != -1) strCommand = strCommand.Mid(iLastBackslash + 1); // Strip off the extension and any flags. int iDot = strCommand.Find(_T('.')); if (iDot != -1) { if (iDot != 0) strCommand = strCommand.Left(iDot); else strCommand.Empty(); } } //----------------------------------------------------------------------------- // Insert the specified item in the startup list. The caller is then not // responsible for deleting the item. //----------------------------------------------------------------------------- void CPageStartup::InsertStartupItem(CStartupItem * pItem) { ASSERT(pItem); if (pItem == NULL) return; // Get the strings to add to the list view. CString strItem, strLocation, strCommand; pItem->GetDisplayInfo(strItem, strLocation, strCommand); // Insert the item in the list view. LV_ITEM lvi; memset(&lvi, 0, sizeof(lvi)); lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iItem = m_iNextPosition; lvi.pszText = (LPTSTR)(LPCTSTR)strItem; lvi.iSubItem = 0; lvi.lParam = (LPARAM)pItem; m_iNextPosition = ListView_InsertItem(m_list.m_hWnd, &lvi); ListView_SetItemText(m_list.m_hWnd, m_iNextPosition, 1, (LPTSTR)(LPCTSTR)strCommand); ListView_SetItemText(m_list.m_hWnd, m_iNextPosition, 2, (LPTSTR)(LPCTSTR)strLocation); ListView_SetCheckState(m_list.m_hWnd, m_iNextPosition, pItem->IsLive()); m_iNextPosition++; } //----------------------------------------------------------------------------- // Remove all the items from the list view (freeing the objects pointed to // by the LPARAM). //----------------------------------------------------------------------------- void CPageStartup::EmptyList(BOOL fFreeMemoryOnly) { LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; for (int i = ListView_GetItemCount(m_list.m_hWnd) - 1; i >= 0; i--) { lvi.iItem = i; if (ListView_GetItem(m_list.m_hWnd, &lvi)) { CStartupItem * pItem = (CStartupItem *)lvi.lParam; if (pItem) delete pItem; // If we're leaving the list with these items, we better // not do a double delete. if (fFreeMemoryOnly) { lvi.lParam = 0; ListView_SetItem(m_list.m_hWnd, &lvi); } } } if (!fFreeMemoryOnly) ListView_DeleteAllItems(m_list.m_hWnd); } //----------------------------------------------------------------------------- // Set the state of all of the items in the list. //----------------------------------------------------------------------------- void CPageStartup::SetEnableForList(BOOL fEnable) { HWND hwndFocus = ::GetFocus(); LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; for (int i = ListView_GetItemCount(m_list.m_hWnd) - 1; i >= 0; i--) ListView_SetCheckState(m_list.m_hWnd, i, fEnable); ::EnableWindow(GetDlgItemHWND(IDC_BUTTONSUDISABLEALL), fEnable); if (!fEnable && hwndFocus == GetDlgItemHWND(IDC_BUTTONSUDISABLEALL)) PrevDlgCtrl(); ::EnableWindow(GetDlgItemHWND(IDC_BUTTONSUENABLEALL), !fEnable); if (fEnable && hwndFocus == GetDlgItemHWND(IDC_BUTTONSUENABLEALL)) NextDlgCtrl(); } //============================================================================ // The CStartupItemRegistry class is used to encapsulate an individual startup // stored in the registry. //============================================================================ //---------------------------------------------------------------------------- // Construct this flavor of startup item. //---------------------------------------------------------------------------- CStartupItemRegistry::CStartupItemRegistry() { m_hkey = NULL; m_fIniMapping = FALSE; } CStartupItemRegistry::CStartupItemRegistry(HKEY hkey, LPCTSTR szKey, LPCTSTR szName, LPCTSTR szValueName, LPCTSTR szValue) { m_fIniMapping = FALSE; m_fLive = TRUE; m_strItem = szName; m_strLocation = szKey; m_strCommand = szValue; // Add the HKEY to the location. if (hkey == HKEY_LOCAL_MACHINE) m_strLocation = CString(_T("HKLM\\")) + m_strLocation; else if (hkey == HKEY_CURRENT_USER) m_strLocation = CString(_T("HKCU\\")) + m_strLocation; m_hkey = hkey; m_strKey = szKey; m_strValueName = szValueName; } //---------------------------------------------------------------------------- // This constructor is specifically for items under the INI file registry // mapping. //---------------------------------------------------------------------------- CStartupItemRegistry::CStartupItemRegistry(LPCTSTR szKey, LPCTSTR szName, LPCTSTR szValueName, LPCTSTR szValue) { m_fIniMapping = TRUE; m_fLive = TRUE; m_strItem = szName; m_strLocation = szKey; m_strCommand = szValue; m_strLocation = CString(_T("HKCU\\")) + m_strLocation + CString(_T(":")) + szValueName; m_hkey = HKEY_CURRENT_USER; m_strKey = szKey; m_strValueName = szValueName; } //---------------------------------------------------------------------------- // Enable or disable the startup item in the registry. //---------------------------------------------------------------------------- BOOL CStartupItemRegistry::SetEnable(BOOL fEnable) { if (fEnable == IsLive()) return FALSE; CRegKey regkey; if (ERROR_SUCCESS != regkey.Open(m_hkey, m_strKey, KEY_ALL_ACCESS)) return FALSE; LONG lReturnCode = ERROR_SUCCESS + 1; // need to initialize it to not ERROR_SUCCESS if (m_fIniMapping == FALSE) { // Create or delete the registry key from the data stored in // this object. if (fEnable) lReturnCode = regkey.SetValue(m_strCommand, m_strValueName); else lReturnCode = regkey.DeleteValue(m_strValueName); } else { // This item is an INI file mapping item (which means there // might be more than one item on this line). TCHAR szValue[MAX_PATH * 4]; DWORD dwSize = MAX_PATH * 4; if (ERROR_SUCCESS == regkey.QueryValue(szValue, m_strValueName, &dwSize)) { CString strValue(szValue); if (fEnable) { if (!strValue.IsEmpty()) strValue += CString(_T(", ")); strValue += m_strCommand; } else { // The harder case - we need to remove the item, and possibly commas. int iCommand = strValue.Find(m_strCommand); if (iCommand != -1) { CString strNewValue; if (iCommand > 0) { strNewValue = strValue.Left(iCommand); strNewValue.TrimRight(_T(", ")); } if (strValue.GetLength() > (m_strCommand.GetLength() + iCommand)) { if (!strNewValue.IsEmpty()) strNewValue += CString(_T(", ")); CString strRemainder(strValue.Mid(iCommand + m_strCommand.GetLength())); strRemainder.TrimLeft(_T(", ")); strNewValue += strRemainder; } strValue = strNewValue; } } lReturnCode = regkey.SetValue(strValue, m_strValueName); } } regkey.Close(); if (lReturnCode != ERROR_SUCCESS) return FALSE; // Delete or create the registry representation of this item. // This representation persists the startup item when it has // been deleted. regkey.Attach(GetRegKey(_T("startupreg"))); if ((HKEY)regkey != NULL) { if (fEnable) regkey.DeleteSubKey(m_strValueName); else { regkey.SetKeyValue(m_strValueName, m_strKey, _T("key")); regkey.SetKeyValue(m_strValueName, m_strItem, _T("item")); if (m_hkey == HKEY_LOCAL_MACHINE) regkey.SetKeyValue(m_strValueName, _T("HKLM"), _T("hkey")); else regkey.SetKeyValue(m_strValueName, _T("HKCU"), _T("hkey")); regkey.SetKeyValue(m_strValueName, m_strCommand, _T("command")); regkey.SetKeyValue(m_strValueName, m_fIniMapping ? _T("1") : _T("0"), _T("inimapping")); } regkey.Close(); } m_fLive = fEnable; return TRUE; } //---------------------------------------------------------------------------- // Create the startup item from the registry representation of the item. //---------------------------------------------------------------------------- BOOL CStartupItemRegistry::Create(LPCTSTR szKeyName, HKEY hkey) { if (hkey == NULL) return FALSE; CRegKey regkey; regkey.Attach(hkey); if ((HKEY)regkey == NULL) return FALSE; // Restore all of the values from the registry. TCHAR szValue[MAX_PATH]; DWORD dwSize; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("key"), &dwSize)) { regkey.Detach(); return FALSE; } m_strKey = szValue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("command"), &dwSize)) { regkey.Detach(); return FALSE; } m_strCommand = szValue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("item"), &dwSize)) { regkey.Detach(); return FALSE; } m_strItem = szValue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("hkey"), &dwSize)) { regkey.Detach(); return FALSE; } if (_tcscmp(szValue, _T("HKLM")) == 0) m_hkey = HKEY_LOCAL_MACHINE; else m_hkey = HKEY_CURRENT_USER; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("inimapping"), &dwSize)) { regkey.Detach(); return FALSE; } if (_tcscmp(szValue, _T("0")) == 0) m_fIniMapping = FALSE; else m_fIniMapping = TRUE; regkey.Detach(); m_strLocation = m_strKey; m_strValueName = szKeyName; m_fLive = FALSE; return TRUE; } //---------------------------------------------------------------------------- // Remove all the entries persisted in the registry. //---------------------------------------------------------------------------- void CStartupItemRegistry::RemovePersistedEntries() { CRegKey regkey; regkey.Attach(GetRegKey()); if ((HKEY)regkey != NULL) regkey.RecurseDeleteKey(_T("startupreg")); } //============================================================================ // The CStartupItemFolder class is used to encapsulate an individual startup // stored in the startup folder. //============================================================================ CStartupItemFolder::CStartupItemFolder() { } //---------------------------------------------------------------------------- // Enable or disable this startup item. Since the item is an actual file in // a folder, disabling it will mean copying that file to a backup folder and // creating a registry entry for it. Enabling will mean copying the file // back to the appropriate startup folder and deleting the registry entry. //---------------------------------------------------------------------------- BOOL CStartupItemFolder::SetEnable(BOOL fEnable) { if (fEnable == IsLive()) return FALSE; // Copy the file (from or to the backup directory). if (fEnable) { m_strBackupPath = GetBackupName(m_strFilePath, m_strLocation); ::CopyFile(m_strBackupPath, m_strFilePath, FALSE); } else { BackupFile(m_strFilePath, m_strLocation, TRUE); m_strBackupPath = GetBackupName(m_strFilePath, m_strLocation); } // Update the registry for the new state. If we are making the file // disabled, then we need to save both the original path and the // backed up path (and the standard startup item stuff). Otherwise // just delete the key. CRegKey regkey; regkey.Attach(GetRegKey(_T("startupfolder"))); if ((HKEY)regkey == NULL) return FALSE; // The name for the registry key just needs to be unique. And not // have any backslashes in it. CString strRegkey(m_strFilePath); strRegkey.Replace(_T("\\"), _T("^")); if (fEnable) { regkey.DeleteSubKey(strRegkey); ::DeleteFile(m_strBackupPath); } else { regkey.SetKeyValue(strRegkey, m_strFilePath, _T("path")); regkey.SetKeyValue(strRegkey, m_strBackupPath, _T("backup")); regkey.SetKeyValue(strRegkey, m_strLocation, _T("location")); regkey.SetKeyValue(strRegkey, m_strCommand, _T("command")); regkey.SetKeyValue(strRegkey, m_strItem, _T("item")); ::DeleteFile(m_strFilePath); } m_fLive = fEnable; return TRUE; } //---------------------------------------------------------------------------- // Load the disabled startup item from the registry. //---------------------------------------------------------------------------- BOOL CStartupItemFolder::Create(LPCTSTR szKeyName, HKEY hkey) { if (hkey == NULL) return FALSE; CRegKey regkey; regkey.Attach(hkey); if ((HKEY)regkey == NULL) return FALSE; // Restore all of the values from the registry. TCHAR szValue[MAX_PATH]; DWORD dwSize; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("path"), &dwSize)) { regkey.Detach(); return FALSE; } m_strFilePath = szValue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("backup"), &dwSize)) { regkey.Detach(); return FALSE; } m_strBackupPath = szValue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("location"), &dwSize)) { regkey.Detach(); return FALSE; } m_strLocation = szValue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("command"), &dwSize)) { regkey.Detach(); return FALSE; } m_strCommand = szValue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, _T("item"), &dwSize)) { regkey.Detach(); return FALSE; } m_strItem = szValue; regkey.Detach(); m_fLive = FALSE; return TRUE; } //---------------------------------------------------------------------------- // Create the startup item from the file found in the folder. If the file // is a shortcut, get the information about the target. //---------------------------------------------------------------------------- BOOL CStartupItemFolder::Create(const WIN32_FIND_DATA & fd, HKEY hkey, LPCTSTR szRegPathToFolder, LPCTSTR szFolder, LPCTSTR szDir) { // We want to save the path to the file in the startup folder (even if // it is a shortcut). m_strFilePath = szDir; if (m_strFilePath.Right(1) != CString(_T("\\"))) m_strFilePath += CString(_T("\\")); m_strFilePath += fd.cFileName; // Look at the file to determine how to handle it. CString strFile(fd.cFileName); strFile.MakeLower(); if (strFile.Right(4) == CString(_T(".lnk"))) { // The file is a shortcut to a different command. Show that command. CIconInfo info; _tcsncpy(info.szPath, m_strFilePath, sizeof(info.szPath) / sizeof(TCHAR)); if (SUCCEEDED(GetIconInfo(info))) { m_fLive = TRUE; m_strItem = fd.cFileName; m_strLocation = szFolder; m_strCommand.Format(_T("%s %s"), info.szTarget, info.szArgs); int iDot = m_strItem.ReverseFind(_T('.')); if (iDot > 0) m_strItem = m_strItem.Left(iDot); return TRUE; } } else { // A file besides a shortcut. It may be an EXE, or some other file type // (we'll handle them the same). m_fLive = TRUE; m_strItem = fd.cFileName; m_strLocation = szFolder; m_strCommand = m_strFilePath; int iDot = m_strItem.ReverseFind(_T('.')); if (iDot > 0) m_strItem = m_strItem.Left(iDot); return TRUE; } return FALSE; } //---------------------------------------------------------------------------- // Get the information about a shortcut. Creates a thread to do so, since it // needs to be done in an apartment model thread. // // JJ's code, cleaned up a bit. //---------------------------------------------------------------------------- DWORD GetIconInfoProc(CStartupItemFolder::CIconInfo * pInfo); HRESULT CStartupItemFolder::GetIconInfo(CIconInfo & info) { DWORD dwID; HANDLE hThread; if (hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GetIconInfoProc, &info, 0, &dwID)) { info.hResult = S_OK; ::WaitForSingleObject(hThread, INFINITE); } else info.hResult = E_FAIL; return info.hResult; } //---------------------------------------------------------------------------- // Remove all the entries persisted in the registry. //---------------------------------------------------------------------------- void CStartupItemFolder::RemovePersistedEntries() { CRegKey regkey; regkey.Attach(GetRegKey()); if ((HKEY)regkey != NULL) regkey.RecurseDeleteKey(_T("startupfolder")); } //---------------------------------------------------------------------------- // Procedure (run in its own thread) to get information about a shortcut. // // JJ's code, cleaned up a bit. //---------------------------------------------------------------------------- DWORD GetIconInfoProc(CStartupItemFolder::CIconInfo * pInfo) { HRESULT hResult; IShellLink * psl = NULL; IPersistFile * ppf = NULL; try { // We have to use COINIT_APARTMENTTHREADED. if (SUCCEEDED(hResult = CoInitialize(NULL))) { // Get a pointer to the IShellLink interface. if (SUCCEEDED(hResult = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (VOID **) &psl))) { // Get a pointer to the IPersistFile interface. if (SUCCEEDED(hResult = psl->QueryInterface(IID_IPersistFile, (VOID **) &ppf))) { BSTR bstrPath; #ifdef UNICODE bstrPath = pInfo->szPath; #else WCHAR wszTemp[MAX_PATH]; if (pInfo->szPath(pInfo->szPath) < MAX_PATH) wsprintfW(wszTemp, L"%hs", pInfo->szPath); else wszTemp[0] = 0; bstrPath = wszTemp; #endif if (SUCCEEDED(hResult = ppf->Load(bstrPath, STGM_READ))) { WIN32_FIND_DATA fd; hResult = psl->GetPath(pInfo->szTarget, sizeof(pInfo->szTarget), &fd, SLGP_SHORTPATH); hResult = psl->GetArguments(pInfo->szArgs, sizeof(pInfo->szArgs)); } } } } pInfo->hResult = hResult; } catch(...) { if (psl) psl->Release(); if (ppf) ppf->Release(); CoUninitialize(); throw; } if (psl) { psl->Release(); psl = NULL; } if (ppf) { ppf->Release(); ppf = NULL; } CoUninitialize(); return 0; } BOOL CPageStartup::OnInitDialog() { CPropertyPage::OnInitDialog(); ::EnableWindow(GetDlgItemHWND(IDC_LISTSTARTUP), TRUE); // Attach to the list view and set the styles we need. m_fIgnoreListChanges = TRUE; m_list.Attach(GetDlgItemHWND(IDC_LISTSTARTUP)); ListView_SetExtendedListViewStyle(m_list.m_hWnd, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); // Add all of the necessary columns to the list view's header control. struct { UINT m_uiStringResource; int m_iPercentOfWidth; } aColumns[] = { { IDS_STARTUP_LOCATION, 50 }, { IDS_STARTUP_COMMAND, 25 }, { IDS_STARTUP_ITEM, 25 }, { 0, 0 } }; CRect rect; m_list.GetClientRect(&rect); int cxWidth = rect.Width(); LVCOLUMN lvc; lvc.mask = LVCF_TEXT | LVCF_WIDTH; CString strCaption; ::AfxSetResourceHandle(_Module.GetResourceInstance()); for (int i = 0; aColumns[i].m_uiStringResource; i++) { strCaption.LoadString(aColumns[i].m_uiStringResource); lvc.pszText = (LPTSTR)(LPCTSTR)strCaption; lvc.cx = aColumns[i].m_iPercentOfWidth * cxWidth / 100; ListView_InsertColumn(m_list.m_hWnd, 0, &lvc); } LoadStartupList(); CPageBase::TabState state = GetCurrentTabState(); ::EnableWindow(GetDlgItemHWND(IDC_BUTTONSUDISABLEALL), (state != DIAGNOSTIC)); ::EnableWindow(GetDlgItemHWND(IDC_BUTTONSUENABLEALL), (state != NORMAL)); m_stateRequested = GetAppliedTabState(); m_fInitialized = TRUE; // Show the restore disabled startup item button based on whether or not // there are items to restore. ::ShowWindow(GetDlgItemHWND(IDC_BUTTONSURESTORE), CRestoreStartup::AreItemsToRestore() ? SW_SHOW : SW_HIDE); return TRUE; // return TRUE unless you set the focus to a control } void CPageStartup::OnDestroy() { CPropertyPage::OnDestroy(); EmptyList(TRUE); } //----------------------------------------------------------------------------- // If there's a change to an item in the list, it's probably because the user // has checked or unchecked a checkbox. //----------------------------------------------------------------------------- void CPageStartup::OnItemChangedList(NMHDR * pNMHDR, LRESULT * pResult) { NM_LISTVIEW * pNMListView = (NM_LISTVIEW *)pNMHDR; if (!m_fIgnoreListChanges) { LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; lvi.iItem = pNMListView->iItem; if (ListView_GetItem(m_list.m_hWnd, &lvi)) { CStartupItem * pItem = (CStartupItem *)lvi.lParam; if (pItem) { BOOL fCurrentCheck = ListView_GetCheckState(m_list.m_hWnd, pNMListView->iItem); if (fCurrentCheck != pItem->IsLive()) SetModified(TRUE); CPageBase::TabState state = GetCurrentTabState(); ::EnableWindow(GetDlgItemHWND(IDC_BUTTONSUDISABLEALL), (state != DIAGNOSTIC)); ::EnableWindow(GetDlgItemHWND(IDC_BUTTONSUENABLEALL), (state != NORMAL)); } } } *pResult = 0; } //----------------------------------------------------------------------------- // Handle the buttons to enable or disable all the items. //----------------------------------------------------------------------------- void CPageStartup::OnButtonDisableAll() { SetEnableForList(FALSE); } void CPageStartup::OnButtonEnableAll() { SetEnableForList(TRUE); } //------------------------------------------------------------------------- // Return the current state of the tab (need to look through the list). //------------------------------------------------------------------------- CPageBase::TabState CPageStartup::GetCurrentTabState() { if (!m_fInitialized) return GetAppliedTabState(); // If there are no startup items, we can only return the // state that was last requested. if (ListView_GetItemCount(m_list.m_hWnd) == 0) return m_stateRequested; TabState stateReturn = USER; BOOL fAllEnabled = TRUE, fAllDisabled = TRUE; LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; for (int i = ListView_GetItemCount(m_list.m_hWnd) - 1; i >= 0; i--) { if (ListView_GetCheckState(m_list.m_hWnd, i)) fAllDisabled = FALSE; else fAllEnabled = FALSE; } if (fAllEnabled) stateReturn = NORMAL; else if (fAllDisabled) stateReturn = DIAGNOSTIC; return stateReturn; } //------------------------------------------------------------------------- // Traverse the list looking for items which have check boxes that don't // match the state of the items. For these items, set the state. // // Finally, the base class implementation is called to maintain the // applied tab state. //------------------------------------------------------------------------- BOOL CPageStartup::OnApply() { if (!m_fModified) return TRUE; LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; for (int i = ListView_GetItemCount(m_list.m_hWnd) - 1; i >= 0; i--) { lvi.iItem = i; if (ListView_GetItem(m_list.m_hWnd, &lvi)) { CStartupItem * pItem = (CStartupItem *)lvi.lParam; if (pItem) { BOOL fCurrentCheck = ListView_GetCheckState(m_list.m_hWnd, i); if (fCurrentCheck != pItem->IsLive()) pItem->SetEnable(fCurrentCheck); } } } CPageBase::SetAppliedState(GetCurrentTabState()); CancelToClose(); m_fMadeChange = TRUE; return TRUE; } //------------------------------------------------------------------------- // Apply the changes, remove the persisted registry entries, refill the // list. Then call the base class implementation. //------------------------------------------------------------------------- void CPageStartup::CommitChanges() { OnApply(); CStartupItemRegistry::RemovePersistedEntries(); CStartupItemFolder::RemovePersistedEntries(); LoadStartupList(); CPageBase::CommitChanges(); } //------------------------------------------------------------------------- // Set the overall state of the tab to normal or diagnostic. //------------------------------------------------------------------------- void CPageStartup::SetNormal() { SetEnableForList(TRUE); m_stateRequested = NORMAL; } void CPageStartup::SetDiagnostic() { SetEnableForList(FALSE); m_stateRequested = DIAGNOSTIC; } //------------------------------------------------------------------------- // If nothing is selected when the list gets focus, select the first item // (so the user sees where the focus is). //------------------------------------------------------------------------- void CPageStartup::OnSetFocusList(NMHDR * pNMHDR, LRESULT * pResult) { if (0 == ListView_GetSelectedCount(m_list.m_hWnd) && 0 < ListView_GetItemCount(m_list.m_hWnd)) ListView_SetItemState(m_list.m_hWnd, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); *pResult = 0; } //------------------------------------------------------------------------- // Show the dialog which allows the user to restore startup items which // were disabled during upgrade. //------------------------------------------------------------------------- void CPageStartup::OnButtonRestore() { CRestoreStartup dlg; dlg.DoModal(); ::EnableWindow(GetDlgItemHWND(IDC_BUTTONSURESTORE), CRestoreStartup::AreItemsToRestore()); } //========================================================================= // This code implements the CRestoreStartup dialog, which allows the // user to restore items disabled by an upgrade. //========================================================================= CRestoreStartup::CRestoreStartup(CWnd* pParent /*=NULL*/) : CDialog(CRestoreStartup::IDD, pParent) { //{{AFX_DATA_INIT(CRestoreStartup) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } void CRestoreStartup::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CRestoreStartup) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CRestoreStartup, CDialog) //{{AFX_MSG_MAP(CRestoreStartup) ON_WM_DESTROY() ON_NOTIFY(LVN_ITEMCHANGED, IDC_RESTORELIST, OnItemChangedRestoreList) //}}AFX_MSG_MAP END_MESSAGE_MAP() //----------------------------------------------------------------------------- // As the dialog initializes, set the format of the list view, add the // appropriate columns, and populate the list with the disabled startup items. //----------------------------------------------------------------------------- BOOL CRestoreStartup::OnInitDialog() { CDialog::OnInitDialog(); // Set the list view to have check boxes. CWnd * pWnd = GetDlgItem(IDC_RESTORELIST); if (pWnd == NULL) return FALSE; m_list.Attach(pWnd->m_hWnd); ListView_SetExtendedListViewStyle(m_list.m_hWnd, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); // Add all of the necessary columns to the list view's header control. struct { UINT m_uiStringResource; int m_iPercentOfWidth; } aColumns[] = { { IDS_STARTUP_ITEM, 65 }, { IDS_STARTUP_LOCATION, 35 }, { 0, 0 } }; CRect rect; m_list.GetClientRect(&rect); int cxWidth = rect.Width(); LVCOLUMN lvc; lvc.mask = LVCF_TEXT | LVCF_WIDTH; CString strCaption; ::AfxSetResourceHandle(_Module.GetResourceInstance()); for (int i = 0; aColumns[i].m_uiStringResource; i++) { strCaption.LoadString(aColumns[i].m_uiStringResource); lvc.pszText = (LPTSTR)(LPCTSTR)strCaption; lvc.cx = aColumns[i].m_iPercentOfWidth * cxWidth / 100; ListView_InsertColumn(m_list.m_hWnd, 0, &lvc); } // Load up the list with the disabled items. LoadDisabledItemList(); SetOKState(); return TRUE; } //----------------------------------------------------------------------------- // Load the items in the list (from the registry and startup directory). //----------------------------------------------------------------------------- BOOL CRestoreStartup::LoadDisabledItemList() { m_iNextPosition = 0; BOOL fRegistry = LoadDisabledRegistry(); BOOL fStartup = LoadDisabledStartupGroup(); return (fRegistry && fStartup); } //----------------------------------------------------------------------------- // Read the list of disabled startup items which would be restored to the // registry. This list is just stored in a different registry location. //----------------------------------------------------------------------------- BOOL CRestoreStartup::LoadDisabledRegistry() { HKEY ahkeyBases[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, NULL }; for (int i = 0; ahkeyBases[i] != NULL; i++) { // Open the key containing the disabled items. We open it KEY_WRITE | KEY_READ, // even though we are just going to read in this function. This is because // if opening for this access fails, we don't want to list the items because // the user won't be able to restore the items. CRegKey regkey; if (ERROR_SUCCESS != regkey.Open(ahkeyBases[i], DISABLED_KEY, KEY_WRITE | KEY_READ)) return FALSE; // Get the number of keys under the key and look at each one. DWORD dwValueCount, dwSize; if (ERROR_SUCCESS == ::RegQueryInfoKey((HKEY)regkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwValueCount, NULL, NULL, NULL, NULL)) { TCHAR szValueName[MAX_PATH], szValue[MAX_PATH]; for (DWORD dwKey = 0; dwKey < dwValueCount; dwKey++) { dwSize = MAX_PATH; if (ERROR_SUCCESS != ::RegEnumValue((HKEY)regkey, dwKey, szValueName, &dwSize, NULL, NULL, NULL, NULL)) continue; dwSize = MAX_PATH; if (ERROR_SUCCESS != regkey.QueryValue(szValue, szValueName, &dwSize)) continue; // Create the startup item and insert it in the list. CStartupDisabledRegistry * pItem = new CStartupDisabledRegistry(szValueName, szValue, ENABLED_KEY, ahkeyBases[i]); InsertDisabledStartupItem(pItem); } } regkey.Close(); } return TRUE; } //----------------------------------------------------------------------------- // Add the items from the disabled startup group to the list: // // GIVEN the path to CSIDL_STARTUP, setup moves disabled items to // ..\Disabled Startup and makes it hidden; it potentially contains // the complete content of the original startup folder, which // can be anything. // // Note - we'll also need to look under CSIDL_COMMON_STARTUP. //----------------------------------------------------------------------------- BOOL CRestoreStartup::LoadDisabledStartupGroup() { int anFolders[] = { CSIDL_STARTUP, CSIDL_COMMON_STARTUP, 0 }; TCHAR szPath[MAX_PATH * 2]; for (int i = 0; anFolders[i] != 0; i++) { if (FAILED(::SHGetSpecialFolderPath(NULL, szPath, anFolders[i], FALSE))) continue; // We need to trim off the last part of the path and replace it with // "Disabled Startup". CString strPath(szPath); int iLastSlash = strPath.ReverseFind(_T('\\')); if (iLastSlash == -1) continue; strPath = strPath.Left(iLastSlash) + CString(DISABLED_STARTUP); // Now look for files in the folder. CString strSearch(strPath); strSearch += CString(_T("\\*.*")); WIN32_FIND_DATA fd; HANDLE hFind = FindFirstFile(strSearch, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { // We want to ignore the desktop.ini file which might appear in startup. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0 || _tcsicmp(fd.cFileName, _T("desktop.ini")) != 0) { // We only want to examine files which aren't directories. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { CStartupDisabledStartup * pItem = new CStartupDisabledStartup(fd.cFileName, szPath, strPath); InsertDisabledStartupItem(pItem); } } } while (FindNextFile(hFind, &fd)); FindClose(hFind); } } return TRUE; } //----------------------------------------------------------------------------- // Insert the disabled item into the list view. //----------------------------------------------------------------------------- void CRestoreStartup::InsertDisabledStartupItem(CStartupDisabled * pItem) { if (pItem == NULL) return; CString strItem, strLocation; pItem->GetColumnCaptions(strItem, strLocation); // Insert the item in the list view. LV_ITEM lvi; memset(&lvi, 0, sizeof(lvi)); lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iItem = m_iNextPosition; lvi.pszText = (LPTSTR)(LPCTSTR)strLocation; lvi.iSubItem = 0; lvi.lParam = (LPARAM)pItem; m_iNextPosition = ListView_InsertItem(m_list.m_hWnd, &lvi); ListView_SetItemText(m_list.m_hWnd, m_iNextPosition, 1, (LPTSTR)(LPCTSTR)strItem); ListView_SetCheckState(m_list.m_hWnd, m_iNextPosition, TRUE); m_iNextPosition++; } //----------------------------------------------------------------------------- // Remove all the items from the list view (freeing the objects pointed to // by the LPARAM). //----------------------------------------------------------------------------- void CRestoreStartup::EmptyList() { LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; for (int i = ListView_GetItemCount(m_list.m_hWnd) - 1; i >= 0; i--) { lvi.iItem = i; if (ListView_GetItem(m_list.m_hWnd, &lvi)) { CStartupDisabled * pItem = (CStartupDisabled *)lvi.lParam; if (pItem) delete pItem; } } ListView_DeleteAllItems(m_list.m_hWnd); } //----------------------------------------------------------------------------- // When the dialog is being destroyed, be sure to free the memory of the // object pointers maintained in the list view. //----------------------------------------------------------------------------- void CRestoreStartup::OnDestroy() { EmptyList(); CDialog::OnDestroy(); } //----------------------------------------------------------------------------- // If the user clicks on OK, then we should make sure he or she really wants // to perform this operation. If so, then look though the list, calling the // Restore function for each object with the checkbox checked. //----------------------------------------------------------------------------- void CRestoreStartup::OnOK() { CString strText, strCaption; strCaption.LoadString(IDS_DIALOGCAPTION); strText.LoadString(IDS_VERIFYRESTORE); if (IDYES == ::MessageBox(m_hWnd, strText, strCaption, MB_YESNO)) { LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; for (int i = ListView_GetItemCount(m_list.m_hWnd) - 1; i >= 0; i--) if (ListView_GetCheckState(m_list.m_hWnd, i)) { lvi.iItem = i; if (ListView_GetItem(m_list.m_hWnd, &lvi)) { CStartupDisabled * pItem = (CStartupDisabled *)lvi.lParam; if (pItem != NULL) pItem->Restore(); } } } CDialog::OnOK(); } //----------------------------------------------------------------------------- // Enable or disable the OK button based on the state of the check boxes. //----------------------------------------------------------------------------- void CRestoreStartup::SetOKState() { CWnd * pWnd = GetDlgItem(IDOK); if (pWnd == NULL) return; BOOL fEnable = FALSE; for (int i = ListView_GetItemCount(m_list.m_hWnd) - 1; i >= 0; i--) if (ListView_GetCheckState(m_list.m_hWnd, i)) { fEnable = TRUE; break; } if (::IsWindowEnabled(pWnd->m_hWnd) != fEnable) ::EnableWindow(pWnd->m_hWnd, fEnable); } //----------------------------------------------------------------------------- // If the user changed something in the list, update the OK button state. //----------------------------------------------------------------------------- void CRestoreStartup::OnItemChangedRestoreList(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; SetOKState(); *pResult = 0; } //----------------------------------------------------------------------------- // This static function will be called by the startup tab to determine if the // button to launch this dialog box should be enabled. //----------------------------------------------------------------------------- BOOL CRestoreStartup::AreItemsToRestore() { // Look in the registry for disabled items. HKEY ahkeyBases[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, NULL }; for (int j = 0; ahkeyBases[j] != NULL; j++) { CRegKey regkey; if (ERROR_SUCCESS == regkey.Open(ahkeyBases[j], DISABLED_KEY, KEY_READ)) { DWORD dwValueCount; if (ERROR_SUCCESS == ::RegQueryInfoKey((HKEY)regkey, NULL, NULL, NULL, NULL, NULL, NULL, &dwValueCount, NULL, NULL, NULL, NULL)) if (dwValueCount > 0) { regkey.Close(); return TRUE; } regkey.Close(); } } // Look in the disabled startup items folders. int anFolders[] = { CSIDL_STARTUP, CSIDL_COMMON_STARTUP, 0 }; TCHAR szPath[MAX_PATH * 2]; BOOL fDisabledItem = FALSE; for (int i = 0; !fDisabledItem && anFolders[i] != 0; i++) { if (FAILED(::SHGetSpecialFolderPath(NULL, szPath, anFolders[i], FALSE))) continue; // We need to trim off the last part of the path and replace it with // "Disabled Startup". CString strPath(szPath); int iLastSlash = strPath.ReverseFind(_T('\\')); if (iLastSlash == -1) continue; strPath = strPath.Left(iLastSlash) + CString(DISABLED_STARTUP); // Now look for files in the folder. CString strSearch(strPath); strSearch += CString(_T("\\*.*")); WIN32_FIND_DATA fd; HANDLE hFind = FindFirstFile(strSearch, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { // We want to ignore the desktop.ini file which might appear in startup. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0 || _tcsicmp(fd.cFileName, _T("desktop.ini")) != 0) { // We only want to examine files which aren't directories. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { fDisabledItem = TRUE; break; } } } while (FindNextFile(hFind, &fd)); FindClose(hFind); } } return fDisabledItem; }