|
|
//
// Module: custom.cpp
//
// Purpose: TsClient MSI custom action code
//
// Copyright(C) Microsoft Corporation 1999-2000
//
// Author: nadima
//
//
#include <custom.h>
#include <stdio.h>
#include <reglic.h>
#include "setuplib.h"
// Unicode wrapper
#include "wraputl.h"
#define TERMINAL_SERVER_CLIENT_REGKEY _T("Software\\Microsoft\\Terminal Server Client")
#define TERMINAL_SERVER_CLIENT_BACKUP_REGKEY _T("Software\\Microsoft\\Terminal Server Client (Backup)")
#define LOGFILE_STR _T("MsiLogFile")
// MSI Folder Names
#define SYSTEM32_IDENTIFIER _T("SystemFolder")
#define PROGRAMMENUFOLDER_INDENTIFIER _T("ProgramMenuFolder")
#define ACCESSORIES_IDENTIFIER _T("AccessoriesMenuFolder")
#define COMMUNICATIONS_IDENTIFIER _T("CommunicationsMenuFolder")
#define INSTALLATION_IDENTIFIER _T("INSTALLDIR")
// MSI Properties
#define ALLUSERS _T("ALLUSERS")
// Assumed max length of shortcut file name.
#define MAX_LNK_FILE_NAME_LEN 50
// ERROR_SUCCESS will let MSI continue with the default value.
// ERROR_INSTALL_FAILURE will block installation.
#define NONCRITICAL_ERROR_RETURN ERROR_SUCCESS
// Require comctl32.dll version 4.70 and above
#define MIN_COMCTL32_VERSION MAKELONG(70,4)
HINSTANCE g_hInstance = (HINSTANCE) NULL; HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
#ifdef UNIWRAP
//It's ok to have a global unicode wrapper
//class. All it does is sets up the g_bRunningOnNT
//flag so it can be shared by multiple instances
//also it is only used from DllMain so there
//are no problems with re-entrancy
CUnicodeWrapper g_uwrp; #endif
void RestoreRegAcl(VOID);
//
// DllMain entry point
//
BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if (NULL == g_hInstance) { g_hInstance = (HINSTANCE)hModule; }
switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { //
// Open the tsclient registry key to get the log file name
//
LONG status; HKEY hKey; TCHAR buffer[MAX_PATH]; DWORD bufLen; memset(buffer, 0, sizeof(buffer)); bufLen = sizeof(buffer); //size in bytes needed
#ifdef UNIWRAP
//UNICODE Wrapper intialization has to happen first,
//before anything ELSE. Even DC_BEGIN_FN, which does tracing
g_uwrp.InitializeWrappers(); #endif
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TERMINAL_SERVER_CLIENT_REGKEY, 0, KEY_ALL_ACCESS, &hKey); if(ERROR_SUCCESS == status) {
//
// Query the tsclient optional logfile path
//
status = RegQueryValueEx(hKey, LOGFILE_STR, NULL, NULL, (BYTE *)buffer, &bufLen); if(ERROR_SUCCESS == status) { g_hLogFile = CreateFile(buffer, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(g_hLogFile != INVALID_HANDLE_VALUE) { //Always append to the end of the log file
SetFilePointer(g_hLogFile, 0, 0, FILE_END); } else { DBGMSG((_T("CreateFile for log file failed %d %d"), g_hLogFile, GetLastError())); } } else { DBGMSG((_T("RegQueryValueEx for log file failed %d %d"), status, GetLastError())); } RegCloseKey(hKey); } if(g_hLogFile != INVALID_HANDLE_VALUE) { DBGMSG((_T("Log file opened by new process attach"))); DBGMSG((_T("-------------------------------------"))); } DBGMSG((_T("custom.dll:Dllmain PROCESS_ATTACH"))); } break; case DLL_THREAD_ATTACH: { DBGMSG((_T("custom.dll:Dllmain THREAD ATTACH"))); } break; case DLL_THREAD_DETACH: { } break; case DLL_PROCESS_DETACH: { DBGMSG((_T("custom.dll:Dllmain THREAD DETACH. Closing log file"))); CloseHandle(g_hLogFile); g_hLogFile = INVALID_HANDLE_VALUE; } break; }
DBGMSG(((_T("In custom.dll DllMain. Reason: %d")), ul_reason_for_call));
return TRUE; }
//
// Check if should be silent to UI
//
// Returns TRUE if it is OK to display UI
BOOL AllowDisplayUI(MSIHANDLE hInstall) { UINT status; TCHAR szResult[3]; DWORD cchResult = 3; BOOL fAllowDisplayUI = FALSE; DBGMSG((_T("Entering: AllowDisplayUI")));
status = MsiGetProperty(hInstall, _T("UILevel"), szResult, &cchResult); if (ERROR_SUCCESS == status) { DBGMSG((_T("AllowDisplayUI: MsiGetProperty for UILevel succeeded, got %s"), szResult)); if (szResult[0] != TEXT('2')) { fAllowDisplayUI = TRUE; } } else { DBGMSG((_T("AllowDisplayUI: MsiGetProperty for UILevel FAILED, Status: 0x%x"), status)); }
DBGMSG((_T("Leaving: AllowDisplayUI ret:%d"), fAllowDisplayUI)); return fAllowDisplayUI; }
/**PROC+************************************************************/ /* Name: RDCSetupInit */ /* */ /* Type: Custom Action */ /* */ /* Purpose: Do any initialization for the setup here. */ /* */ /* Returns: Refer to MSI help. */ /* */ /* Params: Refer to MSI help. */ /* */ /**PROC-************************************************************/
UINT __stdcall RDCSetupInit(MSIHANDLE hInstall) { DBGMSG((_T("Entering: RDCSetupInit"))); DBGMSG((_T("Leaving : RDCSetupInit"))); return ERROR_SUCCESS; }
/**PROC+************************************************************/ /* Name: RDCSetupCheckOsVer */ /* */ /* Type: Custom Action */ /* */ /* Purpose: Block install on certain OS's */ /* */ /* Returns: Refer to MSI help. */ /* */ /* Params: Refer to MSI help. */ /* */ /**PROC-************************************************************/ UINT __stdcall RDCSetupCheckOsVer(MSIHANDLE hInstall) { DBGMSG((_T("Entering: RDCSetupCheckOsVer")));
OSVERSIONINFO osVer; memset(&osVer, 0x0, sizeof(OSVERSIONINFO)); osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(0 == GetVersionEx(&osVer)) { return ERROR_SUCCESS; } else { DBGMSG((_T("RDCSetupCheckOsVer. OS version OK to install")));
DBGMSG((_T("RDCSetupCheckOsVer - now check comctl32 version"))); if(!CheckComctl32Version()) { DBGMSG((_T("RDCSetupCheckOsVer. comctl32.dll check failed. Block on this os"))); TCHAR szBlockOnPlatform[MAX_PATH]; TCHAR szError[MAX_PATH]; LoadString(g_hInstance, IDS_BLOCKCOMCTL32, szBlockOnPlatform,SIZECHAR(szBlockOnPlatform)); LoadString(g_hInstance, IDS_ERROR, szError, SIZECHAR(szError)); if (AllowDisplayUI(hInstall)) { MessageBox(NULL, szBlockOnPlatform, szError, MB_OK|MB_ICONSTOP); } else { DBGMSG((_T("AllowDisplayUI returned False, not displaying msg box!"))); } //Return an error to make msi abort the install.
return ERROR_INVALID_FUNCTION; } else { DBGMSG((_T("RDCSetupCheckOsVer - passed all tests. OK"))); return ERROR_SUCCESS; } }
DBGMSG((_T("Leaving : RDCSetupCheckOsVer"))); }
/**PROC+************************************************************/ /* Name: RDCSetupCheckTcpIp */ /* */ /* Type: Custom Action */ /* */ /* Purpose: Check if TCP/IP is installed in the machine. */ /* */ /* Returns: Refer to MSI help. */ /* */ /* Params: Refer to MSI help. */ /* */ /**PROC-************************************************************/
UINT __stdcall RDCSetupCheckTcpIp(MSIHANDLE hInstall) { DWORD dwVersion, dwWindowsMajorVersion; DWORD dwWindowsMinorVersion, dwBuild; HKEY hKey = NULL; LONG lRet = 0; TCHAR lpTcpMsg[MAX_PATH] = _T(""), szError[MAX_PATH] = _T(""); OSVERSIONINFO osVer;
DBGMSG((_T("Entering: RDCSetupCheckTcpIp")));
memset(&osVer, 0x0, sizeof(OSVERSIONINFO)); osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(0 == GetVersionEx(&osVer)) { return ERROR_SUCCESS; }
//Now search the appropriate registry key.
LoadString(g_hInstance, IDS_ERR_TCP, lpTcpMsg,SIZECHAR(lpTcpMsg)); LoadString(g_hInstance, IDS_WARNING, szError, SIZECHAR(szError)); if(VER_PLATFORM_WIN32_WINDOWS == osVer.dwPlatformId ) { //
// Win95 check for TCP/IP
//
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (_T("Enum\\Network\\MSTCP")), 0, KEY_READ, &hKey); if(ERROR_SUCCESS != lRet) { if(hKey) { RegCloseKey(hKey); } if (AllowDisplayUI(hInstall)) { MessageBox(NULL, lpTcpMsg, szError, MB_OK|MB_ICONWARNING); } else { DBGMSG((_T("AllowDisplayUI returned false, not displaying TCP/IP warning"))); }
return ERROR_SUCCESS; } } else if((VER_PLATFORM_WIN32_NT == osVer.dwPlatformId) && (osVer.dwMajorVersion <= 4)) { //
// NT4 and below check for TCP/IP
//
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (_T("SYSTEM\\CurrentControlSet\\Services\\Tcpip")), 0, KEY_READ, &hKey); if( ERROR_SUCCESS != lRet) { if(hKey) { RegCloseKey(hKey); } if (AllowDisplayUI(hInstall)) { MessageBox(NULL, lpTcpMsg, szError, MB_OK|MB_ICONWARNING); } else { DBGMSG((_T("AllowDisplayUI returned false, not displaying TCP/IP warning"))); } return ERROR_SUCCESS; } } else if((VER_PLATFORM_WIN32_NT == osVer.dwPlatformId) && (osVer.dwMajorVersion >= 5)) { //
// NT5+ check for TCP/IP
//
HRESULT hr = CheckNt5TcpIpInstalled();
if(S_FALSE == hr) { if (AllowDisplayUI(hInstall)) { MessageBox(NULL, lpTcpMsg, szError, MB_OK|MB_ICONWARNING); } else { DBGMSG((_T("AllowDisplayUI returned false, not displaying TCP/IP warning"))); } if(hKey) { RegCloseKey(hKey); } return ERROR_SUCCESS; } }
if(hKey) { RegCloseKey(hKey); }
DBGMSG((_T("Leaving: RDCSetupCheckTcpIp")));
return ERROR_SUCCESS; }
/**PROC+************************************************************/ /* Name: CheckNt5TcpIpInstalled */ /* */ /* Purpose: Find if TCP/IP is installed and running. */ /* This function should be called only NT 5 or greater. */ /* */ /* Returns: S_OK if TCP/IP is installed and running */ /* S_FALSE if TCP/IP is not installed or running */ /* E_FAIL if a failure occurs. */ /* */ /* Params: None */ /* */ /**PROC-************************************************************/ HRESULT CheckNt5TcpIpInstalled() { INetCfg * pnetCfg = NULL; INetCfgClass * pNetCfgClass = NULL; INetCfgComponent * pNetCfgComponentprot = NULL; DWORD dwCharacteristics; ULONG count = 0; HRESULT hResult; WCHAR wsz [2*MAX_PATH] = L""; BOOL bInit = FALSE; DBGMSG((_T("Entering: CheckNt5TcpIpInstalled"))); hResult = CoInitialize(NULL); if(FAILED(hResult)) { DBGMSG( (_T("CoInitialize() failed."))); goto Cleanup; } else { bInit = TRUE; }
hResult = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_SERVER, IID_INetCfg, (LPVOID *)&pnetCfg); if((NULL == pnetCfg) || FAILED(hResult)) { DBGMSG( (_T("CoCreateInstance() failed."))); goto Cleanup; }
hResult = pnetCfg->Initialize(NULL);
if(FAILED(hResult)) { DBGMSG( (_T("pnetCfg->Initialize() failed."))); goto Cleanup; }
hResult = pnetCfg->QueryNetCfgClass(&GUID_DEVCLASS_NETTRANS, IID_INetCfgClass, (void **)&pNetCfgClass); if(FAILED(hResult)) { DBGMSG( (_T("QueryNetCfgClass() failed."))); goto Cleanup; }
#ifdef UNICODE
lstrcpy(wsz, NETCFG_TRANS_CID_MS_TCPIP); #else //UNICODE
if (0 == MultiByteToWideChar(CP_ACP, 0, (LPCSTR)NETCFG_TRANS_CID_MS_TCPIP, -1, wsz, sizeof(wsz)/sizeof(WCHAR))) { DBGMSG( (_T("MultiByteToWideChar() failed."))); hResult = E_FAIL; goto Cleanup; } #endif //UNICODE
hResult = pNetCfgClass->FindComponent(wsz, &pNetCfgComponentprot);
if(hResult == S_FALSE) { DBGMSG( (_T("FindComponent() failed."))); goto Cleanup; }
Cleanup:
if(bInit) { CoUninitialize(); }
if(pNetCfgComponentprot) { pNetCfgComponentprot->Release(); pNetCfgComponentprot = NULL; }
if(pNetCfgClass) { pNetCfgClass->Release(); pNetCfgClass = NULL; }
if(pnetCfg != NULL) { pnetCfg->Uninitialize(); pnetCfg->Release(); pnetCfg = NULL; }
DBGMSG((_T("Leaving: IsTCPIPInstalled"))); return hResult; }
/**PROC+************************************************************/ /* Name: RDCSetupPreInstall */ /* */ /* Type: Custom Action */ /* */ /* Purpose: Does cleanup work during install. */ /* */ /* Returns: Refer to MSI help. */ /* */ /* Params: Refer to MSI help. */ /* */ /**PROC-************************************************************/
UINT __stdcall RDCSetupPreInstall(MSIHANDLE hInstall) { BOOL fInstalling = FALSE; UINT retVal = 0;
DBGMSG((_T("Entering: RDCSetupPreInstall")));
//Figure out if we're installing or removing
ASSERT(hInstall);
DBGMSG((_T("RDCSetupPreInstall: Modifying dirs"))); retVal = RDCSetupModifyDir(hInstall); DBGMSG((_T("RDCSetupPreInstall: Modifying dirs. DONE: %d"), retVal));
//
// This is pre-install if the product
// is not installed then we are installing
//
fInstalling = !IsProductInstalled(hInstall); if(fInstalling) { DBGMSG((_T("RDCSetupPreInstall: We're installing"))); TCHAR szProgmanPath[MAX_PATH]; TCHAR szOldProgmanPath[MAX_PATH]; DBGMSG((_T("RDCSetupPreInstall: Delete desktop shortcuts. START"))); DeleteTSCDesktopShortcuts(); DBGMSG((_T("RDCSetupPreInstall: Delete desktop shortcuts. DONE")));
//Acme uninstall
LoadString(g_hInstance, IDS_PROGMAN_GROUP, szProgmanPath, sizeof(szProgmanPath) / sizeof(TCHAR));
DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. START"), szProgmanPath)); DeleteTSCFromStartMenu(szProgmanPath); DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. DONE"), szProgmanPath)); LoadString(g_hInstance, IDS_OLD_NAME, szOldProgmanPath, sizeof(szOldProgmanPath) / sizeof(TCHAR)); DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. START"), szOldProgmanPath)); DeleteTSCFromStartMenu(szOldProgmanPath); DBGMSG((_T("RDCSetupPreInstall: DeleteTSCFromStartMenu: %s. DONE"), szOldProgmanPath)); DBGMSG((_T("RDCSetupPreInstall: Uninstall ACME program files. START"))); DeleteTSCProgramFiles(); DBGMSG((_T("RDCSetupPreInstall: Uninstall ACME program files. DONE")));
DBGMSG((_T("RDCSetupPreInstall: Uninstall TSCLIENT registry keys. START"))); DeleteTSCRegKeys(); DBGMSG((_T("RDCSetupPreInstall: Uninstall TSCLIENT registry keys. DONE"))); } else { if(MsiGetMode(hInstall, MSIRUNMODE_MAINTENANCE)) { DBGMSG((_T("MsiGetMode: MSIRUNMODE_MAINTENANCE returned TRUE."))); DBGMSG((_T("RDCSetupPreInstall: We're in maintenance mode"))); } else { DBGMSG((_T("MsiGetMode: MSIRUNMODE_MAINTENANCE returned FALSE."))); DBGMSG((_T("RDCSetupPreInstall: We're not installing (removing)"))); } }
DBGMSG((_T("Leaving: RDCSetupPreInstall")));
return ERROR_SUCCESS; }
//
// Run migration 'mstsc /migrate'
//
// This will fail silenty if mstsc.exe is not present
//
BOOL RDCSetupRunMigration(MSIHANDLE hInstall) { BOOL fRet = TRUE; PROCESS_INFORMATION pinfo; STARTUPINFO sinfo; TCHAR szMigratePathLaunch[MAX_PATH]; TCHAR szInstallPath[MAX_PATH]; TCHAR szMigrateCmdLine[] = _T("mstsc.exe /migrate"); DWORD cchInstallPath = SIZECHAR(szInstallPath); UINT uiResult; HRESULT hr;
// Get the path to the installation directory.
uiResult = MsiGetTargetPath( hInstall, INSTALLATION_IDENTIFIER, szInstallPath, &cchInstallPath); if (uiResult != ERROR_SUCCESS) { DBGMSG((_T("Error: MsiGetTargetPath returned 0x%x."), uiResult)); fRet = FALSE; goto Exit; }
DBGMSG((_T("Path to installation directory is %s"), szInstallPath));
// Concatenate the installation directory and the mstsc /migrate command
// so that we can call CreateProcess.
hr = StringCchPrintf( szMigratePathLaunch, SIZECHAR(szMigratePathLaunch), _T("%s%s"), szInstallPath, _T("mstsc.exe"));
if (FAILED(hr)) { DBGMSG((_T("Error: Failed to construct command line for CreateProcess. hr = 0x%x"), hr)); goto Exit; }
//
// Start registry and connection file migration
//
ZeroMemory(&sinfo, sizeof(sinfo)); sinfo.cb = sizeof(sinfo);
fRet = CreateProcess(szMigratePathLaunch, // name of executable module
szMigrateCmdLine, // command line string
NULL, // SD
NULL, // SD
FALSE, // handle inheritance option
CREATE_NEW_PROCESS_GROUP, // creation flags
NULL, // new environment block
NULL, // current directory name
&sinfo, // startup information
&pinfo); // process information
if (fRet) { DBGMSG((_T("RDCSetupRunMigration: Started mstsc.exe /migrate"))); } else { DBGMSG((_T("RDCSetupRunMigration: Failed to start mstsc.exe /migrate: %d"), GetLastError())); }
Exit:
return fRet; }
/**PROC+************************************************************/ /* Name: RDCSetupPostInstall */ /* */ /* Type: Custom Action */ /* */ /* Purpose: Do work after MSI has completed */ /* could be after an uninstall completes, get MSI prop */ /* to determine that */ /* */ /* Returns: Refer to MSI help. */ /* */ /* Params: Refer to MSI help. */ /* */ /**PROC-************************************************************/
UINT __stdcall RDCSetupPostInstall(MSIHANDLE hInstall) { BOOL fInstalling = FALSE; DBGMSG((_T("Entering: RDCSetupPostInstall")));
ASSERT(hInstall); //
// This is post install if the product is installed
// then we are 'installing' otherwise we were
// removing.
//
fInstalling = IsProductInstalled(hInstall);
if(fInstalling) { DBGMSG((_T("RDCSetupPostInstall: We're installing"))); //Add the MsLicensingReg key and ACL it
//This will only happen on NT (it's not needed on 9x)
//and will not (cannot) work if you're not admin
DBGMSG((_T("Setting up MSLicensing key..."))); if(SetupMSLicensingKey()) { DBGMSG((_T("Setting up MSLicensing key...SUCCEEDED"))); } else { DBGMSG((_T("Setting up MSLicensing key...FAILED"))); }
//
// Migrate user settings (will only run if MSTSC.EXE was successfully
// installed).
//
if (RDCSetupRunMigration(hInstall)) { DBGMSG((_T("RDCSetupRunMigration...SUCCEEDED"))); } else { DBGMSG((_T("RDCSetupRunMigration...FAILED"))); } } else { RestoreRegAcl();
DBGMSG((_T("RDCSetupPostInstall: We're not installing (removing)"))); //We're uninstalling
//Delete the bitmap cache folder
}
DBGMSG((_T("Leaving: RDCSetupPostInstall"))); return ERROR_SUCCESS; }
//Return true if we're installing
//False if we're uninstalling
BOOL IsProductInstalled(MSIHANDLE hInstall) { ASSERT(hInstall); TCHAR szProdCode[MAX_PATH]; DWORD dwCharCount = sizeof(szProdCode)/sizeof(TCHAR); UINT status; status = MsiGetProperty(hInstall, _T("ProductCode"), szProdCode, &dwCharCount); if(ERROR_SUCCESS == status) { DBGMSG((_T("MsiGetProperty returned product code %s"), szProdCode)); INSTALLSTATE insState = MsiQueryProductState( szProdCode ); DBGMSG((_T("MsiQueryProductState returned: %d"), (DWORD)insState)); if(INSTALLSTATE_DEFAULT == insState) { DBGMSG((_T("Product installed. IsProductInstalled return TRUE"))); return TRUE; } else { DBGMSG((_T("Product not installed. IsProductInstalled return FALSE"))); return FALSE; } } else { DBGMSG((_T("MsiGetProperty for ProductCode failed: %d %d"), status, GetLastError())); return FALSE; } }
//
// Check that comctl32.dll has a sufficiently
// high version number (4.70).
//
// Return - TRUE - version ok, allow install
// FALSE - version bad (or fail) block install
//
BOOL CheckComctl32Version() { DWORD dwFileVerInfoSize; PBYTE pVerInfo = NULL; VS_FIXEDFILEINFO* pFixedFileInfo = NULL; BOOL bRetVal = FALSE; UINT len = 0; DBGMSG((_T("Entering: CheckComctl32Version")));
//
// USE Ansi versions of GetFileVersionInfo calls
// because we don't have unicode wrappers for them
//
dwFileVerInfoSize = GetFileVersionInfoSizeA("comctl32.dll", NULL); if(!dwFileVerInfoSize) { DBGMSG((_T("GetFileVersionInfoSize for comctl32.dll failed: %d %d"), dwFileVerInfoSize, GetLastError())); }
pVerInfo = (PBYTE) LocalAlloc(LPTR, dwFileVerInfoSize); if(pVerInfo) { if(GetFileVersionInfoA("comctl32.dll", NULL, dwFileVerInfoSize, (LPVOID)pVerInfo )) { DBGMSG((_T("GetFileVersionInfo: succeeded"))); pFixedFileInfo = NULL; if(VerQueryValueA(pVerInfo, "\\", //get root version info block
(LPVOID*)&pFixedFileInfo, &len ) && len) { DBGMSG((_T("comctl32.dll filever is 0x%x-0x%x"), pFixedFileInfo->dwFileVersionMS, pFixedFileInfo->dwFileVersionLS));
if(pFixedFileInfo->dwFileVersionMS >= MIN_COMCTL32_VERSION) { DBGMSG((_T("Sufficently new comctl32.dll found. Allow install"))) bRetVal = TRUE; } else { DBGMSG((_T("comctl32.dll too old block install"))) bRetVal = FALSE; } } else { DBGMSG((_T("VerQueryValue: failed len:%d gle:%d"), len, GetLastError())); bRetVal = FALSE; goto BAIL_OUT; } } else { DBGMSG((_T("GetFileVersionInfo: failed %d"), GetLastError())); bRetVal = FALSE; goto BAIL_OUT; } } else { DBGMSG((_T("LocalAlloc for %d bytes of ver info failed"), dwFileVerInfoSize)); bRetVal = FALSE; goto BAIL_OUT; }
BAIL_OUT:
DBGMSG((_T("Leaving: CheckComctl32Version"))); if(pVerInfo) { LocalFree(pVerInfo); pVerInfo = NULL; } return bRetVal; }
UINT RDCSetupModifyDir(MSIHANDLE hInstall) { UINT uReturn; int iAccessories; int iCommunications; TCHAR szAccessories[MAX_PATH]; TCHAR szCommunications[MAX_PATH]; TCHAR szProgram[MAX_PATH]; TCHAR szFullAccessories[MAX_PATH]; TCHAR szFullCommunications[MAX_PATH]; OSVERSIONINFO osVer; DWORD dwSize;
DBGMSG((_T("Entering: RDCSetupModifyDir")));
//
// OS Version
//
ZeroMemory( &osVer, sizeof( osVer ) ); osVer.dwOSVersionInfoSize = sizeof( osVer );
if (!GetVersionEx(&osVer)) { DBGMSG( (TEXT("RDCSetupModifyDir: GetVersionEx failed.")) ); return(NONCRITICAL_ERROR_RETURN); }
if (osVer.dwMajorVersion >= 5) { DBGMSG((TEXT("RDCSetupModifyDir: Ver >= 5. No need to apply the altertnate path."))); return(ERROR_SUCCESS); }
//
// Get ProgramMenuFolder
//
dwSize = sizeof( szProgram ) / sizeof( TCHAR ); uReturn = MsiGetProperty(hInstall,PROGRAMMENUFOLDER_INDENTIFIER, szProgram,&dwSize); if ( ERROR_SUCCESS != uReturn ) { DBGMSG((TEXT("RDCSetupModifyDir: MsiGetProperty failed. %d"), uReturn)); return NONCRITICAL_ERROR_RETURN; }
//
// Load String
//
iAccessories = LoadString(g_hInstance, IDS_ACCESSORIES, szAccessories, sizeof(szAccessories)/sizeof(TCHAR)-1); if (!iAccessories) { DBGMSG((TEXT("RDCSetupModifyDir: IDS_ACCESSORIES failed."))); return NONCRITICAL_ERROR_RETURN; }
iCommunications = LoadString(g_hInstance, IDS_COMMUNICATIONS, szCommunications, sizeof(szCommunications)/sizeof(TCHAR)-1); if (!iCommunications) { DBGMSG((TEXT("RDCSetupModifyDir: IDS_COMMUNICATIONS failed."))); return NONCRITICAL_ERROR_RETURN; }
//
// Check Length
//
if (MAX_PATH < lstrlen( szProgram ) + iAccessories + 1 + iCommunications + 1 + MAX_LNK_FILE_NAME_LEN + 1 ) { DBGMSG((TEXT( "RDCSetupModifyDir: Too long path." ))); return NONCRITICAL_ERROR_RETURN; }
//
// Make Full Path
//
memset(szFullAccessories, 0, sizeof(szFullAccessories)); memset(szFullCommunications, 0, sizeof(szFullCommunications)); //
// Use lstrcat as that has unicode wrappers
//
lstrcat(szFullAccessories, szProgram); lstrcat(szFullAccessories, szAccessories); lstrcat(szFullAccessories, _T("\\"));
lstrcat(szFullCommunications, szFullAccessories); lstrcat(szFullCommunications, szCommunications); lstrcat(szFullCommunications, _T("\\"));
//
// Set Directory
//
uReturn = MsiSetTargetPath(hInstall, ACCESSORIES_IDENTIFIER, szFullAccessories); if (ERROR_SUCCESS != uReturn) { DBGMSG ((TEXT("RDCSetupModifyDir: SetTargetPathACCESSORIES_IDENTIFIER failed."))); return NONCRITICAL_ERROR_RETURN; }
uReturn = MsiSetTargetPath(hInstall, COMMUNICATIONS_IDENTIFIER, szFullCommunications); if (ERROR_SUCCESS != uReturn) { DBGMSG( (TEXT( "RDCSetupModifyDir: COMMUNICATIONS_IDENTIFIER failed."))); return NONCRITICAL_ERROR_RETURN; }
DBGMSG((_T("Leaving: RDCSetupModifyDir"))); return ERROR_SUCCESS; }
//*****************************************************************************
//
// CopyRegistryValues
//
// Copies all the values from a registry key to the other.
//
//*****************************************************************************
HRESULT __stdcall CopyRegistryValues( IN HKEY hSourceKey, IN HKEY hTargetKey ) { DWORD dwStatus = 0, cValues, cchValueName, cbData, dwType; BYTE rgbData[MAX_PATH]; TCHAR szValueName[MAX_PATH]; LONG lResult = 0; HRESULT hr = E_FAIL;
// Determine how many values are in the registry key.
lResult = RegQueryInfoKey( hSourceKey, NULL, NULL, NULL, NULL, NULL, NULL, &cValues, NULL, NULL, NULL, NULL);
if (lResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(lResult); DBGMSG((_T("RegQueryInfoKey failed getting the number of values in the source. hr = 0x%x"), hr)); goto Exit; } // Loop through all of the values and copy them from the source key into
// the target key.
for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++) { cchValueName = SIZECHAR(szValueName); cbData = sizeof(rgbData);
lResult = RegEnumValue( hSourceKey, dwIndex, szValueName, &cchValueName, NULL, &dwType, rgbData, &cbData);
if (lResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(lResult); DBGMSG((_T("RegEnumValue failed while obtaining source value. hr = 0x%x"), hr)); goto Exit; }
lResult = RegSetValueEx( hTargetKey, szValueName, NULL, dwType, rgbData, cbData);
if (lResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(lResult); DBGMSG((_T("RegSetValueEx failed while copying value into target. hr = 0x%x"), hr)); goto Exit; } }
hr = S_OK;
Exit:
return hr; }
//*****************************************************************************
//
// CopyRegistryKey
//
// Copies the source registry key into the target registry key completely.
//
//*****************************************************************************
HRESULT __stdcall CopyRegistryKey( IN HKEY hRootKey, IN TCHAR *szSourceKey, IN TCHAR *szTargetKey ) { HKEY hSourceKey = NULL, hTargetKey = NULL; LONG lResult; DWORD cchSubSize = MAX_PATH, i = 0, dwDisposition = 0; TCHAR szSubKey[MAX_PATH]; HRESULT hr = E_FAIL;
// Open the source key.
lResult = RegOpenKeyEx( hRootKey, szSourceKey, 0, KEY_READ, &hSourceKey);
if(lResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(lResult); DBGMSG((_T("Unable to open source registry key. hr = 0x%x"), hr)); goto Exit; }
// Create or open the target registry key.
lResult = RegCreateKeyEx( hRootKey, szTargetKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hTargetKey, &dwDisposition);
if (lResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(lResult); DBGMSG((_T("Unable to create or open target registry key. hr = 0x%x"), hr)); goto Exit; }
// Copy the values in the source key to the target key.
hr = CopyRegistryValues(hSourceKey, hTargetKey); if (FAILED(hr)) { DBGMSG((_T("Unable to copy registry values from source to target. hr = 0x%x"), hr)); goto Exit; }
// Loop through the source's subkeys and copy each of these into the
// target key.
while (ERROR_SUCCESS == RegEnumKey( hSourceKey, i++, szSubKey, cchSubSize)) { TCHAR szNewSubKey[MAX_PATH] = _T(""); TCHAR szOldSubKey[MAX_PATH] = _T("");
hr = StringCchPrintf(szOldSubKey, SIZECHAR(szOldSubKey), _T("%s\\%s"), szSourceKey, szSubKey); if (FAILED(hr)) { DBGMSG((_T("StringCchPrintf failed when constructing source registry key string. hr = 0x%x"), hr)); goto Exit; }
StringCchPrintf(szNewSubKey, SIZECHAR(szNewSubKey), _T("%s\\%s"), szTargetKey, szSubKey); if (FAILED(hr)) { DBGMSG((_T("StringCchPrintf failed when constructing target registry key string. hr = 0x%x"), hr)); goto Exit; }
hr = CopyRegistryKey(hRootKey, szOldSubKey, szNewSubKey); if (FAILED(hr)) { DBGMSG((_T("Failed to copy source subkey into target. hr = 0x%x"), hr)); goto Exit; } }
hr = S_OK; Exit:
if (hTargetKey) { RegCloseKey(hTargetKey); }
if (hSourceKey) { RegCloseKey(hSourceKey); }
return hr; }
//*****************************************************************************
//
// DeleteRegistryKey
//
// Deletes the registry key completely, including all subkeys. This is a bit
// tricky to do because Win9x and WinNT have different semantics for
// RegDeleteKey. From MSDN:
//
// Windows 95/98/Me: RegDeleteKey deletes all subkeys and values.
// Windows NT/2000/XP: The subkey to be deleted must not have subkeys.
//
// This function implements deletion for Windows NT and above only.
//
//*****************************************************************************
HRESULT __stdcall DeleteRegistryKey( IN HKEY hRootKey, IN LPTSTR pszDeleteKey ) { DWORD dwResult, cchSubKeyLength; TCHAR szSubKey[MAX_PATH]; HKEY hDeleteKey = NULL; HRESULT hr = E_FAIL;
// Open the key to delete so we can remove the subkeys.
dwResult = RegOpenKeyEx( hRootKey, pszDeleteKey, 0, KEY_READ, &hDeleteKey);
if (dwResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(dwResult); DBGMSG((_T("Error while opening deletion key. hr = 0x%x"), hr)); goto Exit; }
// Enumerate through each of the subkeys and delete them in the process.
while (dwResult == ERROR_SUCCESS) { cchSubKeyLength = SIZECHAR(szSubKey); dwResult = RegEnumKeyEx( hDeleteKey, 0, // Always index zero.
szSubKey, &cchSubKeyLength, NULL, NULL, NULL, NULL);
if (dwResult == ERROR_NO_MORE_ITEMS) { // All of the subkeys have been deleted. So, just delete the
// deletion key.
RegCloseKey(hDeleteKey); hDeleteKey = NULL; dwResult = RegDeleteKey(hRootKey, pszDeleteKey); hr = HRESULT_FROM_WIN32(dwResult);
goto Exit;
} else if (dwResult == ERROR_SUCCESS) { // There are more subkeys to delete, so delete the current one
// recursively.
dwResult = DeleteRegistryKey(hDeleteKey, szSubKey); } else { // Some other error happened, so report a problem.
hr = HRESULT_FROM_WIN32(dwResult); DBGMSG((_T("Error while enumerating subkeys. hr = 0x%x"), hr)); goto Exit; } }
Exit:
if (hDeleteKey) { RegCloseKey(hDeleteKey); }
return hr; }
//*****************************************************************************
//
// RDCSetupBackupRegistry
//
// Copies the data in the Terminal Server Client registry key to a backup
// registry key so that this data can be restored when the client is removed
// at a later stage. This function is only necessary on Windows XP and above
// since they have built in clients which may rely on these registry keys, and
// because these clients take over after an uninstall, we have to make sure
// that they will work properly.
//
//*****************************************************************************
UINT __stdcall RDCSetupBackupRegistry( IN MSIHANDLE hInstall ) { UINT uiResult; TCHAR szAllUsers[MAX_PATH]; DWORD cchAllUsers = SIZECHAR(szAllUsers); HKEY hRootKey = HKEY_LOCAL_MACHINE; HRESULT hr = E_FAIL;
DBGMSG((_T("Entering: RDCSetupBackupRegistry"))); // Determine whether we will be using HKLM or HKCU. If it is a per user
// install use HKCU, otherwise, use HKLM.
uiResult = MsiGetProperty( hInstall, ALLUSERS, szAllUsers, &cchAllUsers);
if (uiResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(GetLastError()); DBGMSG((_T("Unable to get ALLUSERS property. hr = 0x%x."), hr)); goto Exit; }
DBGMSG((_T("ALLUSERS = %s."), szAllUsers)); // If ALLUSERS[0] == NULL, then we are doing a per-user install.
if (szAllUsers[0] == NULL) { hRootKey = HKEY_CURRENT_USER; } // Copy the source registry key into the backup key.
hr = CopyRegistryKey( hRootKey, TERMINAL_SERVER_CLIENT_REGKEY, TERMINAL_SERVER_CLIENT_BACKUP_REGKEY);
if (FAILED(hr)) { DBGMSG((_T("Unable to backup registry key. hr = 0x%x."), hr)); goto Exit; }
hr = S_OK;
Exit:
DBGMSG((_T("Leaving: RDCSetupBackupRegistry"))); return ERROR_SUCCESS; }
//*****************************************************************************
//
// RDCSetupRestoreRegistry
//
// Copies the data in the Terminal Server Client backup registry key back to
// the original key. Any data in the original key is deleted and the key is
// restored to exactly how it appeared when the backup was done.
//
//*****************************************************************************
UINT __stdcall RDCSetupRestoreRegistry( IN MSIHANDLE hInstall ) { LONG lResult; UINT uiResult; TCHAR szAllUsers[MAX_PATH]; DWORD cchAllUsers = SIZECHAR(szAllUsers); HKEY hRootKey = HKEY_LOCAL_MACHINE; HRESULT hr = E_FAIL;
DBGMSG((_T("Entering: RDCSetupRestoreRegistry"))); // Determine whether we will be using HKLM or HKCU. If it is a per user
// install use HKCU, otherwise, use HKLM.
uiResult = MsiGetProperty( hInstall, ALLUSERS, szAllUsers, &cchAllUsers);
if (uiResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(GetLastError()); DBGMSG((_T("Unable to get ALLUSERS property. hr = 0x%x."), hr)); goto Exit; }
DBGMSG((_T("ALLUSERS = %s."), szAllUsers));
// If ALLUSERS[0] == NULL, then we are doing a per-user uninstall.
if (szAllUsers[0] == NULL) { hRootKey = HKEY_CURRENT_USER; } // Restore the registry key from the backup key.
hr = CopyRegistryKey( hRootKey, TERMINAL_SERVER_CLIENT_BACKUP_REGKEY, TERMINAL_SERVER_CLIENT_REGKEY);
if (FAILED(hr)) { DBGMSG((_T("Unable to restore registry key. hr = 0x%x."), hr)); goto Exit; }
// Delete the restore source as we don't need it anymore.
hr = DeleteRegistryKey( hRootKey, TERMINAL_SERVER_CLIENT_BACKUP_REGKEY); if (FAILED(hr)) { DBGMSG((_T("Failed to delete backup registry key. hr = 0x%x"), hr)); goto Exit; }
hr = S_OK;
Exit:
DBGMSG((_T("Leaving: RDCSetupRestoreRegistry"))); return ERROR_SUCCESS; }
//*****************************************************************************
//
// CreateLinkFile
//
// Creates a shortcut named lpszLinkFile that points to the target lpszPath
// and contains the description given by lpszDescription.
//
//*****************************************************************************
HRESULT __stdcall CreateLinkFile( IN LPTSTR lpszLinkFile, IN LPCTSTR lpszPath, IN LPCTSTR lpszDescription ) { IShellLink* psl; HRESULT hr = E_FAIL;
// Get a pointer to the IShellLink interface.
hr = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*) &psl);
if (SUCCEEDED(hr)) { IPersistFile* ppf; // Get a pointer to the IPersistFile interface.
hr = psl->QueryInterface(IID_IPersistFile, (void**) &ppf);
if (SUCCEEDED(hr)) {
// Set the path to the link target.
hr = psl->SetPath(lpszPath);
if (SUCCEEDED(hr)) {
hr = psl->SetDescription(lpszDescription);
if (SUCCEEDED(hr)) {
#ifndef UNICODE
WCHAR wsz[MAX_PATH]; int cch; // Ensure that the string is Unicode.
cch = MultiByteToWideChar( CP_ACP, 0, lpszLinkFile, -1, wsz, MAX_PATH); if (cch > 0) { // Load the shortcut.
hr = ppf->Save(wsz, FALSE); #else
// Load the shortcut.
hr = ppf->Save(lpszLinkFile, FALSE); #endif
#ifndef UNICODE
} #endif
} }
// Release the pointer to the IPersistFile interface.
ppf->Release(); ppf = NULL; }
// Release the pointer to the IShellLink interface.
psl->Release(); psl = NULL; }
return hr; }
//*****************************************************************************
//
// RDCSetupResetShortCut
//
// Reset the Remote Desktop Connection shortcut in the Communications submenu
// of the Start menu to point to the original Remote Desktop client.
//
//*****************************************************************************
UINT __stdcall RDCSetupResetShortCut( IN MSIHANDLE hInstall ) { TCHAR szCommunicationsPath[MAX_PATH], szSystem32Path[MAX_PATH], szRdcShortCutTitle[MAX_PATH], szRdcShortCutPath[MAX_PATH], szMstscExecutableName[MAX_PATH], szMstscPath[MAX_PATH], szDescription[MAX_PATH]; DWORD cchCommunicationsPath = SIZECHAR(szCommunicationsPath), cchSystem32Path = SIZECHAR(szSystem32Path); UINT uiResult; INT iResult; HRESULT hr = E_FAIL; DBGMSG((_T("Entering: RDCSetupResetShortCut")));
// Get the path to the Remote Desktop Connection shortcut.
uiResult = MsiGetTargetPath( hInstall, COMMUNICATIONS_IDENTIFIER, szCommunicationsPath, &cchCommunicationsPath); if (uiResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(uiResult); DBGMSG((_T("Error: MsiGetTargetPath returned hr = 0x%x."), hr)); goto Exit; }
// Get the full path to the Remote Desktop shortcut.
iResult = LoadString( g_hInstance, IDS_RDC_SHORTCUT_FILE, szRdcShortCutTitle, SIZECHAR(szRdcShortCutTitle));
if (iResult == 0) { DBGMSG((_T("Error: Resource IDS_RDC_SHORTCUT_FILE not found."))); hr = HRESULT_FROM_WIN32(GetLastError()); goto Exit; }
hr = StringCchPrintf( szRdcShortCutPath, SIZECHAR(szRdcShortCutPath), _T("%s%s"), szCommunicationsPath, szRdcShortCutTitle);
if (FAILED(hr)) { DBGMSG((_T("Error: Failed to construct the RDC shortcut path. hr = 0x%x"), hr)); goto Exit; }
DBGMSG((_T("Path to RDC shortcut is %s"), szRdcShortCutPath));
// Get the path to the system32 directory.
uiResult = MsiGetTargetPath( hInstall, SYSTEM32_IDENTIFIER, szSystem32Path, &cchSystem32Path); if (uiResult != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(uiResult); DBGMSG((_T("Error: MsiGetTargetPath returned hr = 0x%x."), hr)); goto Exit; }
// Get the full path to the mstsc executable.
iResult = LoadString( g_hInstance, IDS_MSTSC_EXE_FILE, szMstscExecutableName, SIZECHAR(szMstscExecutableName));
if (iResult == 0) { DBGMSG((_T("Error: Resource IDS_MSTSC_EXE_FILE not found."))); hr = HRESULT_FROM_WIN32(GetLastError()); goto Exit; }
hr = StringCchPrintf( szMstscPath, SIZECHAR(szMstscPath), _T("%s%s"), szSystem32Path, szMstscExecutableName);
if (FAILED(hr)) { DBGMSG((_T("Error: Failed to construct mstsc executable path. hr = 0x%x"), hr)); goto Exit; }
DBGMSG((_T("Path to mstsc executable is %s"), szMstscPath));
// Get the description text for the shortcut.
iResult = LoadString( g_hInstance, IDS_RDC_DESCRIPTION, szDescription, SIZECHAR(szDescription));
if (iResult == 0) { DBGMSG((_T("Error: Resource IDS_RDC_DESCRIPTION not found."))); hr = HRESULT_FROM_WIN32(GetLastError()); goto Exit; }
// Create a shortcut that points back to the old remote desktop client
// in system32.
hr = CreateLinkFile( szRdcShortCutPath, szMstscPath, szDescription);
if (FAILED(hr)) { DBGMSG((_T("Error: Failed to set link file target. hr = 0x%x"), hr)); goto Exit; }
hr = S_OK;
Exit:
DBGMSG((_T("Leaving: RDCSetupResetShortCut")));
return ERROR_SUCCESS; }
|