You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1676 lines
51 KiB
1676 lines
51 KiB
//
|
|
// 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;
|
|
}
|
|
|