930 lines
36 KiB
C++
930 lines
36 KiB
C++
/*
|
|
* dll.c
|
|
|
|
* Standard DLL entry-point functions
|
|
*/
|
|
|
|
#include "shellprv.h"
|
|
|
|
#include <ntverp.h>
|
|
#include <advpub.h> // For REGINSTALL
|
|
#include "fstreex.h"
|
|
#include "ids.h"
|
|
|
|
#ifdef WINNT
|
|
#define INSTALL_WEBFOLDERS 1
|
|
#include <msi.h> // For OfficeWebFolders_Install()
|
|
void OfficeWebFolders_Install(void);
|
|
#endif // WINNT
|
|
|
|
extern "C" STDAPI_(void) Control_FillCache_RunDLL( HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow );
|
|
|
|
// 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 },
|
|
#ifdef WINNT
|
|
{ "25", "%SystemRoot%" },
|
|
{ "11", "%SystemRoot%\\system32" },
|
|
#endif
|
|
};
|
|
STRTABLE stReg = { ARRAYSIZE(seReg), seReg };
|
|
|
|
// Get the location of shdocvw.dll
|
|
#ifdef WINNT
|
|
lstrcpyA(szShdocvwPath, "%SystemRoot%\\system32");
|
|
#else
|
|
GetSystemDirectory(szShdocvwPath, SIZECHARS(szShdocvwPath));
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
STDAPI Favorites_Install(BOOL bInstall);
|
|
STDAPI RecentDocs_Install(BOOL bInstall);
|
|
STDAPI CDeskHtmlProp_RegUnReg(BOOL bInstall);
|
|
#ifdef WINNT
|
|
STDAPI_(BOOL) ApplyRegistrySecurity();
|
|
#endif
|
|
STDAPI_(void) FixPlusIcons();
|
|
STDAPI_(void) CleanupFileSystem();
|
|
|
|
#define KEEP_FAILURE(hrSum, hrLast) if (FAILED(hrLast)) hrSum = hrLast;
|
|
|
|
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)
|
|
{
|
|
#ifdef WINNT
|
|
NT_PRODUCT_TYPE type = NtProductWinNt;
|
|
RtlGetNtProductType(&type);
|
|
#endif
|
|
|
|
// "U" means it's the per user install call
|
|
if (!StrCmpIW(pszCmdLine, L"U"))
|
|
{
|
|
// 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"
|
|
|
|
#ifdef INSTALL_WEBFOLDERS
|
|
// Install the Office WebFolders shell namespace extension per user.
|
|
OfficeWebFolders_Install();
|
|
#endif
|
|
|
|
// do the work to Install/Uninstall the favorites directory shellext...
|
|
hrTemp = Favorites_Install(bInstall);
|
|
KEEP_FAILURE(hrTemp, hr);
|
|
|
|
// do the work to Install/Uninstall the Recent Docs Folder
|
|
hrTemp = RecentDocs_Install(bInstall);
|
|
KEEP_FAILURE(hrTemp, hr);
|
|
|
|
hrTemp = CreateShowDesktopOnQuickLaunch();
|
|
KEEP_FAILURE(hrTemp, hr);
|
|
|
|
// populate the control panel cache
|
|
Control_FillCache_RunDLL(NULL, NULL, NULL, SW_HIDE);
|
|
|
|
}
|
|
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);
|
|
|
|
#ifdef WINNT
|
|
// 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);
|
|
}
|
|
#endif
|
|
|
|
// 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);
|
|
#ifdef WINNT
|
|
ApplyRegistrySecurity();
|
|
#endif
|
|
FixPlusIcons();
|
|
CleanupFileSystem();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
// Migration/Upgrade functions put here for want of a better place
|
|
// See DllInstall() for an explanation of usage.
|
|
|
|
BOOL Clone (LPCTSTR pszSourcePath, LPCTSTR pszTargetPath, LPCTSTR pszName)
|
|
|
|
{
|
|
TCHAR szSourcePath[MAX_PATH], szTargetPath[MAX_PATH];
|
|
|
|
lstrcpy(szSourcePath, pszSourcePath);
|
|
lstrcpy(szTargetPath, pszTargetPath);
|
|
PathAppend(szSourcePath, pszName);
|
|
PathAppend(szTargetPath, pszName);
|
|
return(CopyFile(szSourcePath, szTargetPath, TRUE));
|
|
}
|
|
|
|
STDAPI CopySampleJPG (void)
|
|
|
|
{
|
|
TCHAR szCurrentUserMyPicturesPath[MAX_PATH],
|
|
szDefaultUserMyPicturesPath[MAX_PATH];
|
|
|
|
// Here's the logic for copying the "Sample.jpg" file on upgrade.
|
|
|
|
// 1. Get the CSIDL_MYPICUTRES path by using shell32!SHGetFolderPath passing (HANDLE)-1
|
|
// as the hToken meaning "Default User".
|
|
// 2. Copy the file
|
|
|
|
// mydocs!PerUserInit is invoked to setup the desktop.ini and do all the other work
|
|
// required to make this correct.
|
|
|
|
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, szCurrentUserMyPicturesPath)) &&
|
|
SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYPICTURES, (HANDLE)-1, SHGFP_TYPE_CURRENT, szDefaultUserMyPicturesPath)))
|
|
{
|
|
TCHAR szMyDocsDLLPath[MAX_PATH];
|
|
|
|
Clone(szDefaultUserMyPicturesPath, szCurrentUserMyPicturesPath, TEXT("Sample.jpg"));
|
|
if (GetSystemDirectory(szMyDocsDLLPath, ARRAYSIZE(szMyDocsDLLPath)) != 0)
|
|
{
|
|
HINSTANCE hInstMyDocs;
|
|
|
|
PathAppend(szMyDocsDLLPath, TEXT("mydocs.dll"));
|
|
hInstMyDocs = LoadLibrary(szMyDocsDLLPath);
|
|
if (hInstMyDocs != NULL)
|
|
{
|
|
typedef void (*PFNPerUserInit) (void);
|
|
|
|
PFNPerUserInit pfnPerUserInit;
|
|
|
|
pfnPerUserInit = (PFNPerUserInit)GetProcAddress(hInstMyDocs, "PerUserInit");
|
|
if (pfnPerUserInit != NULL)
|
|
pfnPerUserInit();
|
|
FreeLibrary(hInstMyDocs);
|
|
}
|
|
}
|
|
}
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
void CopyRegistryValues (HKEY hKeyBaseSource, LPCTSTR pszSource, HKEY hKeyBaseTarget, LPCTSTR pszTarget)
|
|
{
|
|
DWORD dwDisposition, dwMaxValueNameSize, dwMaxValueDataSize;
|
|
HKEY hKeySource, hKeyTarget;
|
|
|
|
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<TCHAR*>(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<LPBYTE>(pValueData), &dwValueDataSize))
|
|
{
|
|
RegSetValueEx(hKeyTarget, pszValueName, 0, dwType, reinterpret_cast<LPBYTE>(pValueData), dwValueDataSize);
|
|
++dwIndex;
|
|
dwValueNameSize = dwMaxValueNameSize;
|
|
dwValueDataSize = dwMaxValueDataSize;
|
|
}
|
|
LocalFree(pValueData);
|
|
}
|
|
LocalFree(pszValueName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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<DWORD*>(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<LPBYTE>(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<LPBYTE>(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<LPBYTE>(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<LPBYTE>(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<LPBYTE>(pValueData), &dwValueDataSize)))
|
|
{
|
|
TCHAR szTarget[16], szStreamPath[MAX_PATH];
|
|
|
|
if ((SHGetPathFromIDList(reinterpret_cast<LPCITEMIDLIST>(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<LPCITEMIDLIST>(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<LPBYTE>(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<DWORD>(-1);
|
|
RegSetValueEx(hKeyStreamMRU, TEXT("MRUListEx"), NULL, REG_BINARY, reinterpret_cast<LPCBYTE>(pdwMRUListExBase), ++dwLastFreeSlot * sizeof(DWORD));
|
|
LocalFree(reinterpret_cast<HLOCAL>(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<LPBYTE>(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<LPBYTE>(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);
|
|
}
|
|
|
|
|
|
extern "C" STDAPI FirstUserLogon (LPCSTR pcszCommand, LPCSTR pcszOptionalArguments)
|
|
{
|
|
enum
|
|
{
|
|
kCommandCopySampleJPG,
|
|
kCommandMergeDesktopAndNormalStreams,
|
|
kCommandMoveAndAdjustIconMetrics
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
LPCSTR pcszCommand;
|
|
int iCommand;
|
|
} tCommandElement;
|
|
|
|
tCommandElement sCommands[] =
|
|
{
|
|
{ "CopySampleJPG", kCommandCopySampleJPG },
|
|
{ "MergeDesktopAndNormalStreams", kCommandMergeDesktopAndNormalStreams },
|
|
{ "MoveAndAdjustIconMetrics", kCommandMoveAndAdjustIconMetrics },
|
|
};
|
|
|
|
HRESULT hResult;
|
|
int i;
|
|
|
|
// 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 (i = 0; (i < ARRAYSIZE(sCommands)) && (lstrcmpA(pcszCommand, sCommands[i].pcszCommand) != 0); ++i)
|
|
;
|
|
switch (sCommands[i].iCommand)
|
|
{
|
|
case kCommandCopySampleJPG:
|
|
hResult = CopySampleJPG();
|
|
break;
|
|
case kCommandMergeDesktopAndNormalStreams:
|
|
hResult = MergeDesktopAndNormalStreams();
|
|
break;
|
|
case kCommandMoveAndAdjustIconMetrics:
|
|
hResult = MoveAndAdjustIconMetrics();
|
|
break;
|
|
default:
|
|
hResult = E_FAIL;
|
|
break;
|
|
}
|
|
return(hResult);
|
|
}
|
|
|
|
#ifdef INSTALL_WEBFOLDERS
|
|
|
|
// 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
|
|
|
|
typedef struct _WEBFOLDER_INSTALL_RETRY_STRUCT {
|
|
HMODULE hmod;
|
|
PFNMSISETINTERNALUI pfnMsiSetInternalUI;
|
|
PFNMSIINSTALLPRODUCT pfnMsiInstallProduct;
|
|
} WEBFOLDER_INSTALL_RETRY_STRUCT;
|
|
|
|
|
|
void WebFolder_Install_RetryThreadProc(WEBFOLDER_INSTALL_RETRY_STRUCT * pwirs)
|
|
{
|
|
ASSERT(pwirs);
|
|
ASSERT(pwirs->hmod);
|
|
ASSERT(pwirs->pfnMsiSetInternalUI);
|
|
ASSERT(pwirs->pfnMsiInstallProduct);
|
|
|
|
TCHAR szPath[MAX_PATH];
|
|
UINT uRet = 0;
|
|
INSTALLUILEVEL oldUILevel = pwirs->pfnMsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
|
|
|
|
do {
|
|
GetSystemDirectory(szPath, ARRAYSIZE(szPath));
|
|
PathAppend(szPath, TEXT("webfldrs.msi"));
|
|
uRet = pwirs->pfnMsiInstallProduct(szPath, TEXT(""));
|
|
} while (uRet == ERROR_INSTALL_ALREADY_RUNNING);
|
|
|
|
pwirs->pfnMsiSetInternalUI(oldUILevel, NULL);
|
|
DllRelease();
|
|
FreeLibrary(pwirs->hmod);
|
|
LocalFree(pwirs);
|
|
}
|
|
|
|
void OfficeWebFolders_Install(void)
|
|
{
|
|
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, TEXT("webfldrs.msi"));
|
|
|
|
|
|
// Use "silent" install mode. No UI.
|
|
|
|
INSTALLUILEVEL oldUILevel = pfnMsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
|
|
|
|
|
|
// Install the web folders MSI package.
|
|
|
|
if (pfnMsiInstallProduct(szPath, TEXT("")) == ERROR_INSTALL_ALREADY_RUNNING)
|
|
{
|
|
WEBFOLDER_INSTALL_RETRY_STRUCT * pwirs = (WEBFOLDER_INSTALL_RETRY_STRUCT *)LocalAlloc(LPTR, SIZEOF(WEBFOLDER_INSTALL_RETRY_STRUCT));
|
|
if (pwirs)
|
|
{
|
|
DWORD thid; // Not used but we have to pass something in
|
|
|
|
pwirs->hmod = hmod;
|
|
pwirs->pfnMsiSetInternalUI = pfnMsiSetInternalUI;
|
|
pwirs->pfnMsiInstallProduct = pfnMsiInstallProduct;
|
|
|
|
DllAddRef();
|
|
HANDLE hthreadWorker = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WebFolder_Install_RetryThreadProc, (LPVOID)pwirs, CREATE_SUSPENDED, &thid);
|
|
if (hthreadWorker)
|
|
{
|
|
// Demote the priority so it doesn't interfere with the
|
|
SetThreadPriority(hthreadWorker, THREAD_PRIORITY_BELOW_NORMAL);
|
|
ResumeThread(hthreadWorker);
|
|
bFreeLib = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DllRelease();
|
|
LocalFree(pwirs);
|
|
}
|
|
}
|
|
}
|
|
|
|
pfnMsiSetInternalUI(oldUILevel, NULL);
|
|
|
|
}
|
|
|
|
if (bFreeLib)
|
|
FreeLibrary(hmod);
|
|
}
|
|
}
|
|
|
|
#endif // INSTALL_WEBFOLDERS
|
|
|
|
|
|
#ifdef WINNT
|
|
// now is the time on sprockets when we lock down the registry
|
|
STDAPI_(BOOL) ApplyRegistrySecurity()
|
|
{
|
|
BOOL fSuccess = FALSE; // assume failure
|
|
SECURITY_DESCRIPTOR* psd;
|
|
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;
|
|
|
|
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;
|
|
}
|
|
#endif // WINNT
|
|
|
|
|
|
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
|
|
}
|