#include "shellprv.h" #pragma hdrstop #include "control.h" HDSA g_hacplmLoaded = NULL; void ConvertCplInfo(void * lpv) { NEWCPLINFOA CplInfoA; LPNEWCPLINFOW lpCplInfoW = (LPNEWCPLINFOW)lpv; memcpy(&CplInfoA, lpv, sizeof(CplInfoA)); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CplInfoA.szName, ARRAYSIZE(CplInfoA.szName), lpCplInfoW->szName, ARRAYSIZE(lpCplInfoW->szName)); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CplInfoA.szInfo, ARRAYSIZE(CplInfoA.szInfo), lpCplInfoW->szInfo, ARRAYSIZE(lpCplInfoW->szInfo)); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CplInfoA.szHelpFile, ARRAYSIZE(CplInfoA.szHelpFile), lpCplInfoW->szHelpFile, ARRAYSIZE(lpCplInfoW->szHelpFile)); lpCplInfoW->dwSize = sizeof(NEWCPLINFOW); } // See if pszShort is a truncated version of the string referred to by // hinst/id. If so, then use the long string. This is to work around // a "bad design feature" of CPL_NEWINQUIRE where the app returns a buffer // (which is only 32 or 64 chars long) rather than a resource id // like CPL_INQUIRE. So if the app responds to both messages, and the // NEWINQUIRE string is a truncated version of the INQUIRE string, then // switch to the INQUIRE string. LPTSTR _RestoreTruncatedCplString( HINSTANCE hinst, int id, LPTSTR pszShort, LPTSTR pszBuf, int cchBufMax) { int cchLenShort, cchLen; cchLenShort = lstrlen(pszShort); cchLen = LoadString(hinst, id, pszBuf, cchBufMax); // Don't use SHTruncateString since KERNEL32 doesn't either if (StrCmpNC(pszShort, pszBuf, cchLenShort) == 0) { pszShort = pszBuf; } return pszShort; } // // Initializes *pcpli. // // Requires: // *pcpli is filled with 0 & NULL's. // BOOL _InitializeControl(LPCPLMODULE pcplm, LPCPLITEM pcpli) { BOOL fSucceed = TRUE; union { NEWCPLINFO Native; NEWCPLINFOA NewCplInfoA; NEWCPLINFOW NewCplInfoW; } Newcpl; CPLINFO cpl; TCHAR szName[MAX_CCH_CPLNAME]; TCHAR szInfo[MAX_CCH_CPLINFO]; LPTSTR pszName = Newcpl.Native.szName, pszInfo = Newcpl.Native.szInfo; HICON hIconTemp = NULL; // // always do the old method to get the icon ID // cpl.idIcon = 0; CPL_CallEntry(pcplm, NULL, CPL_INQUIRE, (LONG)pcpli->idControl, (LONG_PTR)(LPCPLINFO)&cpl); // // if this is a 32bit CPL and it gave us an ID then validate it // this fixes ODBC32 which gives back a bogus ID but a correct HICON // note that the next load of the same icon should be very fast // if (cpl.idIcon) { hIconTemp = LoadImage(pcplm->minst.hinst, MAKEINTRESOURCE(cpl.idIcon), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); if (!hIconTemp) { // the id was bogus, make it a negative number (invalid resource)... cpl.idIcon = -1; TraceMsg(TF_GENERAL, "_InitializaControl: %s returned an invalid icon id, ignoring", pcplm->szModule); } } pcpli->idIcon = cpl.idIcon; // // Try the new method first and call it with the largest structure // so it doesn't overwrite anything on the stack. If you put a // Unicode applet on Windows '95 it will kill the Explorer because // it trashes memory by overwriting the stack. // memset(&Newcpl,0,sizeof(Newcpl)); CPL_CallEntry(pcplm, NULL, CPL_NEWINQUIRE, (LONG)pcpli->idControl, (LONG_PTR)(LPCPLINFO)&Newcpl); // // If the call is to an ANSI applet, convert strings to Unicode // // I'm leaving this ANSI-only code as it helps explain // the way some of the subsequent code works - or why it was // written the way it was. [brianau - 03/07/02] // #ifdef UNICODE #define UNNATIVE_SIZE sizeof(NEWCPLINFOA) #else #define UNNATIVE_SIZE sizeof(NEWCPLINFOW) #endif if (Newcpl.Native.dwSize == UNNATIVE_SIZE) { ConvertCplInfo(&Newcpl); // This will set Newcpl.Native.dwSize } if (Newcpl.Native.dwSize == sizeof(NEWCPLINFO)) { pszName = _RestoreTruncatedCplString(pcplm->minst.hinst, cpl.idName, pszName, szName, ARRAYSIZE(szName)); pszInfo = _RestoreTruncatedCplString(pcplm->minst.hinst, cpl.idInfo, pszInfo, szInfo, ARRAYSIZE(szInfo)); } else { Newcpl.Native.hIcon = LoadImage(pcplm->minst.hinst, MAKEINTRESOURCE(cpl.idIcon), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); pszName = szName; LoadString(pcplm->minst.hinst, cpl.idName, szName, ARRAYSIZE(szName)); pszInfo = szInfo; LoadString(pcplm->minst.hinst, cpl.idInfo, szInfo, ARRAYSIZE(szInfo)); Newcpl.Native.szHelpFile[0] = 0; Newcpl.Native.lData = cpl.lData; Newcpl.Native.dwHelpContext = 0; } pcpli->hIcon = Newcpl.Native.hIcon; if (hIconTemp) DestroyIcon(hIconTemp); fSucceed = Str_SetPtr(&pcpli->pszName, pszName) && Str_SetPtr(&pcpli->pszInfo, pszInfo) && Str_SetPtr(&pcpli->pszHelpFile, Newcpl.Native.szHelpFile); pcpli->lData = Newcpl.Native.lData; pcpli->dwContext = Newcpl.Native.dwHelpContext; #ifdef DEBUG if (!pcpli->idIcon) TraceMsg(TF_GENERAL, "PERFORMANCE: cannot cache %s because no icon ID for <%s>", pcplm->szModule, pcpli->pszName); #endif return fSucceed; } // // Terminate the control // void _TerminateControl(LPCPLMODULE pcplm, LPCPLITEM pcpli) { if (pcpli->hIcon) { DestroyIcon(pcpli->hIcon); pcpli->hIcon = NULL; } Str_SetPtr(&pcpli->pszName, NULL); Str_SetPtr(&pcpli->pszInfo, NULL); Str_SetPtr(&pcpli->pszHelpFile, NULL); CPL_CallEntry(pcplm, NULL, CPL_STOP, pcpli->idControl, pcpli->lData); } void _FreeLibraryForControlPanel(MINST *pminst) { FreeLibrary(pminst->hinst); } // // For each control of the specified CPL module, call the control entry // with CPL_STOP. Then, call it with CPL_EXIT. // void _TerminateCPLModule(LPCPLMODULE pcplm) { if (pcplm->minst.hinst) { ULONG_PTR dwCookie = 0; if (pcplm->lpfnCPL) { if (pcplm->hacpli) { int cControls, i; for (i = 0, cControls = DSA_GetItemCount(pcplm->hacpli); i < cControls; ++i) { LPCPLITEM pcpli = DSA_GetItemPtr(pcplm->hacpli, i); _TerminateControl(pcplm, pcpli); } DSA_DeleteAllItems(pcplm->hacpli); DSA_Destroy(pcplm->hacpli); pcplm->hacpli=NULL; } CPL_CallEntry(pcplm, NULL, CPL_EXIT, 0, 0); pcplm->lpfnCPL=NULL; } ActivateActCtx(pcplm->hActCtx, &dwCookie); _FreeLibraryForControlPanel(&pcplm->minst); if (dwCookie != 0) DeactivateActCtx(0, dwCookie); pcplm->minst.hinst = NULL; } pcplm->minst.idOwner = (DWORD)-1; if (pcplm->hActCtx != NULL) { ReleaseActCtx(pcplm->hActCtx); pcplm->hActCtx = NULL; } if (pcplm->minst.hOwner) { CloseHandle(pcplm->minst.hOwner); pcplm->minst.hOwner = NULL; } } // // Initializes the CPL Module. // // Requires: // *pcplm should be initialized appropriately. // // BOOL _InitializeCPLModule(LPCPLMODULE pcplm) { BOOL fSuccess = FALSE; pcplm->lpfnCPL32 = (APPLET_PROC)GetProcAddress(pcplm->minst.hinst, "CPlApplet"); // // Initialize the CPL if (pcplm->lpfnCPL && CPL_CallEntry(pcplm, NULL, CPL_INIT, 0, 0)) { int cControls = (int)CPL_CallEntry(pcplm, NULL, CPL_GETCOUNT, 0, 0); if (cControls>0) { // // By passing in the number of applets, we should speed up allocation // of this array. // pcplm->hacpli = DSA_Create(sizeof(CPLITEM), cControls); if (pcplm->hacpli) { int i; fSuccess = TRUE; // succeded, so far. // Go through the applets and load the information about them for (i = 0; i < cControls; ++i) { CPLITEM control = {i, 0}; if (_InitializeControl(pcplm, &control)) { // removing this now saves us from doing it later CPL_StripAmpersand(control.pszName); if (DSA_AppendItem(pcplm->hacpli, &control) >= 0) { continue; } } _TerminateControl(pcplm, &control); fSuccess=FALSE; break; } } } } else { // don't ever call it again if we couldn't CPL_INIT pcplm->lpfnCPL = NULL; } return fSuccess; } // // Returns: // The index to the g_hacplmLoaded, if the specified DLL is already // loaded; -1 otherwise. // int _FindCPLModule(const MINST * pminst) { int i = -1; // Assumes error ENTERCRITICAL; if (g_hacplmLoaded) { for (i=DSA_GetItemCount(g_hacplmLoaded)-1; i>=0; --i) { LPCPLMODULE pcplm = DSA_GetItemPtr(g_hacplmLoaded, i); // // owner id tested last since hinst is more varied // if ((pcplm->minst.hinst == pminst->hinst) && (pcplm->minst.idOwner == pminst->idOwner)) { break; } } } LEAVECRITICAL; return i; } LPCPLMODULE FindCPLModule(const MINST * pminst) { return (LPCPLMODULE) DSA_GetItemPtr(g_hacplmLoaded, _FindCPLModule(pminst)); } // // Adds the specified CPL module to g_hacplmLoaded. // // Requires: // The specified CPL module is not in g_hacplmLoaded yet. // // Returns: // The index to the CPL module if succeeded; -1 otherwise. // int _AddModule(LPCPLMODULE pcplm) { int result; // // Create the Loaded Modules guy if necessary // ENTERCRITICAL; if (g_hacplmLoaded == NULL) g_hacplmLoaded = DSA_Create(sizeof(CPLMODULE), 4); // // Add this CPL to our list // if (g_hacplmLoaded == NULL) result = -1; else result = DSA_AppendItem(g_hacplmLoaded, pcplm); LEAVECRITICAL; return(result); } #define SZ_RUNDLL32_NOEXCEPT_ARGS TEXT("/d ") BOOL CatchCPLExceptions(UINT msg) { LPCTSTR pszCmdLine = GetCommandLine(); BOOL fCatch = TRUE; // Some callers don't want to run and have exceptions caught. // This will allow crashes to be uploaded to PCHealth. if (((CPL_STARTWPARMSA == msg) || (CPL_STARTWPARMS == msg) || (CPL_DBLCLK == msg)) && StrCmpNI(pszCmdLine, SZ_RUNDLL32_NOEXCEPT_ARGS, (ARRAYSIZE(SZ_RUNDLL32_NOEXCEPT_ARGS) - 1))) { fCatch = FALSE; } return fCatch; } LRESULT CPL_CallEntry(LPCPLMODULE pcplm, HWND hwnd, UINT msg, LPARAM lParam1, LPARAM lParam2) { LRESULT lres; ULONG_PTR dwCookie = 0; ActivateActCtx(pcplm->hActCtx, &dwCookie); if (!CatchCPLExceptions(msg)) { lres = pcplm->lpfnCPL32(hwnd, msg, lParam1, lParam2); } else { __try { lres = pcplm->lpfnCPL32(hwnd, msg, lParam1, lParam2); } __except(SetErrorMode(SEM_NOGPFAULTERRORBOX),UnhandledExceptionFilter(GetExceptionInformation())) { TraceMsg(TF_ERROR, "CPL: Exception calling CPL module: %s", pcplm->szModule); ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE(IDS_CPL_EXCEPTION), MAKEINTRESOURCE(IDS_CONTROLPANEL), MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL, (LPTSTR)pcplm->szModule); lres = 0; } } if (dwCookie != 0) DeactivateActCtx(0, dwCookie); return lres; } EXTERN_C DECLSPEC_IMPORT BOOL STDAPICALLTYPE ApphelpCheckExe( LPCWSTR lpApplicationName, BOOL bApphelp, BOOL bShim, BOOL bUseModuleName ); // // Loads the specified CPL module and returns the index to g_hacplmLoaded. // int _LoadCPLModule(LPCTSTR pszModule) { TCHAR szManifest[MAX_PATH]; HANDLE hActCtx = NULL; MINST minst; int iModule; ULONG_PTR dwCookie; ACTCTX act = {0}; #ifndef UNICODE WCHAR wszModuleName[MAX_PATH]; #endif LPWSTR pwszModuleName = NULL; if (FAILED(StringCchPrintf(szManifest, ARRAYSIZE(szManifest), TEXT("%s.manifest"), pszModule))) { return -1; } minst.idOwner = GetCurrentProcessId(); minst.hOwner = OpenProcess(SYNCHRONIZE,FALSE,minst.idOwner); if (NULL == minst.hOwner) { return -1; } // See if this application has a context if (PathFileExists(szManifest)) { act.cbSize = sizeof(act); act.dwFlags = 0; act.lpSource = szManifest; hActCtx = CreateActCtx(&act); } else { act.cbSize = sizeof(act); act.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; act.lpSource = pszModule; act.lpResourceName = MAKEINTRESOURCE(123); hActCtx = CreateActCtx(&act); } if (hActCtx == INVALID_HANDLE_VALUE) hActCtx = NULL; ActivateActCtx(hActCtx, &dwCookie); // // check and [possibly] fix this module // #ifdef UNICODE pwszModuleName = (LPWSTR)pszModule; #else // if !defined(UNICODE) if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszModule, -1, wszModuleName, ARRAYSIZE(wszModuleName)) != 0) { pwszModuleName = wszModuleName; } #endif // UNICODE if (!ApphelpCheckExe(pwszModuleName, TRUE, TRUE, TRUE)) { minst.hinst = NULL; } else { minst.hinst = LoadLibrary(pszModule); } if (dwCookie != 0) DeactivateActCtx(0, dwCookie); if (!ISVALIDHINSTANCE(minst.hinst)) { if (hActCtx != NULL) { ReleaseActCtx(hActCtx); } CloseHandle(minst.hOwner); return -1; } // // Check if this module is already in the list. // iModule = _FindCPLModule(&minst); if (iModule >= 0) { // // Yes. Increment the reference count and return the ID. // LPCPLMODULE pcplm; ENTERCRITICAL; pcplm = DSA_GetItemPtr(g_hacplmLoaded, iModule); ++pcplm->cRef; LEAVECRITICAL; ActivateActCtx(hActCtx, &dwCookie); // // Decrement KERNELs reference count // _FreeLibraryForControlPanel(&minst); if (dwCookie != 0) DeactivateActCtx(0, dwCookie); if (hActCtx != NULL) ReleaseActCtx(hActCtx); CloseHandle(minst.hOwner); } else { CPLMODULE sModule = {0}; // // No. Append it. // sModule.cRef = 1; sModule.minst = minst; sModule.hActCtx = hActCtx; GetModuleFileName(minst.hinst, sModule.szModule, ARRAYSIZE(sModule.szModule)); if (_InitializeCPLModule(&sModule)) { iModule = _AddModule(&sModule); } if (iModule < 0) { _TerminateCPLModule(&sModule); } } return iModule; } int _FreeCPLModuleIndex(int iModule) { LPCPLMODULE pcplm; ENTERCRITICAL; pcplm = DSA_GetItemPtr(g_hacplmLoaded, iModule); if (!pcplm) { LEAVECRITICAL; return(-1); } // // Dec the ref count; return if not 0 // --pcplm->cRef; if (pcplm->cRef) { LEAVECRITICAL; return(pcplm->cRef); } // // Free up the whole thing and return 0 // _TerminateCPLModule(pcplm); DSA_DeleteItem(g_hacplmLoaded, iModule); // // Destroy this when all CPLs have been removed // if (DSA_GetItemCount(g_hacplmLoaded) == 0) { DSA_Destroy(g_hacplmLoaded); g_hacplmLoaded = NULL; } LEAVECRITICAL; return(0); } int _FreeCPLModuleHandle(const MINST * pminst) { int iModule; // // Check if the module is actually loaded (major error if not) // iModule = _FindCPLModule(pminst); if (iModule < 0) { return(-1); } return _FreeCPLModuleIndex(iModule); } int CPL_FreeCPLModule(LPCPLMODULE pcplm) { return _FreeCPLModuleHandle(&pcplm->minst); } void CPL_StripAmpersand(LPTSTR szBuffer) { LPTSTR pIn, pOut; // // copy the name sans '&' chars // pIn = pOut = szBuffer; do { // // strip FE accelerators with parentheses. e.g. "foo(&F)" -> "foo" // if (*pIn == TEXT('(') && *(pIn+1) == TEXT('&') && *(pIn+2) && *(pIn+3) == TEXT(')')) { pIn += 4; } #ifdef DBCS // Also strip FE accelerators in old win31 cpl, i.e, 01EH/01FH. if (*pIn == 0x1e && *++pIn) { // Assumes a character right before the mnemonic // is a parenthesis or something to be removed as well. // pOut=CharPrev(szBuffer, pOut); // Skip Alphabet accelerator. pIn = CharNext(pIn); if (*pIn) { if (*pIn == 0x1f && *++pIn) { // Skip FE accelelator // pIn = CharNext(pIn); } // Skip second parenthesis. // pIn = CharNext(pIn); } } #endif if (*pIn != TEXT('&')) { *pOut++ = *pIn; } if (IsDBCSLeadByte(*pIn)) { *pOut++ = *++pIn; } } while (*pIn++) ; } // // filter out bogus old ini keys... we may be able to blow this off BOOL IsValidCplKey(LPCTSTR pszKey) { return lstrcmpi(pszKey, TEXT("NumApps")) && !((*(pszKey+1) == 0) && ((*pszKey == TEXT('X')) || (*pszKey == TEXT('Y')) || (*pszKey == TEXT('W')) || (*pszKey == TEXT('H')))); } LPCPLMODULE CPL_LoadCPLModule(LPCTSTR szModule) { LPCPLMODULE result; int iModule = _LoadCPLModule(szModule); if (iModule < 0) result = NULL; else { ENTERCRITICAL; result = DSA_GetItemPtr(g_hacplmLoaded, iModule); LEAVECRITICAL; } return result; } BOOL DontLoadCPL(LPCTSTR pszName) { // the first reg location is the old alias for control.ini [don't load] // entries that map into the registry on NT. the next is for per // machine support to hide cpls, new for whistler return (SHGetValue(HKEY_CURRENT_USER, TEXT("Control Panel\\don't load"), pszName, NULL, NULL, NULL) == ERROR_SUCCESS) || (SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\don't load"), pszName, NULL, NULL, NULL) == ERROR_SUCCESS); } // Called for each CPL module file which we may want to load void _InsertModuleName(ControlData *lpData, LPCTSTR szPath, PMODULEINFO pmi) { pmi->pszModule = NULL; Str_SetPtr(&pmi->pszModule, szPath); if (pmi->pszModule) { int i; pmi->pszModuleName = PathFindFileName(pmi->pszModule); if (DontLoadCPL(pmi->pszModuleName)) { Str_SetPtr(&pmi->pszModule, NULL); goto skip; } // don't insert the module if it's already in the list! for (i = DSA_GetItemCount(lpData->hamiModule)-1 ; i >= 0 ; i--) { PMODULEINFO pmi1 = DSA_GetItemPtr(lpData->hamiModule, i); if (!lstrcmpi(pmi1->pszModuleName, pmi->pszModuleName)) { Str_SetPtr(&pmi->pszModule, NULL); goto skip; } } DSA_AppendItem(lpData->hamiModule, pmi); skip: ; } } #define GETMODULE(haminst,i) ((MINST *)DSA_GetItemPtr(haminst, i)) #define ADDMODULE(haminst,pminst) DSA_AppendItem(haminst, (void *)pminst) int _LoadCPLModuleAndAdd(ControlData *lpData, LPCTSTR szModule) { int iModule, i; LPCPLMODULE pcplm; // // Load the module and controls (or get the previous one if already // loaded). // iModule = _LoadCPLModule(szModule); if (iModule < 0) { TraceMsg(TF_WARNING, "_LoadCPLModuleAndAdd: _LoadControls refused %s", szModule); return -1; } pcplm = DSA_GetItemPtr(g_hacplmLoaded, iModule); if (pcplm == NULL) { TraceMsg(TF_WARNING, "_LoadCPLModuleAndAdd: DSA returned NULL structure"); return -1; } // // Check if this guy has already loaded this module // for (i = DSA_GetItemCount(lpData->haminst) - 1; i >= 0; --i) { const MINST * pminst = GETMODULE(lpData->haminst,i); // // note: owner id tested last since hinst is more varied // if ((pminst->hinst == pcplm->minst.hinst) && (pminst->idOwner == pcplm->minst.idOwner)) { FreeThisModule: // // This guy already loaded this module, so dec // the reference and return failure // _FreeCPLModuleIndex(iModule); return(-1); } } // // this is a new module, so add it to the list // if (ADDMODULE(lpData->haminst, &pcplm->minst) < 0) { goto FreeThisModule; } return iModule; } void _AddItemsFromKey(ControlData *pcd, HKEY hkRoot) { HKEY hkey; MODULEINFO mi = {0}; mi.flags = MI_FIND_FILE; if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\CPLs"), 0, KEY_QUERY_VALUE, &hkey)) { TCHAR szData[MAX_PATH], szValue[128]; DWORD dwSizeData, dwValue, dwIndex; for (dwIndex = 0; ERROR_SUCCESS == (dwValue = ARRAYSIZE(szValue), dwSizeData = sizeof(szData), RegEnumValue(hkey, dwIndex, szValue, &dwValue, NULL, NULL, (BYTE *)szData, &dwSizeData)); dwIndex++) { TCHAR szPath[MAX_PATH]; if (SHExpandEnvironmentStrings(szData, szPath, ARRAYSIZE(szPath))) { WIN32_FIND_DATA fd; HANDLE hfind = FindFirstFile(szPath, &fd); if (hfind != INVALID_HANDLE_VALUE) { mi.ftCreationTime = fd.ftCreationTime; mi.nFileSizeHigh = fd.nFileSizeHigh; mi.nFileSizeLow = fd.nFileSizeLow; FindClose(hfind); _InsertModuleName(pcd, szPath, &mi); } } } RegCloseKey(hkey); } } /* Get the keynames under [MMCPL] in CONTROL.INI and cycle through all such keys to load their applets into our list box. Also allocate the array of CPLMODULE structs. Returns early if can't load old WIN3 applets. */ BOOL CPLD_GetModules(ControlData *lpData) { LPTSTR pStr; HANDLE hFindFile; WIN32_FIND_DATA findData; MODULEINFO mi; TCHAR szPath[MAX_PATH], szSysDir[MAX_PATH], szName[MAX_PATH]; ASSERT(lpData->hamiModule == NULL); lpData->hamiModule = DSA_Create(sizeof(mi), 4); if (!lpData->hamiModule) { return FALSE; } lpData->haminst = DSA_Create(sizeof(MINST), 4); if (!lpData->haminst) { DSA_Destroy(lpData->hamiModule); lpData->hamiModule = NULL; // no one is freeing hamiModule in the caller if this fails, but just to make sure ... return FALSE; } // // So here's the deal: // We have this global list of all modules that have been loaded, along // with a reference count for each. This is so that we don't need to // load a CPL file again when the user double clicks on it. // We still need to keep a list for each window that is open, so that // we will not load the same CPL twice in a single window. Therefore, // we need to keep a list of all modules that are loaded (note that // we cannot just keep indexes, since the global list can move around). // // hamiModule contains the module name, the instance info if loaded, // and some other information for comparing with cached information // ZeroMemory(&mi, sizeof(mi)); // // don't special case main, instead sort the data by title // GetSystemDirectory(szSysDir, ARRAYSIZE(szSysDir)); // load the modules specified in CONTROL.INI under [MMCPL] { TCHAR szKeys[512]; // from section of extra cpls to load GetPrivateProfileString(TEXT("MMCPL"), NULL, c_szNULL, szKeys, ARRAYSIZE(szKeys), TEXT("control.ini")); for (pStr = szKeys; *pStr; pStr += lstrlen(pStr) + 1) { GetPrivateProfileString(TEXT("MMCPL"), pStr, c_szNULL, szName, ARRAYSIZE(szName), TEXT("control.ini")); if (IsValidCplKey(pStr)) { _InsertModuleName(lpData, szName, &mi); } } } // load applets from the system directory if (PathCombine(szPath, szSysDir, TEXT("*.CPL"))) { mi.flags |= MI_FIND_FILE; hFindFile = FindFirstFile(szPath, &findData); if (hFindFile != INVALID_HANDLE_VALUE) { do { if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { BOOL fOkToUse = TRUE; if (IsOS(OS_WOW6432)) { GetWindowsDirectory(szPath,ARRAYSIZE(szPath)); if (PathCombine(szPath, szPath, TEXT("system32")) && PathCombine(szPath, szPath, findData.cFileName)) { // // Don't use any CPLs which exist in the real system32 // directory, since we only have them in the fake system // directory for compatibility reasons. // if (PathFileExists(szPath)) { fOkToUse = FALSE; } } } if (fOkToUse) { if (PathCombine(szPath, szSysDir, findData.cFileName)) { mi.ftCreationTime = findData.ftCreationTime; mi.nFileSizeHigh = findData.nFileSizeHigh; mi.nFileSizeLow = findData.nFileSizeLow; _InsertModuleName(lpData, szPath, &mi); } } } } while (FindNextFile(hFindFile, &findData)); FindClose(hFindFile); } } _AddItemsFromKey(lpData, HKEY_CURRENT_USER); _AddItemsFromKey(lpData, HKEY_LOCAL_MACHINE); lpData->cModules = DPA_GetPtrCount(lpData->hamiModule); return TRUE; } // // A bug has existed in Control Panel for as long as I can tell that, // under the right conditions involving roaming user profiles, could // prevent a control panel item from appearing in the folder. The // problem description is too long for a comment but the MI_REG_INVALID // flag was added to solve it. See it's use in CControlPanelEnum::Next. // However, presentation caches created prior to this fix under the // problem scenario may still result in affected CPL items not appearing. // In those cases, refreshing the presentation cache using the new code // corrects the problem. Therefore, any cache created prior to the date // of this new code's introduction must be discarded and refreshed. // The presence of the REGCPL_POST_102001 (oct 2001) flag in a cache entry // indicates the cache was created after this new code was introduced and may // be safely used. This function reports the presence/absence of this // flag in the cache buffer. [brianau - 10/03/01] // BOOL _IsPresentationCachePostOctober2001(LPBYTE pbData, DWORD cbData) { BOOL bPostOct2001 = FALSE; if (cbData >= sizeof(REG_CPL_INFO)) { if (REGCPL_POST_102001 & ((REG_CPL_INFO *)pbData)->flags) { bPostOct2001 = TRUE; } } return bPostOct2001; } // Read the registry for cached CPL info. // If this info is up-to-date with current modules (from CPLD_GetModules), // then we can enumerate these without loading the CPLs. void CPLD_GetRegModules(ControlData *lpData) { HKEY hkey; LPCTSTR lpszRegKey; // dont cache any-thing in clean boot. if (GetSystemMetrics(SM_CLEANBOOT)) return; if (IsOS(OS_WOW6432)) { lpszRegKey = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Controls Folder (Wow64)"); } else { lpszRegKey = REGSTR_PATH_CONTROLSFOLDER; } if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, lpszRegKey, 0, KEY_READ, &hkey)) { DWORD cbSize; DWORD dwLCID = 0; cbSize = sizeof(dwLCID); // fail if this cache is not tagged with the UI language ID, or we are not in // the language ID that the cache was saved in. if (ERROR_SUCCESS != SHQueryValueEx(hkey, TEXT("Presentation LCID"), NULL, NULL, (LPBYTE) &dwLCID, &cbSize) || dwLCID != GetUserDefaultUILanguage()) { RegCloseKey(hkey); return; } if (ERROR_SUCCESS == SHQueryValueEx(hkey, TEXT("Presentation Cache"), NULL, NULL, NULL, &cbSize)) { lpData->pRegCPLBuffer = LocalAlloc(LPTR, cbSize); if (lpData->pRegCPLBuffer) { if (ERROR_SUCCESS == SHQueryValueEx(hkey, TEXT("Presentation Cache"), NULL, NULL, lpData->pRegCPLBuffer, &cbSize)) { if (!_IsPresentationCachePostOctober2001(lpData->pRegCPLBuffer, cbSize)) { TraceMsg(TF_WARNING, "CPLD_GetRegModules: Presentation cache pre Oct 2001."); LocalFree(lpData->pRegCPLBuffer); lpData->pRegCPLBuffer = NULL; } else { lpData->hRegCPLs = DPA_Create(4); if (lpData->hRegCPLs) { REG_CPL_INFO * p; DWORD cbOffset; for (cbOffset = 0 ; cbOffset < cbSize ; cbOffset += p->cbSize) { p = (REG_CPL_INFO *)&(lpData->pRegCPLBuffer[cbOffset]); p->flags |= REGCPL_FROMREG; DPA_AppendPtr(lpData->hRegCPLs, p); //DebugMsg(DM_TRACE,"sh CPLD_GetRegModules: %s (%s)", REGCPL_FILENAME(p), REGCPL_CPLNAME(p)); } lpData->cRegCPLs = DPA_GetPtrCount(lpData->hRegCPLs); } } } else { TraceMsg(TF_WARNING, "CPLD_GetRegModules: failed read!"); } } // Alloc } // SHQueryValueEx for size RegCloseKey(hkey); } // RegOpenKey } // // On a typical system, we will successfully cache all the CPLs. So this // function will write out the data only once. // void CPLD_FlushRegModules(ControlData *lpData) { if (lpData->fRegCPLChanged) { int num = DPA_GetPtrCount(lpData->hRegCPLs); DWORD cbSize = num * sizeof(REG_CPL_INFO); REG_CPL_INFO * prcpli = LocalAlloc(LPTR, cbSize); LPCTSTR lpszRegKey; if (IsOS(OS_WOW6432)) { lpszRegKey = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Controls Folder (Wow64)"); } else { lpszRegKey = REGSTR_PATH_CONTROLSFOLDER; } if (prcpli) { REG_CPL_INFO * pDest; HKEY hkey; int i; // // 0<=i<=num && CPLs 0..i-1 have been copied to prcpli or skipped // for (i = 0, pDest = prcpli; i < num ;) { REG_CPL_INFO * p = DPA_GetPtr(lpData->hRegCPLs, i); int j; // // if any CPL in this module has a dynamic icon, we cannot cache // any of this module's CPLs. // // i<=j<=num && CPLs i..j-1 are in same module // for (j = i; j < num; j++) { REG_CPL_INFO * q = DPA_GetPtr(lpData->hRegCPLs, j); if (lstrcmp(REGCPL_FILENAME(p), REGCPL_FILENAME(q))) { // all CPLs in this module are okay, save 'em break; } if (q->idIcon == 0) { TraceMsg(TF_GENERAL, "CPLD_FlushRegModules: SKIPPING %s (%s) [dynamic icon]",REGCPL_FILENAME(p),REGCPL_CPLNAME(p)); // this module has a dynamic icon, skip it for (j++ ; j < num ; j++) { q = DPA_GetPtr(lpData->hRegCPLs, j); if (lstrcmp(REGCPL_FILENAME(p), REGCPL_FILENAME(q))) break; } i = j; break; } } // CPLs i..j-1 are in the same module and need to be saved // (if jhRegCPLs, i); hmemcpy(pDest, p, p->cbSize); // // Set the post-Oct 2001 flag. // pDest->flags |= REGCPL_POST_102001; pDest = (REG_CPL_INFO *)(((LPBYTE)pDest) + pDest->cbSize); //DebugMsg(DM_TRACE,"CPLD_FlushRegModules: %s (%s)",REGCPL_FILENAME(p),REGCPL_CPLNAME(p)); } } // for (i=0,pDest=prcpli // prcpli contains packed REG_CPL_INFO structures to save to the registry if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, lpszRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, // default security &hkey, NULL)) { DWORD dwLCID; DWORD dwSize = sizeof(dwLCID); dwLCID = GetUserDefaultUILanguage(); if (ERROR_SUCCESS != RegSetValueEx(hkey, TEXT("Presentation LCID"), 0, REG_DWORD, (LPBYTE) &dwLCID, dwSize)) { TraceMsg(TF_WARNING, "CPLD_FLushRegModules: failed to write the LCID!"); } if (ERROR_SUCCESS != RegSetValueEx(hkey, TEXT("Presentation Cache"), 0, REG_BINARY, (LPBYTE)prcpli, (DWORD) ((LPBYTE)pDest-(LPBYTE)prcpli))) { TraceMsg(TF_WARNING, "CPLD_FLushRegModules: failed write!"); } RegCloseKey(hkey); } LocalFree((HLOCAL)prcpli); lpData->fRegCPLChanged = FALSE; // no longer dirty } // if (prcpli) } // if dirty } //--------------------------------------------------------------------------- void CPLD_Destroy(ControlData *lpData) { int i; if (lpData->haminst) { for (i=DSA_GetItemCount(lpData->haminst)-1 ; i>=0 ; --i) _FreeCPLModuleHandle(DSA_GetItemPtr(lpData->haminst, i)); DSA_Destroy(lpData->haminst); } if (lpData->hamiModule) { for (i=DSA_GetItemCount(lpData->hamiModule)-1 ; i>=0 ; --i) { PMODULEINFO pmi = DSA_GetItemPtr(lpData->hamiModule, i); Str_SetPtr(&pmi->pszModule, NULL); } DSA_Destroy(lpData->hamiModule); } if (lpData->hRegCPLs) { CPLD_FlushRegModules(lpData); for (i = DPA_GetPtrCount(lpData->hRegCPLs)-1 ; i >= 0 ; i--) { REG_CPL_INFO * p = DPA_GetPtr(lpData->hRegCPLs, i); if (!(p->flags & REGCPL_FROMREG)) LocalFree((HLOCAL)p); } DPA_Destroy(lpData->hRegCPLs); } if (lpData->pRegCPLBuffer) LocalFree((HLOCAL)lpData->pRegCPLBuffer); } // // Loads module lpData->hamiModule[nModule] and returns # cpls in module int CPLD_InitModule(ControlData *lpData, int nModule, MINST *pminst) { PMODULEINFO pmi; LPCPLMODULE pcplm; int iModule; pmi = DSA_GetItemPtr(lpData->hamiModule, nModule); if (pmi == NULL) { TraceMsg(TF_WARNING, "CPLD_InitModule: DSA returned NULL structure"); return 0; } iModule = _LoadCPLModuleAndAdd(lpData, pmi->pszModule); if (iModule < 0) { return(0); } pcplm = DSA_GetItemPtr(g_hacplmLoaded, iModule); *pminst = pcplm->minst; return DSA_GetItemCount(pcplm->hacpli); } BOOL CPLD_AddControlToReg(ControlData *lpData, const MINST * pminst, int nControl) { int iModule; LPCPLMODULE pcplm; LPCPLITEM pcpli = NULL; TCHAR buf[MAX_PATH]; HANDLE hFindFile; WIN32_FIND_DATA findData; iModule = _FindCPLModule(pminst); if (iModule >= 0) { pcplm = DSA_GetItemPtr(g_hacplmLoaded, iModule); if (pcplm != NULL) pcpli = DSA_GetItemPtr(pcplm->hacpli, nControl); } if (pcpli == NULL) return FALSE; // // PERF: Why are we using GetModuleFileName instead of the name // of the file we used to load this module? (We have the name both // in the calling function and in lpData.) // GetModuleFileName(pcplm->minst.hinst, buf, MAX_PATH); if (*buf != 0) hFindFile = FindFirstFile(buf, &findData); else hFindFile = INVALID_HANDLE_VALUE; if (hFindFile != INVALID_HANDLE_VALUE) { REG_CPL_INFO * prcpli = LocalAlloc(LPTR, sizeof(REG_CPL_INFO)); FindClose(hFindFile); if (prcpli) { if (SUCCEEDED(StringCchCopy(REGCPL_FILENAME(prcpli), MAX_PATH, buf))) { prcpli->flags = FALSE; prcpli->ftCreationTime = findData.ftCreationTime; prcpli->nFileSizeHigh = findData.nFileSizeHigh; prcpli->nFileSizeLow = findData.nFileSizeLow; prcpli->idIcon = pcpli->idIcon; prcpli->oName = lstrlen(REGCPL_FILENAME(prcpli)) + 1; // // We don't check return values from StringCchCopy because we // don't care if the CPL display name or info text are truncated. // We do care that the file name is complete (above). // StringCchCopy(REGCPL_CPLNAME(prcpli), MAX_CCH_CPLNAME, pcpli->pszName); prcpli->oInfo = prcpli->oName + lstrlen(REGCPL_CPLNAME(prcpli)) + 1; StringCchCopy(REGCPL_CPLINFO(prcpli), MAX_CCH_CPLINFO, pcpli->pszInfo); prcpli->cbSize = FIELD_OFFSET(REG_CPL_INFO, buf) + (prcpli->oInfo + lstrlen(REGCPL_CPLINFO(prcpli)) + 1) * sizeof(TCHAR); // // Force struct size to be DWORD aligned since these are packed // together in registry, then read and accessed after reading // cache from registry. // if (prcpli->cbSize & 3) prcpli->cbSize += sizeof(DWORD) - (prcpli->cbSize & 3); if (!lpData->hRegCPLs) { lpData->hRegCPLs = DPA_Create(4); } if (lpData->hRegCPLs) { DPA_AppendPtr(lpData->hRegCPLs, prcpli); // // don't update cRegCPLs. We don't need it any more, and // it is also the upper-end counter for ESF_Next registry enum. //lpData->cRegCPLs++; // lpData->fRegCPLChanged = TRUE; } else LocalFree((HLOCAL)prcpli); } } } return TRUE; }