/*************************************************************************** * dll.c * * Standard DLL entry-point functions * ***************************************************************************/ #include "shellprv.h" #include // for NtQuery #include #include // For REGINSTALL #include "fstreex.h" #include "ids.h" #include "filefldr.h" #include "uemapp.h" #include #define DECL_CRTFREE #include #define INSTALL_MSI 1 #include // For MSI_Install() void MSI_Install(LPTSTR pszMSIFile); void DoFusion(); STDAPI_(void) Control_FillCache_RunDLL( HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow ); BOOL CopyRegistryValues (HKEY hKeyBaseSource, LPCTSTR pszSource, HKEY hKeyBaseTarget, LPCTSTR pszTarget); // DllGetVersion - New for IE 4.0 shell integrated mode // // All we have to do is declare this puppy and CCDllGetVersion does the rest // DLLVER_DUALBINARY(VER_PRODUCTVERSION_DW, VER_PRODUCTBUILD_QFE); HRESULT CallRegInstall(LPCSTR szSection) { HRESULT hr = E_FAIL; HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL")); if (hinstAdvPack) { REGINSTALL pfnri = (REGINSTALL)GetProcAddress(hinstAdvPack, "RegInstall"); if (pfnri) { char szShdocvwPath[MAX_PATH]; STRENTRY seReg[] = { { "SHDOCVW_PATH", szShdocvwPath }, { "25", "%SystemRoot%" }, { "11", "%SystemRoot%\\system32" }, }; STRTABLE stReg = { ARRAYSIZE(seReg), seReg }; // Get the location of shdocvw.dll lstrcpyA(szShdocvwPath, "%SystemRoot%\\system32"); PathAppendA(szShdocvwPath, "shdocvw.dll"); hr = pfnri(g_hinst, szSection, &stReg); } // since we only do this from DllInstall() don't load and unload advpack over and over // FreeLibrary(hinstAdvPack); } return hr; } BOOL UnregisterTypeLibrary(const CLSID* piidLibrary) { TCHAR szScratch[GUIDSTR_MAX]; HKEY hk; BOOL f = FALSE; // convert the libid into a string. // SHStringFromGUID(*piidLibrary, szScratch, ARRAYSIZE(szScratch)); if (RegOpenKey(HKEY_CLASSES_ROOT, TEXT("TypeLib"), &hk) == ERROR_SUCCESS) { f = RegDeleteKey(hk, szScratch); RegCloseKey(hk); } return f; } HRESULT Shell32RegTypeLib(void) { TCHAR szPath[MAX_PATH]; WCHAR wszPath[MAX_PATH]; // Load and register our type library. // GetModuleFileName(HINST_THISDLL, szPath, ARRAYSIZE(szPath)); SHTCharToUnicode(szPath, wszPath, ARRAYSIZE(wszPath)); ITypeLib *pTypeLib; HRESULT hr = LoadTypeLib(wszPath, &pTypeLib); if (SUCCEEDED(hr)) { // call the unregister type library as we had some old junk that // was registered by a previous version of OleAut32, which is now causing // the current version to not work on NT... UnregisterTypeLibrary(&LIBID_Shell32); hr = RegisterTypeLib(pTypeLib, wszPath, NULL); if (FAILED(hr)) { TraceMsg(TF_WARNING, "SHELL32: RegisterTypeLib failed (%x)", hr); } pTypeLib->Release(); } else { TraceMsg(TF_WARNING, "SHELL32: LoadTypeLib failed (%x)", hr); } return hr; } STDAPI CreateShowDesktopOnQuickLaunch() { // delete the "_Current Item" key used for tip rotation in welcome.exe on every upgrade HKEY hkey; if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\Welcome"), 0, MAXIMUM_ALLOWED, &hkey ) ) { RegDeleteValue(hkey, TEXT("_Current Item")); RegCloseKey(hkey); } // create the "Show Desktop" icon in the quick launch tray TCHAR szPath[MAX_PATH]; if ( SHGetSpecialFolderPath(NULL, szPath, CSIDL_APPDATA, TRUE) ) { TCHAR szQuickLaunch[MAX_PATH]; LoadString(g_hinst, IDS_QUICKLAUNCH, szQuickLaunch, ARRAYSIZE(szQuickLaunch)); if ( PathAppend( szPath, szQuickLaunch ) ) { WritePrivateProfileSection( TEXT("Shell"), TEXT("Command=2\0IconFile=explorer.exe,3\0"), szPath ); WritePrivateProfileSection( TEXT("Taskbar"), TEXT("Command=ToggleDesktop\0"), szPath ); return S_OK; } } return E_FAIL; } void _DoMyDocsPerUserInit(void) { // mydocs!PerUserInit is invoked to setup the desktop.ini and do all the other work // required to make this correct. HINSTANCE hInstMyDocs = LoadLibrary(TEXT("mydocs.dll")); if (hInstMyDocs != NULL) { typedef void (*PFNPerUserInit)(void); PFNPerUserInit pfnPerUserInit = (PFNPerUserInit)GetProcAddress(hInstMyDocs, "PerUserInit"); if (pfnPerUserInit) { pfnPerUserInit(); } FreeLibrary(hInstMyDocs); } } void _NoDriveAutorunTweak() { HKEY hkey; DWORD dwDisp; if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &dwDisp)) { DWORD dwRest; DWORD cbSize = sizeof(dwRest); if (ERROR_SUCCESS != RegQueryValueEx(hkey, TEXT("NoDriveTypeAutoRun"), NULL, NULL, (PBYTE)&dwRest, &cbSize)) { dwRest = 0; } if ((0x95 == dwRest) || (0 == dwRest)) { // Did not change or is not there, let's put 0x91 dwRest = 0x91; RegSetValueEx(hkey, TEXT("NoDriveTypeAutoRun"), 0, REG_DWORD, (PBYTE)&dwRest, sizeof(dwRest)); } else { // Did change, leave it as is } RegCloseKey(hkey); } } // code moved from grpconv.exe // we'd much rather have new firm links than old floppy ones // clear out any old ones made by previous runs of setup void _DeleteOldFloppyLinks(LPITEMIDLIST pidlSendTo, IPersistFile *ppf, IShellLink *psl) { IShellFolder *psfSendTo; if (SUCCEEDED(SHBindToObjectEx(NULL, pidlSendTo, NULL, IID_PPV_ARG(IShellFolder, &psfSendTo)))) { IEnumIDList *penum; if (SUCCEEDED(psfSendTo->EnumObjects(NULL, SHCONTF_NONFOLDERS, &penum))) { LPITEMIDLIST pidl; ULONG celt; while (penum->Next(1, &pidl, &celt) == S_OK) { // is it a link??? if (SHGetAttributes(psfSendTo, pidl, SFGAO_LINK)) { // get the target LPITEMIDLIST pidlFullPath = ILCombine(pidlSendTo, pidl); if (pidlFullPath) { WCHAR szPath[MAX_PATH]; if (SHGetPathFromIDList(pidlFullPath, szPath) && SUCCEEDED(ppf->Load(szPath, 0))) { LPITEMIDLIST pidlTarget; if (SUCCEEDED(psl->GetIDList(&pidlTarget))) { TCHAR szTargetPath[MAX_PATH]; // its possible for the old drive letters to have changed. for example if you // move a removable drive from M:\ to N:\, the shortcut will be invalid, so // we check against DRIVE_NO_ROOT_DIR. // unfortunately we can't tell if we should remove it if they used to have a zip // drive on D:\, and then upgraded and it turned into a hard drive on D:\. the // shortcut will resolve as DRIVE_FIXED and we dont remove it because they might // have created a shortcut to the fixed drive before the upgrade. if (SHGetPathFromIDList(pidlTarget, szTargetPath) && PathIsRoot(szTargetPath) && ((DriveType(PathGetDriveNumber(szTargetPath)) == DRIVE_REMOVABLE) || (DriveType(PathGetDriveNumber(szTargetPath)) == DRIVE_NO_ROOT_DIR))) { Win32DeleteFile(szPath); } ILFree(pidlTarget); } } ILFree(pidlFullPath); } } ILFree(pidl); } penum->Release(); } psfSendTo->Release(); } } void _DeleteOldRemovableLinks() { IShellLink *psl; if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLink, &psl)))) { IPersistFile *ppf; if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))) ) { LPITEMIDLIST pidlSendTo = SHCloneSpecialIDList(NULL, CSIDL_SENDTO, TRUE); if (pidlSendTo) { // we no longer build a list of removable drives here, since that's done on the fly // by the sendto menu. just delete any links that we owned before. _DeleteOldFloppyLinks(pidlSendTo, ppf, psl); ILFree(pidlSendTo); } ppf->Release(); } psl->Release(); } } static const struct { PCWSTR pszExt; } _DeleteSendToList[] = { // make sure these extensions are definitely owned by us, since we're deleting all of them. { L".cdburn" }, // clean up after XP beta2 upgrade { L".publishwizard" } // clean up after XP beta1 upgrade }; void _DeleteSendToEntries() { TCHAR szSendTo[MAX_PATH]; if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_SENDTO, NULL, 0, szSendTo))) { for (int i = 0; i < ARRAYSIZE(_DeleteSendToList); i++) { TCHAR szSearch[MAX_PATH]; StrCpyN(szSearch, szSendTo, ARRAYSIZE(szSearch)); PathAppend(szSearch, TEXT("*")); StrCatBuff(szSearch, _DeleteSendToList[i].pszExt, ARRAYSIZE(szSearch)); WIN32_FIND_DATA fd; HANDLE hfind = FindFirstFile(szSearch, &fd); if (hfind != INVALID_HANDLE_VALUE) { do { TCHAR szFile[MAX_PATH]; StrCpyN(szFile, szSendTo, ARRAYSIZE(szFile)); PathAppend(szFile, fd.cFileName); DeleteFile(szFile); } while (FindNextFile(hfind, &fd)); FindClose(hfind); } } } // next kill old removable drives _DeleteOldRemovableLinks(); } DWORD _GetProcessorSpeed() // in MHz { static DWORD s_dwSpeed = 0; if (s_dwSpeed == 0) { DWORD cb = sizeof(s_dwSpeed); SHGetValue(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), TEXT("~MHz"), NULL, &s_dwSpeed, &cb); s_dwSpeed += 1; // fudge factor, my 400 Mhz machine reports 399 } return s_dwSpeed; } DWORD _GetPhysicalMemory() // in MBs { NTSTATUS Status; SYSTEM_BASIC_INFORMATION BasicInfo; Status = NtQuerySystemInformation( SystemBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL ); if (NT_SUCCESS(Status)) { return ((BasicInfo.NumberOfPhysicalPages * BasicInfo.PageSize) / (1024 * 1024)) + 1; // fudge factor, my 256 Meg machine reports 255 } else { return 64; // Default to 64 Meg (something lame, so that we turn a bunch of stuff off) } } const TCHAR g_cszLetters[] = TEXT("The Quick Brown Fox Jumped Over The Lazy Dog"); BOOL _PerfTestSmoothFonts(void) { int cchLength = lstrlen(g_cszLetters); HDC hdc; LOGFONT lf; HFONT hfont; HFONT hfontOld; int bkmodeOld; int iIter; int iIter2; int iIter3; LARGE_INTEGER liFrequency; LARGE_INTEGER liStart; LARGE_INTEGER liStop; LARGE_INTEGER liTotal; COLORREF colorref; SIZE size; DOUBLE eTime[2]; BYTE lfQuality[2]; HDC hdcScreen = GetDC(NULL); hdc = CreateCompatibleDC(hdcScreen); HBITMAP hbm = CreateCompatibleBitmap(hdcScreen,200,20); HBITMAP hbmOld = (HBITMAP)SelectObject(hdc,hbm); ReleaseDC(NULL,hdcScreen); bkmodeOld = SetBkMode(hdc, TRANSPARENT); QueryPerformanceFrequency( &liFrequency ); lfQuality[0] = NONANTIALIASED_QUALITY; lfQuality[1] = ANTIALIASED_QUALITY; memset(&lf,0,sizeof(lf)); lstrcpy(lf.lfFaceName,TEXT("Arial")); lf.lfWeight = FW_BOLD; for (iIter3 = 0; iIter3 < 2; iIter3++) { liTotal.QuadPart = 0; for (iIter2 = 0; iIter2 < 5; iIter2++) { // // First, Flush the constructed font cache // for (iIter = 0; iIter < 64; iIter++) { lf.lfHeight = -14-iIter; // 10+ pt lf.lfQuality = NONANTIALIASED_QUALITY; hfont = CreateFontIndirect(&lf); hfontOld = (HFONT)SelectObject(hdc, hfont); TextOut(hdc, 0, 0, g_cszLetters, cchLength); SelectObject(hdc, hfontOld); DeleteObject(hfont); } GdiFlush(); colorref = GetPixel(hdc,0,0); // // Now measure how long it takes to construct and use a this font // lf.lfHeight = -13; // 10 pt lf.lfQuality = lfQuality[iIter3]; QueryPerformanceCounter( &liStart ); hfont = CreateFontIndirect(&lf); hfontOld = (HFONT)SelectObject(hdc, hfont); for (iIter = 0; iIter < 10; iIter++) { TextOut(hdc, 0, 0, g_cszLetters, cchLength); } GdiFlush(); colorref = GetPixel(hdc,0,0); QueryPerformanceCounter( &liStop ); liTotal.QuadPart += liStop.QuadPart - liStart.QuadPart; GetTextExtentPoint(hdc, g_cszLetters, cchLength, &size); SelectObject(hdc, hfontOld); DeleteObject(hfont); } eTime[iIter3] = (double)liTotal.QuadPart / (double)liFrequency.QuadPart; } SetBkMode(hdc, bkmodeOld); SelectObject(hdc,hbmOld); DeleteObject(hbm); DeleteDC(hdc); return (eTime[1]/eTime[0] <= 4.0); } BOOL _PerfTestAlphaLayer(void) { DOUBLE eTime = 100.0; // 100 is too large to enable features int cx = 200; int cy = 500; LARGE_INTEGER liFrequency; QueryPerformanceFrequency( &liFrequency ); BITMAPINFO bmi; memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = cx; bmi.bmiHeader.biHeight = cy; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; // // Create the image we want to alpha blend onto the screen // HDC hdcScreen = GetDC(NULL); HDC hdcImage = CreateCompatibleDC(NULL); if (hdcImage != NULL) { PVOID pbits; HBITMAP hbmImage = CreateDIBSection(hdcImage, &bmi, DIB_RGB_COLORS, &pbits, NULL, NULL); if (hbmImage != NULL) { HBITMAP hbmOld = (HBITMAP)SelectObject(hdcImage, hbmImage); BitBlt(hdcImage, 0, 0, cx, cy, hdcScreen, 0, 0, SRCCOPY); if (pbits != NULL) { RGBQUAD *prgb = (RGBQUAD *)pbits; for (int y = 0; y < cy; y++) { for (int x = 0; x < cx; x++) { BYTE color_r; BYTE color_g; BYTE color_b; color_r = prgb->rgbRed; color_g = prgb->rgbBlue; color_b = prgb->rgbGreen; color_r = color_r / 2; color_g = color_g / 2; color_b = color_b / 2; prgb->rgbRed = color_r; prgb->rgbBlue = color_g; prgb->rgbGreen = color_b; prgb->rgbReserved = 0x80; prgb++; } } } HWND hwnd1 = CreateWindowEx( WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW, TEXT("Button"), TEXT("Windows XP"), WS_POPUPWINDOW, 0, 0, cx, cy, NULL, NULL, 0, NULL); HWND hwnd2 = CreateWindowEx( WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW, TEXT("Button"), TEXT("Windows XP"), WS_POPUPWINDOW, 0, 0, cx, cy, NULL, NULL, 0, NULL); if (hwnd1 != NULL && hwnd2 != NULL) { SetWindowPos(hwnd1, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); SetWindowPos(hwnd2, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); BLENDFUNCTION blend; blend.BlendOp = AC_SRC_OVER; blend.BlendFlags = 0; blend.SourceConstantAlpha = 0xFF; blend.AlphaFormat = AC_SRC_ALPHA; HDC hdc1 = GetDC(hwnd1); HDC hdc2 = GetDC(hwnd2); POINT ptSrc; SIZE size; ptSrc.x = 0; ptSrc.y = 0; size.cx = cx; size.cy = cy; COLORREF colorref; LARGE_INTEGER liStart; LARGE_INTEGER liStop; LARGE_INTEGER liTotal; GdiFlush(); colorref = GetPixel(hdc1,0,0); colorref = GetPixel(hdc2,0,0); colorref = GetPixel(hdcScreen,0,0); QueryPerformanceCounter( &liStart ); for (int iIter = 0; iIter < 10; iIter++) { UpdateLayeredWindow(hwnd1, hdc1, NULL, &size, hdcImage, &ptSrc, 0, &blend, ULW_ALPHA); UpdateLayeredWindow(hwnd2, hdc2, NULL, &size, hdcImage, &ptSrc, 0, &blend, ULW_ALPHA); } GdiFlush(); colorref = GetPixel(hdc1,0,0); colorref = GetPixel(hdc2,0,0); colorref = GetPixel(hdcScreen,0,0); QueryPerformanceCounter( &liStop ); liTotal.QuadPart = liStop.QuadPart - liStart.QuadPart; eTime = ((DOUBLE)liTotal.QuadPart * 1000.0) / (DOUBLE)liFrequency.QuadPart; eTime = eTime / 10.0; ReleaseDC(hwnd1, hdc1); ReleaseDC(hwnd2, hdc2); } if (hwnd1) { DestroyWindow(hwnd1); } if (hwnd2) { DestroyWindow(hwnd2); } SelectObject(hdcImage,hbmOld); DeleteObject(hbmImage); } DeleteDC(hdcImage); } ReleaseDC(NULL, hdcScreen); return (eTime <= 75.0); } BOOL g_fPerfFont = FALSE; BOOL g_fPerfAlpha = FALSE; #define VISUALEFFECTS_KEY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VisualEffects") #define VISUALEFFECTS_CHECK TEXT("CheckedValue") #define VISUALEFFECTS_UNCHECK TEXT("UncheckedValue") #define VISUALEFFECTS_DEFAULT TEXT("DefaultValue") #define VISUALEFFECTS_APPLIED TEXT("DefaultApplied") #define VISUALEFFECTS_MINCPU TEXT("MinimumCPU") #define VISUALEFFECTS_MINMEM TEXT("MinimumMEM") #define VISUALEFFECTS_FONT TEXT("DefaultByFontTest") #define VISUALEFFECTS_ALPHA TEXT("DefaultByAlphaTest") #define VISUALEFFECTS_VER 1 void _ApplyDefaultVisualEffect(HKEY hkey,HKEY hkeyUser) { // // blow this off completely on TS client to avoid stepping // on TS client's toes // if (!IsOS(OS_TERMINALCLIENT)) { DWORD cb; DWORD dwDefaultApplied = 0; if (0 != hkeyUser) { cb = sizeof(dwDefaultApplied); RegQueryValueEx(hkeyUser, VISUALEFFECTS_APPLIED, NULL, NULL, (LPBYTE)&dwDefaultApplied, &cb); } // // Apply defaults only if the version number is old // if (VISUALEFFECTS_VER > dwDefaultApplied) { LPTSTR pszValue = NULL; // use the default value DWORD dwMinimumCPU = 0; DWORD dwMinimumMEM = 0; BOOL fFontTestDefault = FALSE; BOOL fAlphaTestDefault = FALSE; // // see if a minimum physical memory value is specified // cb = sizeof(dwMinimumMEM); RegQueryValueEx(hkey, VISUALEFFECTS_MINMEM, NULL, NULL, (LPBYTE)&dwMinimumMEM, &cb); // // see if a minimum CPU speed is specified // cb = sizeof(dwMinimumCPU); RegQueryValueEx(hkey, VISUALEFFECTS_MINCPU, NULL, NULL, (LPBYTE)&dwMinimumCPU, &cb); // // see if the font performance test value is needed // cb = sizeof(fFontTestDefault); RegQueryValueEx(hkey, VISUALEFFECTS_FONT, NULL, NULL, (LPBYTE)&fFontTestDefault, &cb); // // see if the alpha performance test value is needed // cb = sizeof(fAlphaTestDefault); RegQueryValueEx(hkey, VISUALEFFECTS_ALPHA, NULL, NULL, (LPBYTE)&fAlphaTestDefault, &cb); if ( dwMinimumCPU > 0 || dwMinimumMEM > 0 || fFontTestDefault || fAlphaTestDefault) { pszValue = VISUALEFFECTS_CHECK; if (_GetProcessorSpeed() < dwMinimumCPU) { pszValue = VISUALEFFECTS_UNCHECK; } if (_GetPhysicalMemory() < dwMinimumMEM) { pszValue = VISUALEFFECTS_UNCHECK; } if (fFontTestDefault && !g_fPerfFont) { pszValue = VISUALEFFECTS_UNCHECK; } if (fAlphaTestDefault && !g_fPerfAlpha) { pszValue = VISUALEFFECTS_UNCHECK; } } if (IsOS(OS_ANYSERVER)) { // // on server, we default to best performance (*everything* off) // pszValue = VISUALEFFECTS_UNCHECK; } DWORD dwValue = 0; cb = sizeof(dwValue); if (pszValue) { // // set the default according to the chosen value // RegQueryValueEx(hkey, pszValue, NULL, NULL, (LPBYTE)&dwValue, &cb); // // when figuring out settings that need to adjust the default // value the VISUALEFFECTS_DEFAULT value must be re-applied // to the per-user key. // if (0 != hkeyUser) { RegSetValueEx(hkeyUser, VISUALEFFECTS_DEFAULT, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue)); } } else { // // read in the default value // RegQueryValueEx(hkey, VISUALEFFECTS_DEFAULT, NULL, NULL, (LPBYTE)&dwValue, &cb); } // // how do we apply this setting? // DWORD uiAction; TCHAR szBuf[MAX_PATH]; if (cb = sizeof(szBuf), ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("CLSID"), NULL, NULL, (LPBYTE)&szBuf, &cb)) { // // by CLSID // CLSID clsid; GUIDFromString(szBuf, &clsid); IRegTreeItem* pti; if (SUCCEEDED(CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IRegTreeItem, &pti)))) { pti->SetCheckState(dwValue); pti->Release(); } } else if (cb = sizeof(uiAction), ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("SPIActionSet"), NULL, NULL, (LPBYTE)&uiAction, &cb)) { // // by SPI // SHBoolSystemParametersInfo(uiAction, &dwValue); } else if (cb = sizeof(szBuf), ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("RegPath"), NULL, NULL, (LPBYTE)&szBuf, &cb)) { // // by reg key // TCHAR szValueName[96]; cb = sizeof(szValueName); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("ValueName"), NULL, NULL, (LPBYTE)&szValueName, &cb)) { SHSetValue(HKEY_CURRENT_USER, szBuf, szValueName, REG_DWORD, &dwValue, sizeof(dwValue)); } } if (0 != hkeyUser) { dwDefaultApplied = VISUALEFFECTS_VER; RegSetValueEx(hkeyUser, VISUALEFFECTS_APPLIED, 0, REG_DWORD, (LPBYTE)&dwDefaultApplied, sizeof(dwDefaultApplied)); } } } } void _DefaultVisualEffects(void) { HKEY hkeyUser; DWORD dw; g_fPerfFont = _PerfTestSmoothFonts(); g_fPerfAlpha = _PerfTestAlphaLayer(); if (ERROR_SUCCESS == RegCreateKeyExW(HKEY_CURRENT_USER, VISUALEFFECTS_KEY, 0, TEXTW(""), 0, KEY_SET_VALUE, NULL, &hkeyUser, &dw)) { HKEY hkey; REGSAM samDesired = KEY_READ; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, VISUALEFFECTS_KEY, 0, samDesired, &hkey)) { TCHAR szName[96]; for (int i = 0; ERROR_SUCCESS == RegEnumKey(hkey, i, szName, ARRAYSIZE(szName)); i++) { HKEY hkeyUserItem; if (ERROR_SUCCESS == RegCreateKeyExW(hkeyUser, szName, 0, TEXTW(""), 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hkeyUserItem, &dw)) { HKEY hkeyItem; if (ERROR_SUCCESS == RegOpenKeyEx(hkey, szName, 0, samDesired, &hkeyItem)) { // only apply the default for the setting if the "NoApplyDefault" reg value is NOT present if (RegQueryValueEx(hkeyItem, TEXT("NoApplyDefault"), NULL, NULL, NULL, NULL) != ERROR_SUCCESS) { _ApplyDefaultVisualEffect(hkeyItem, hkeyUserItem); } RegCloseKey(hkeyItem); } RegCloseKey(hkeyUserItem); } } RegCloseKey(hkey); } RegCloseKey(hkeyUser); } } STDAPI CDeskHtmlProp_RegUnReg(BOOL bInstall); STDAPI_(BOOL) ApplyRegistrySecurity(); STDAPI_(void) FixPlusIcons(); STDAPI_(void) CleanupFileSystem(); STDAPI_(void) InitializeSharedDocs(BOOL fOnWow64); #define KEEP_FAILURE(hrSum, hrLast) if (FAILED(hrLast)) hrSum = hrLast; #ifdef _WIN64 // On IA64 machines we need to copy the EULA file (eula.txt) from %SytemRoot%\System32, to // %SystemRoot%\SysWOW64. This is needed because when you do help|about on a 32-bit app running // under wow64 it will look in the syswow64 directory for the file. // // Why is shell32.dll doing this and not setup you might ask? The EULA has to be installed by textmode // since it is unsigned (and changes for every sku). Since txtmode setup does a MOVE instead of a copy, we // cannot have it installed into two places. Thus the easies thing to do is to simply copy the file // from the System32 directory to the SysWOW64 directory here. Sigh. BOOL CopyEULAForWow6432() { BOOL bRet = FALSE; TCHAR szEULAPath[MAX_PATH]; TCHAR szWow6432EULAPath[MAX_PATH]; if (GetSystemWindowsDirectory(szEULAPath, ARRAYSIZE(szEULAPath))) { lstrcpyn(szWow6432EULAPath, szEULAPath, ARRAYSIZE(szWow6432EULAPath)); if (PathAppend(szEULAPath, TEXT("System32\\eula.txt")) && PathAppend(szWow6432EULAPath, TEXT("SysWOW64\\eula.txt"))) { // now we have the source (%SystemRoot%\System32\eula.txt) and dest (%SystemRoot%\SysWOW64\eula.txt) // paths, lets do the copy! bRet = CopyFile(szEULAPath, szWow6432EULAPath, FALSE); } } return bRet; } #endif // _WIN64 // // Upgrades from Win9x are internally handled by setup as "clean" installs followed by some // migration of settings. So, the following function does the job of truly detecting a win9x // upgrade. // The way it detects: Look in %windir%\system32\$winnt$.inf in the section [Data] // for Win9xUpgrade=Yes if it is a Win9x upgrade. // BOOL IsUpgradeFromWin9x() { TCHAR szFilePath[MAX_PATH]; TCHAR szYesOrNo[10]; GetSystemDirectory(szFilePath, ARRAYSIZE(szFilePath)); PathAppend(szFilePath, TEXT("$WINNT$.INF")); GetPrivateProfileString(TEXT("Data"), // Section name. TEXT("Win9xUpgrade"), // Key name. TEXT("No"), // Default string, if key is missing. szYesOrNo, ARRAYSIZE(szYesOrNo), szFilePath); // Full path to "$winnt$.inf" file. return (0 == lstrcmpi(szYesOrNo, TEXT("Yes"))); } BOOL IsCleanInstallInProgress() { LPCTSTR szKeyName = TEXT("SYSTEM\\Setup"); HKEY hKeySetup; BOOL fCleanInstall = FALSE; if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_READ, &hKeySetup) == ERROR_SUCCESS) { DWORD dwSize; LONG lResult; DWORD dwSystemSetupInProgress = 0; dwSize = sizeof(DWORD); lResult = RegQueryValueEx (hKeySetup, TEXT("SystemSetupInProgress"), NULL, NULL, (LPBYTE) &dwSystemSetupInProgress, &dwSize); if (lResult == ERROR_SUCCESS) { DWORD dwMiniSetupInProgress = 0; dwSize = sizeof(DWORD); RegQueryValueEx (hKeySetup, TEXT("MiniSetupInProgress"), NULL, NULL, (LPBYTE) &dwMiniSetupInProgress, &dwSize); if(dwSystemSetupInProgress && !dwMiniSetupInProgress) { DWORD dwUpgradeInProgress = 0; dwSize = sizeof(DWORD); //Setup is in progress and MiniSetup is NOT in progress. //That means that we are in the GUI mode of setup! //On clean installs, this value won't be there and the following call will fail. RegQueryValueEx (hKeySetup, TEXT("UpgradeInProgress"), NULL, NULL, (LPBYTE) &dwUpgradeInProgress, &dwSize); fCleanInstall = !dwUpgradeInProgress; } } RegCloseKey (hKeySetup); } if(fCleanInstall) { // Caution: An upgrade from Win9x is internally done by setup as a "clean" install. // So, we need to figure out if this is really a clean install or an upgrade from // win9x. fCleanInstall = !IsUpgradeFromWin9x(); } return fCleanInstall ; } // // Set the default MFU to be picked up when each user logs on for the // first time. The string block must be 16 strings long (_0 through _15). // STDAPI SetDefaultMFU(UINT ids) { HKEY hkMFU; LONG lRc = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\SMDEn"), 0, NULL, 0, KEY_WRITE, NULL, &hkMFU, NULL); if (lRc == ERROR_SUCCESS) { for (int i = 0; lRc == ERROR_SUCCESS && i < 16; i++) { TCHAR szValue[80]; TCHAR szData[80]; wnsprintf(szValue, ARRAYSIZE(szValue), TEXT("Link%d"), i); wnsprintf(szData, ARRAYSIZE(szData), TEXT("@xpsp1res.dll,-%d"), ids + i); DWORD cbData = (lstrlen(szData) + 1) * sizeof(TCHAR); lRc = RegSetValueEx(hkMFU, szValue, 0, REG_SZ, (LPBYTE)szData, cbData); } RegCloseKey(hkMFU); } return HRESULT_FROM_WIN32(lRc); } STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine) { HRESULT hrTemp, hr = S_OK; // 99/05/03 vtan: If you're reading this section then you are considering // adding code to the registration/installation of shell32.dll. There are // now 3 schemes to accomplish this task: // 1. IE4UINIT.EXE // Check HKLM\Software\Microsoft\Active Setup\Installed Components\{89820200-ECBD-11cf-8B85-00AA005B4383} // This says that if there is a new version of IE5 to launch ie4uinit.exe. // You can add code to this executable if you wish. You need to enlist in // the setup project on \\trango\slmadd using "ieenlist setup" // 2. REGSVR32.EXE /n /i:U shell32.dll // Check HKLM\Software\Microsoft\Active Setup\Installed Components\{89820200-ECBD-11cf-8B85-00AA005B4340} // This is executed using the same scheme as IE4UINIT.EXE except that the // executable used is regsvr32.exe with a command line passed to // shell32!DllInstall. Add your code in the section for "U" below. // If you put the code in the section which is NOT "U" then your // code is executed at GUI setup and any changes you make to HKCU // go into the default user (template). Be careful when putting // things here as winlogon.exe (or some other process) may put // your changes into a user profile unconditionally. // 3. HIVEUSD.INX // Checks NT build numbers and does command based on the previous build // number and the current build number only executing the changes between // the builds. If you wish to add something using this method, currently // you have to enlist in the setup project on \\rastaman\ntwin using // "enlist -fgs \\rastaman\ntwin -p setup". To find hiveusd.inx go to // nt\private\setup\inf\win4\inf. Add the build number which the delta // is required and a command to launch %SystemRoot%\System32\shmgrate.exe // with one or two parameters. The first parameter tells what command to // execute. The second parameter is optional. shmgrate.exe then finds // shell32.dll and calls shell32!FirstUserLogon. Code here is for upgrading // HKCU user profiles from one NT build to another NT build. // Code is executed in the process context shmgrate.exe and is executed // at a time where no UI is possible. Always use HKLM\Software\Microsoft // \Windows NT\CurrentVersion\Image File Execution Options\Debugger with // "-d". // Schemes 1 and 2 work on either Win9x or WinNT but have the sometimes // unwanted side effect of ALWAYS getting executed on version upgrades. // Scheme 3 only gets executed on build number deltas. Because schemes 1 // and 2 are always executed, if a user changes (or deletes) that setting // it will always get put back. Not so with scheme 3. // Ideally, the best solution is have an internal shell32 build number // delta scheme which determines the from build and the to build and does // a similar mechanism to what hiveusd.inx and shmgrate.exe do. This // would probably involve either a common installation function (such as // FirstUserLogon()) which is called differently from Win9x and WinNT or // common functions to do the upgrade and two entry points (such as // FirstUserLogonNT() and FirstUserLogonWin9X(). if (bInstall) { NT_PRODUCT_TYPE type = NtProductWinNt; RtlGetNtProductType(&type); // "U" means it's the per user install call BOOL fPerUser = pszCmdLine && (StrCmpIW(pszCmdLine, L"U") == 0); if (fPerUser) { // NOTE: Code in this segment get run during first login. We want first // login to be as quick as possible so try to minimize this section. // Put per-user install stuff here. Any HKCU registration // done here is suspect. (If you are setting defaults, do // so in HKLM and use the SHRegXXXUSValue functions.) // WARNING: we get called by the ie4unit.exe (ieunit.inf) scheme: // %11%\shell32.dll,NI,U // this happens per user, to test this code "regsvr32 /n /i:U shell32.dll" // Some of this stuff we don't want to do on all upgrades (in particular XP Gold -> XP SPn) BOOL fUpgradeUserSettings = TRUE; WCHAR szVersion[50]; // plenty big for aaaa,bbbb,cccc,dddd DWORD cbVersion = sizeof(szVersion); if (ERROR_SUCCESS==SHGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Active Setup\\Installed Components\\{89820200-ECBD-11cf-8B85-00AA005B4340}",L"Version", // guid is Shell32's Active Setup NULL, szVersion, &cbVersion)) { __int64 nverUpgradeFrom; __int64 nverWinXP; if (SUCCEEDED(GetVersionFromString64(szVersion, &nverUpgradeFrom)) && SUCCEEDED(GetVersionFromString64(L"6,0,2600,0000", &nverWinXP))) { fUpgradeUserSettings = nverUpgradeFrom < nverWinXP; } } #ifdef INSTALL_MSI // Install the Office WebFolders shell namespace extension per user. MSI_Install(TEXT("webfldrs.msi")); #endif hrTemp = CreateShowDesktopOnQuickLaunch(); KEEP_FAILURE(hrTemp, hr); // upgrade the recent folder. WCHAR sz[MAX_PATH]; SHGetFolderPath(NULL, CSIDL_RECENT | CSIDL_FLAG_CREATE | CSIDL_FLAG_PER_USER_INIT, NULL, SHGFP_TYPE_CURRENT, sz); SHGetFolderPath(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE | CSIDL_FLAG_PER_USER_INIT, NULL, SHGFP_TYPE_CURRENT, sz); // torch this value on upgrade. // insuring the warning that they will see the ugly desktop.ini files if (fUpgradeUserSettings) { SKDeleteValue(SHELLKEY_HKCU_EXPLORER, L"Advanced", L"ShowSuperHidden"); } HKEY hk = SHGetShellKey(SHELLKEY_HKCULM_MUICACHE, NULL, FALSE); if (hk) { SHDeleteKeyA(hk, NULL); RegCloseKey(hk); } // delete old sendto entries we dont want any more _DeleteSendToEntries(); // handle the per user init for this guy _DoMyDocsPerUserInit(); _DefaultVisualEffects(); // handle the per user change of value for NoDriveAutoRun if (!IsOS(OS_ANYSERVER)) { _NoDriveAutorunTweak(); } } else { // Delete any old registration entries, then add the new ones. // Keep ADVPACK.DLL loaded across multiple calls to RegInstall. // (The inf engine doesn't guarantee DelReg/AddReg order, that's // why we explicitly unreg and reg here.) // hrTemp = CallRegInstall("RegDll"); KEEP_FAILURE(hrTemp, hr); // I suppose we should call out NT-only registrations, just in case // we ever have to ship a win9x based shell again hrTemp = CallRegInstall("RegDllNT"); KEEP_FAILURE(hrTemp, hr); // If we are on NT server, do additional stuff if (type != NtProductWinNt) { hrTemp = CallRegInstall("RegDllNTServer"); KEEP_FAILURE(hrTemp, hr); } else // workstation { if (!IsOS(OS_PERSONAL)) { hrTemp = CallRegInstall("RegDllNTPro"); KEEP_FAILURE(hrTemp, hr); // // NTRAID#NTBUG9-418621-2001/06/27-jeffreys // // If the ForceGuest value is unset, e.g. on upgrade // from Win2k, set the SimpleSharing/DefaultValue to 0. // DWORD dwForceGuest; DWORD cb = sizeof(dwForceGuest); if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Lsa"), TEXT("ForceGuest"), NULL, &dwForceGuest, &cb)) { dwForceGuest = 0; SHSetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\Folder\\SimpleSharing"), TEXT("DefaultValue"), REG_DWORD, &dwForceGuest, sizeof(dwForceGuest)); } } // Service Pack is not allowed to change selfreg.inf // so we need to do this manually. Must do this after // RegDll so we can overwrite his settings. #ifdef _WIN64 hrTemp = SetDefaultMFU(IDS_SHELL32_PROMFU64_0); #else hrTemp = SetDefaultMFU(IDS_SHELL32_PROMFU32_0); #endif KEEP_FAILURE(hrTemp, hr); } // // If this is clean install, then hide some desktop icons. // if(IsCleanInstallInProgress()) { hrTemp = CallRegInstall("RegHideDeskIcons"); KEEP_FAILURE(hrTemp, hr); } // This is apparently the only way to get setup to remove all the registry backup // for old names no longer in use... hrTemp = CallRegInstall("CleanupOldRollback1"); KEEP_FAILURE(hrTemp, hr); hrTemp = CallRegInstall("CleanupOldRollback2"); KEEP_FAILURE(hrTemp, hr); // REVIEW (ToddB): Move this to DllRegisterServer. hrTemp = Shell32RegTypeLib(); KEEP_FAILURE(hrTemp, hr); ApplyRegistrySecurity(); FixPlusIcons(); // Filesystem stuff should be done only on the native platform // (don't do it when in the emulator) since there is only one // filesystem. Otherwise the 32-bit version writes 32-bit goo // into the filesystem that the 64-bit shell32 can't handle if (!IsOS(OS_WOW6432)) { CleanupFileSystem(); } #ifdef _WIN64 // this will copy eula.txt to the %SystemRoot%\SysWOW64 directory for // 32-bit apps running on IA64 under emulation CopyEULAForWow6432(); #endif // Initialize the shared documents objects InitializeSharedDocs(IsOS(OS_WOW6432)); DoFusion(); } } else { // We only need one unreg call since all our sections share // the same backup information hrTemp = CallRegInstall("UnregDll"); KEEP_FAILURE(hrTemp, hr); UnregisterTypeLibrary(&LIBID_Shell32); } CDeskHtmlProp_RegUnReg(bInstall); return hr; } STDAPI DllRegisterServer(void) { // NT5 setup calls this so it is now safe to put code here. return S_OK; } STDAPI DllUnregisterServer(void) { return S_OK; } BOOL CopyRegistryValues (HKEY hKeyBaseSource, LPCTSTR pszSource, HKEY hKeyBaseTarget, LPCTSTR pszTarget) { DWORD dwDisposition, dwMaxValueNameSize, dwMaxValueDataSize; HKEY hKeySource, hKeyTarget; BOOL fSuccess = FALSE; //Assume error! hKeySource = hKeyTarget = NULL; if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyBaseSource, pszSource, 0, KEY_READ, &hKeySource)) && (ERROR_SUCCESS == RegCreateKeyEx(hKeyBaseTarget, pszTarget, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyTarget, &dwDisposition)) && (ERROR_SUCCESS == RegQueryInfoKey(hKeySource, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwMaxValueNameSize, &dwMaxValueDataSize, NULL, NULL))) { TCHAR *pszValueName; void *pValueData; pszValueName = reinterpret_cast(LocalAlloc(LMEM_FIXED, ++dwMaxValueNameSize * sizeof(TCHAR))); if (pszValueName != NULL) { pValueData = LocalAlloc(LMEM_FIXED, dwMaxValueDataSize); if (pValueData != NULL) { DWORD dwIndex, dwType, dwValueNameSize, dwValueDataSize; dwIndex = 0; dwValueNameSize = dwMaxValueNameSize; dwValueDataSize = dwMaxValueDataSize; while (ERROR_SUCCESS == RegEnumValue(hKeySource, dwIndex, pszValueName, &dwValueNameSize, NULL, &dwType, reinterpret_cast(pValueData), &dwValueDataSize)) { RegSetValueEx(hKeyTarget, pszValueName, 0, dwType, reinterpret_cast(pValueData), dwValueDataSize); ++dwIndex; dwValueNameSize = dwMaxValueNameSize; dwValueDataSize = dwMaxValueDataSize; } LocalFree(pValueData); fSuccess = TRUE; //Succeeded! } LocalFree(pszValueName); } } if(hKeySource) RegCloseKey(hKeySource); if(hKeyTarget) RegCloseKey(hKeyTarget); return fSuccess; } STDAPI MergeDesktopAndNormalStreams(void) { static const TCHAR scszBaseRegistryLocation[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"); static const int sciMaximumStreams = 128; static const TCHAR sccOldMRUListBase = TEXT('a'); // Upgrade from NT4 (classic shell) to Windows 2000 (integrated shell) // This involves TWO major changes and one minor change: // 1. Merging DesktopStreamMRU and StreamMRU // 2. Upgrading the MRUList to MRUListEx // 3. Leaving the old settings alone for the roaming user profile scenario // This also involves special casing the users desktop PIDL because this is // stored as an absolute path PIDL in DesktopStream and needs to be stored // in Streams\Desktop instead. // The conversion is performed in-situ and simultaneously. // 1. Open all the keys we are going to need to do the conversion. HKEY hKeyBase, hKeyDesktopStreamMRU, hKeyDesktopStreams, hKeyStreamMRU, hKeyStreams; hKeyBase = hKeyDesktopStreamMRU = hKeyDesktopStreams = hKeyStreamMRU = hKeyStreams = NULL; if ((ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, scszBaseRegistryLocation, 0, KEY_ALL_ACCESS, &hKeyBase)) && (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase, TEXT("DesktopStreamMRU"), 0, KEY_ALL_ACCESS, &hKeyDesktopStreamMRU)) && (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase, TEXT("DesktopStreams"), 0, KEY_ALL_ACCESS, &hKeyDesktopStreams)) && (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase, TEXT("StreamMRU"), 0, KEY_ALL_ACCESS, &hKeyStreamMRU)) && (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase, TEXT("Streams"), 0, KEY_ALL_ACCESS, &hKeyStreams)) && // 2. Determine whether this upgrade is needed at all. If the presence of // StreamMRU\MRUListEx is detected then stop. (ERROR_SUCCESS != RegQueryValueEx(hKeyStreamMRU, TEXT("MRUListEx"), NULL, NULL, NULL, NULL))) { DWORD *pdwMRUListEx, *pdwMRUListExBase; pdwMRUListExBase = pdwMRUListEx = reinterpret_cast(LocalAlloc(LPTR, sciMaximumStreams * sizeof(DWORD) * 2)); if (pdwMRUListEx != NULL) { DWORD dwLastFreeSlot, dwMRUListSize, dwType; TCHAR *pszMRUList, szMRUList[sciMaximumStreams]; // 3. Read the StreamMRU\MRUList, iterate thru this list // and convert as we go. dwLastFreeSlot = 0; dwMRUListSize = sizeof(szMRUList); if (ERROR_SUCCESS == RegQueryValueEx(hKeyStreamMRU, TEXT("MRUList"), NULL, &dwType, reinterpret_cast(szMRUList), &dwMRUListSize)) { pszMRUList = szMRUList; while (*pszMRUList != TEXT('\0')) { DWORD dwValueDataSize; TCHAR szValue[16]; // Read the PIDL information based on the letter in // the MRUList. szValue[0] = *pszMRUList++; szValue[1] = TEXT('\0'); if (ERROR_SUCCESS == RegQueryValueEx(hKeyStreamMRU, szValue, NULL, NULL, NULL, &dwValueDataSize)) { DWORD dwValueType; void *pValueData; pValueData = LocalAlloc(LMEM_FIXED, dwValueDataSize); if ((pValueData != NULL) && (ERROR_SUCCESS == RegQueryValueEx(hKeyStreamMRU, szValue, NULL, &dwValueType, reinterpret_cast(pValueData), &dwValueDataSize))) { // Allocate a new number in the MRUListEx for the PIDL. *pdwMRUListEx = szValue[0] - sccOldMRUListBase; wsprintf(szValue, TEXT("%d"), *pdwMRUListEx++); ++dwLastFreeSlot; RegSetValueEx(hKeyStreamMRU, szValue, NULL, dwValueType, reinterpret_cast(pValueData), dwValueDataSize); LocalFree(pValueData); } } } } // 4. Read the DesktopStreamMRU\MRUList, iterate thru this // this and append to the new MRUListEx that is being // created as well as copying both the PIDL in DesktopStreamMRU // and the view information in DesktopStreams. dwMRUListSize = sizeof(szMRUList); if (ERROR_SUCCESS == RegQueryValueEx(hKeyDesktopStreamMRU, TEXT("MRUList"), NULL, &dwType, reinterpret_cast(szMRUList), &dwMRUListSize)) { bool fConvertedEmptyPIDL; TCHAR szDesktopDirectoryPath[MAX_PATH]; fConvertedEmptyPIDL = false; SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, szDesktopDirectoryPath); pszMRUList = szMRUList; while (*pszMRUList != TEXT('\0')) { DWORD dwValueDataSize; TCHAR szSource[16]; // Read the PIDL information based on the letter in // the MRUList. szSource[0] = *pszMRUList++; szSource[1] = TEXT('\0'); if (ERROR_SUCCESS == RegQueryValueEx(hKeyDesktopStreamMRU, szSource, NULL, NULL, NULL, &dwValueDataSize)) { DWORD dwValueType; void *pValueData; pValueData = LocalAlloc(LMEM_FIXED, dwValueDataSize); if ((pValueData != NULL) && (ERROR_SUCCESS == RegQueryValueEx(hKeyDesktopStreamMRU, szSource, NULL, &dwValueType, reinterpret_cast(pValueData), &dwValueDataSize))) { TCHAR szTarget[16], szStreamPath[MAX_PATH]; if ((SHGetPathFromIDList(reinterpret_cast(pValueData), szStreamPath) != 0) && (0 == lstrcmpi(szStreamPath, szDesktopDirectoryPath))) { if (!fConvertedEmptyPIDL) { // 99/05/24 #343721 vtan: Prefer the desktop relative PIDL // (empty PIDL) when given a choice of two PIDLs that refer // to the desktop. The old absolute PIDL is from SP3 and // earlier days. The new relative PIDL is from SP4 and // later days. An upgraded SP3 -> SP4 -> SPx -> Windows // 2000 system will possibly have old absolute PIDLs. // Check for the empty PIDL. If this is encountered already // then don't process this stream. fConvertedEmptyPIDL = ILIsEmpty(reinterpret_cast(pValueData)); wsprintf(szSource, TEXT("%d"), szSource[0] - sccOldMRUListBase); CopyRegistryValues(hKeyDesktopStreams, szSource, hKeyStreams, TEXT("Desktop")); } } else { // Allocate a new number in the MRUListEx for the PIDL. *pdwMRUListEx++ = dwLastFreeSlot; wsprintf(szTarget, TEXT("%d"), dwLastFreeSlot++); if (ERROR_SUCCESS == RegSetValueEx(hKeyStreamMRU, szTarget, NULL, dwValueType, reinterpret_cast(pValueData), dwValueDataSize)) { // Copy the view information from DesktopStreams to Streams wsprintf(szSource, TEXT("%d"), szSource[0] - sccOldMRUListBase); CopyRegistryValues(hKeyDesktopStreams, szSource, hKeyStreams, szTarget); } } LocalFree(pValueData); } } } } *pdwMRUListEx++ = static_cast(-1); RegSetValueEx(hKeyStreamMRU, TEXT("MRUListEx"), NULL, REG_BINARY, reinterpret_cast(pdwMRUListExBase), ++dwLastFreeSlot * sizeof(DWORD)); LocalFree(reinterpret_cast(pdwMRUListExBase)); } } if (hKeyStreams != NULL) RegCloseKey(hKeyStreams); if (hKeyStreamMRU != NULL) RegCloseKey(hKeyStreamMRU); if (hKeyDesktopStreams != NULL) RegCloseKey(hKeyDesktopStreams); if (hKeyDesktopStreamMRU != NULL) RegCloseKey(hKeyDesktopStreamMRU); if (hKeyBase != NULL) RegCloseKey(hKeyBase); return(S_OK); } static const int s_ciMaximumNumericString = 32; int GetRegistryStringValueAsInteger (HKEY hKey, LPCTSTR pszValue, int iDefaultValue) { int iResult; DWORD dwType, dwStringSize; TCHAR szString[s_ciMaximumNumericString]; dwStringSize = sizeof(szString); if (ERROR_SUCCESS == RegQueryValueEx(hKey, pszValue, NULL, &dwType, reinterpret_cast(szString), &dwStringSize) && (dwType == REG_SZ)) { iResult = StrToInt(szString); } else { iResult = iDefaultValue; } return(iResult); } void SetRegistryIntegerAsStringValue (HKEY hKey, LPCTSTR pszValue, int iValue) { TCHAR szString[s_ciMaximumNumericString]; wnsprintf(szString, ARRAYSIZE(szString), TEXT("%d"), iValue); TW32(RegSetValueEx(hKey, pszValue, 0, REG_SZ, reinterpret_cast(szString), (lstrlen(szString) + sizeof('\0')) * sizeof(TCHAR))); } STDAPI MoveAndAdjustIconMetrics(void) { // 99/06/06 #309198 vtan: The following comes from hiveusd.inx which is // where this functionality used to be executed. It used to consist of // simple registry deletion and addition. This doesn't work on upgrade // when the user has large icons (Shell Icon Size == 48). // In this case that metric must be moved and the new values adjusted // so that the metric is preserved should the user then decide to turn // off large icons. // To restore old functionality, remove the entry in hiveusd.inx at // build 1500 which is where this function is invoked and copy the // old text back in. /* HKR,"1508\Hive\2","Action",0x00010001,3 HKR,"1508\Hive\2","KeyName",0000000000,"Control Panel\Desktop\WindowMetrics" HKR,"1508\Hive\2","Value",0000000000,"75" HKR,"1508\Hive\2","ValueName",0000000000,"IconSpacing" HKR,"1508\Hive\3","Action",0x00010001,3 HKR,"1508\Hive\3","KeyName",0000000000,"Control Panel\Desktop\WindowMetrics" HKR,"1508\Hive\3","Value",0000000000,"1" HKR,"1508\Hive\3","ValueName",0000000000,"IconTitleWrap" */ // Icon metric keys have moved from HKCU\Control Panel\Desktop\Icon* // to HKCU\Control Panel\Desktop\WindowMetrics\Icon* but only 3 values // should be moved. These are "IconSpacing", "IconTitleWrap" and // "IconVerticalSpacing". This code is executed before the deletion // entry in hiveusd.inx so that it can get the values before they // are deleted. The addition section has been remove (it's above). static const TCHAR s_cszIconSpacing[] = TEXT("IconSpacing"); static const TCHAR s_cszIconTitleWrap[] = TEXT("IconTitleWrap"); static const TCHAR s_cszIconVerticalSpacing[] = TEXT("IconVerticalSpacing"); static const int s_ciStandardOldIconSpacing = 75; static const int s_ciStandardNewIconSpacing = -1125; HKEY hKeyDesktop, hKeyWindowMetrics; hKeyDesktop = hKeyWindowMetrics = NULL; if ((ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, KEY_ALL_ACCESS, &hKeyDesktop)) && (ERROR_SUCCESS == RegOpenKeyEx(hKeyDesktop, TEXT("WindowMetrics"), 0, KEY_ALL_ACCESS, &hKeyWindowMetrics))) { int iIconSpacing, iIconTitleWrap, iIconVerticalSpacing; // 1. Read the values that we wish the move and adjust. iIconSpacing = GetRegistryStringValueAsInteger(hKeyDesktop, s_cszIconSpacing, s_ciStandardOldIconSpacing); iIconTitleWrap = GetRegistryStringValueAsInteger(hKeyDesktop, s_cszIconTitleWrap, 1); iIconVerticalSpacing = GetRegistryStringValueAsInteger(hKeyDesktop, s_cszIconVerticalSpacing, s_ciStandardOldIconSpacing); // 2. Perform the adjustment. iIconSpacing = s_ciStandardNewIconSpacing * iIconSpacing / s_ciStandardOldIconSpacing; iIconVerticalSpacing = s_ciStandardNewIconSpacing * iIconVerticalSpacing / s_ciStandardOldIconSpacing; // 3. Write the values back out in the new (moved) location. SetRegistryIntegerAsStringValue(hKeyWindowMetrics, s_cszIconSpacing, iIconSpacing); SetRegistryIntegerAsStringValue(hKeyWindowMetrics, s_cszIconTitleWrap, iIconTitleWrap); SetRegistryIntegerAsStringValue(hKeyWindowMetrics, s_cszIconVerticalSpacing, iIconVerticalSpacing); // 4. Let winlogon continue processing hiveusd.inx and delete the // old entries in the process. We already created the new entries // and that has been removed from hiveusd.inx. } if (hKeyWindowMetrics != NULL) TW32(RegCloseKey(hKeyWindowMetrics)); if (hKeyDesktop != NULL) TW32(RegCloseKey(hKeyDesktop)); return(S_OK); } STDAPI FirstUserLogon(LPCSTR pcszCommand, LPCSTR pcszOptionalArguments) { const struct { LPCSTR pcszCommand; HRESULT (WINAPI *pfn)(); } sCommands[] = { { "MergeDesktopAndNormalStreams", MergeDesktopAndNormalStreams }, { "MoveAndAdjustIconMetrics", MoveAndAdjustIconMetrics }, }; HRESULT hr = E_FAIL; // Match what shmgrate.exe passed us and execute the command. // Only use the optional argument if required. Note this is // done ANSI because the original command line is ANSI from // shmgrate.exe. for (int i = 0; i < ARRAYSIZE(sCommands); ++i) { if (lstrcmpA(pcszCommand, sCommands[i].pcszCommand) == 0) { hr = sCommands[i].pfn(); break; } } return hr; } #ifdef INSTALL_MSI // // WebFolders namespace extension installation. // This is the code that initially installs the Office WebFolders // shell namespace extension on the computer. Code in shmgrate.exe // (see private\windows\shell\migrate) performs per-user // web folders registration duties. // typedef UINT (WINAPI * PFNMSIINSTALLPRODUCT)(LPCTSTR, LPCTSTR); typedef INSTALLUILEVEL (WINAPI * PFNMSISETINTERNALUI)(INSTALLUILEVEL, HWND *); #define GETPROC(var, hmod, ptype, fn) ptype var = (ptype)GetProcAddress(hmod, fn) #define API_MSISETINTERNALUI "MsiSetInternalUI" #ifdef UNICODE # define API_MSIINSTALLPRODUCT "MsiInstallProductW" #else # define API_MSIINSTALLPRODUCT "MsiInstallProductA" #endif void MSI_Install(LPTSTR pszMSIFile) { HMODULE hmod = LoadLibrary(TEXT("msi.dll")); if (hmod) { BOOL bFreeLib = FALSE; GETPROC(pfnMsiSetInternalUI, hmod, PFNMSISETINTERNALUI, API_MSISETINTERNALUI); GETPROC(pfnMsiInstallProduct, hmod, PFNMSIINSTALLPRODUCT, API_MSIINSTALLPRODUCT); if (pfnMsiSetInternalUI && pfnMsiInstallProduct) { TCHAR szPath[MAX_PATH]; GetSystemDirectory(szPath, ARRAYSIZE(szPath)); PathAppend(szPath, pszMSIFile); // // Use "silent" install mode. No UI. // INSTALLUILEVEL oldUILevel = pfnMsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); // Install the MSI package. // Old code did this polling loop on a background thread, but that doesn't // work since we're called at DllInstall time, and the process will // go away before the install completes. Must do it all on this thread. // Wait a little bit before re-trying. UINT uRet = 0; do { uRet = pfnMsiInstallProduct(szPath, TEXT("")); } while ((uRet == ERROR_INSTALL_ALREADY_RUNNING) && (Sleep(100),TRUE)); pfnMsiSetInternalUI(oldUILevel, NULL); } if (bFreeLib) FreeLibrary(hmod); } } #endif // INSTALL_MSI // now is the time on sprockets when we lock down the registry STDAPI_(BOOL) ApplyRegistrySecurity() { BOOL fSuccess = FALSE; // assume failure SHELL_USER_PERMISSION supEveryone; SHELL_USER_PERMISSION supSystem; SHELL_USER_PERMISSION supAdministrators; PSHELL_USER_PERMISSION aPerms[3] = {&supEveryone, &supSystem, &supAdministrators}; // we want the "Everyone" to have read access supEveryone.susID = susEveryone; supEveryone.dwAccessType = ACCESS_ALLOWED_ACE_TYPE; supEveryone.dwAccessMask = KEY_READ; supEveryone.fInherit = TRUE; supEveryone.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); supEveryone.dwInheritAccessMask = GENERIC_READ; // we want the "SYSTEM" to have full control supSystem.susID = susSystem; supSystem.dwAccessType = ACCESS_ALLOWED_ACE_TYPE; supSystem.dwAccessMask = KEY_ALL_ACCESS; supSystem.fInherit = TRUE; supSystem.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); supSystem.dwInheritAccessMask = GENERIC_ALL; // we want the "Administrators" to have full control supAdministrators.susID = susAdministrators; supAdministrators.dwAccessType = ACCESS_ALLOWED_ACE_TYPE; supAdministrators.dwAccessMask = KEY_ALL_ACCESS; supAdministrators.fInherit = TRUE; supAdministrators.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); supAdministrators.dwInheritAccessMask = GENERIC_ALL; SECURITY_DESCRIPTOR* psd = GetShellSecurityDescriptor(aPerms, ARRAYSIZE(aPerms)); if (psd) { HKEY hkLMBitBucket; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket"), 0, KEY_ALL_ACCESS, &hkLMBitBucket) == ERROR_SUCCESS) { if (RegSetKeySecurity(hkLMBitBucket, DACL_SECURITY_INFORMATION, psd) == ERROR_SUCCESS) { // victory is mine! fSuccess = TRUE; } RegCloseKey(hkLMBitBucket); } LocalFree(psd); } return fSuccess; } CComModule _Module; BEGIN_OBJECT_MAP(ObjectMap) // nothing in here, use clsobj.c class table instead END_OBJECT_MAP() // ATL DllMain, needed to support our ATL classes that depend on _Module // REVIEW: confirm that _Module is really needed STDAPI_(BOOL) ATL_DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { _Module.Init(ObjectMap, hInstance); } else if (dwReason == DLL_PROCESS_DETACH) { _Module.Term(); } return TRUE; // ok } #define FUSION_FLAG_SYSTEM32 0 #define FUSION_FLAG_WINDOWS 1 #define FUSION_FLAG_PROGRAMFILES 2 #define FUSION_ENTRY(x, z) {L#x, NULL, z}, #define FUSION_ENTRY_DEST(x, y, z) {L#x, L#y, z}, struct { PWSTR pszAppName; PWSTR pszDestination; DWORD dwFlags; } g_FusionizedApps[] = { FUSION_ENTRY(ncpa.cpl, FUSION_FLAG_SYSTEM32) FUSION_ENTRY(nwc.cpl, FUSION_FLAG_SYSTEM32) FUSION_ENTRY(sapi.cpl, FUSION_FLAG_SYSTEM32) FUSION_ENTRY(wuaucpl.cpl, FUSION_FLAG_SYSTEM32) FUSION_ENTRY(cdplayer.exe, FUSION_FLAG_SYSTEM32) FUSION_ENTRY_DEST(msimn.exe, "OutLook Express", FUSION_FLAG_PROGRAMFILES) // WARNING: Do NOT add iexplorer or Explorer.exe! This will cause all apps to get fusioned; which is bad, mkay? }; void DoFusion() { TCHAR szManifest[MAX_PATH]; // We will however generate a manifest for other apps for (int i = 0; i < ARRAYSIZE(g_FusionizedApps); i++) { switch(g_FusionizedApps[i].dwFlags) { case FUSION_FLAG_SYSTEM32: GetSystemDirectory(szManifest, ARRAYSIZE(szManifest)); break; case FUSION_FLAG_WINDOWS: GetWindowsDirectory(szManifest, ARRAYSIZE(szManifest)); break; case FUSION_FLAG_PROGRAMFILES: SHGetSpecialFolderPath(NULL, szManifest, CSIDL_PROGRAM_FILES, FALSE); PathCombine(szManifest, szManifest, g_FusionizedApps[i].pszDestination); break; } PathCombine(szManifest, szManifest, g_FusionizedApps[i].pszAppName); StrCatBuff(szManifest, TEXT(".manifest"), ARRAYSIZE(szManifest)); SHSquirtManifest(HINST_THISDLL, IDS_EXPLORERMANIFEST, szManifest); } SHGetManifest(szManifest, ARRAYSIZE(szManifest)); SHSquirtManifest(HINST_THISDLL, IDS_EXPLORERMANIFEST, szManifest); }