|
|
//+-------------------------------------------------------------------------
//
// AppWiz.cpl - "Add or Remove Programs" CPL.
// Copyright (C) Microsoft
//
// File: Scripts.CPP
// authomates running of TS application compatibility scripts
//
// History: Nov-14-2000 skuzin Created
//
//--------------------------------------------------------------------------
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <regapi.h>
#include <tsappcmp.h>
#include <strsafe.h>
#include "Scripts.h"
#include <list>
using namespace std; #ifndef ARRAYSIZE
#define ARRAYSIZE(sz) (sizeof(sz)/sizeof(sz[0]))
#endif
//
//Struct to establish correspondence between app. name (key name)
//and install and uninstall script names
//
class CAppScript { private: LPWSTR m_szKeyName; //key name that represents installed application
LPWSTR m_szInstallScript; //install script name
LPWSTR m_szUninstallScript; //uninstall script name
DWORD m_bNeedReboot; //If set - then scripts must be run after reboot.
BOOL m_bAlreadyInstalled; //
public: //
CAppScript() : m_szKeyName(NULL), m_szInstallScript(NULL), m_szUninstallScript(NULL), m_bNeedReboot(FALSE), m_bAlreadyInstalled(FALSE) { } ~CAppScript() { if(m_szKeyName) { LocalFree(m_szKeyName); }
if(m_szInstallScript) { LocalFree(m_szInstallScript); }
if(m_szUninstallScript) { LocalFree(m_szUninstallScript); } }
BOOL Load(HKEY hKeyParent,LPCWSTR szKeyName); BOOL RunScriptIfApplicable();
private: BOOL RunScript(LPCWSTR szDir, LPCWSTR szScript); BOOL PrepareScriptForReboot(LPCWSTR szInstallDir, LPCWSTR szScript); //BUGBUG this function is public for test only
};
//This class describes a list of pointers
//to objects of class CAppScript
class CAppScriptList : public list<CAppScript*> { public: //Deletes all CAppScript objects
//before destroing the list itself.
~CAppScriptList() { CAppScriptList::iterator it; for(it=begin();it!=end(); it++) { delete (*it); } } };
class CAppScriptManager { private: CAppScriptList m_AppScriptList; public: CAppScriptManager(){}; ~CAppScriptManager(){};
BOOL Init(); BOOL RunScripts(); private: BOOL LoadSupportedAppList(); BOOL IsAppCompatOn(); };
//Functions - helpers.
DWORD RegLoadString(HKEY hKey, LPCWSTR szValueName, LPWSTR *pszValue); DWORD RegLoadDWORD(HKEY hKey, LPCWSTR szValueName, DWORD *pdwValue); BOOL RegIsKeyExist(HKEY hKeyParent, LPCWSTR szKeyName); DWORD RegGetKeyInfo(HKEY hKey, LPDWORD pcSubKeys, LPDWORD pcMaxNameLen); DWORD RegKeyEnum(HKEY hKey, DWORD dwIndex, LPWSTR szSubKeyName, DWORD cSubKeyName);
///////////////////////////////////////////////////////////////////////////////
//Exports
///////////////////////////////////////////////////////////////////////////////
extern "C" LPVOID ScriptManagerInitScripts() { CAppScriptManager *pScriptManager = new CAppScriptManager(); if(pScriptManager) { if(!pScriptManager->Init()) { delete pScriptManager; pScriptManager = NULL; } }
return pScriptManager; }
extern "C" void ScriptManagerRunScripts( LPVOID *ppScriptManager) { if(*ppScriptManager) { CAppScriptManager *pScriptManager = reinterpret_cast<CAppScriptManager *>(*ppScriptManager); pScriptManager->RunScripts(); delete pScriptManager; *ppScriptManager = NULL; } }
///////////////////////////////////////////////////////////////////////////////
//class CAppScript
///////////////////////////////////////////////////////////////////////////////
/******************************************************************************
CAppScript::Load() Purpose: Loads script information from the registry Sets m_bIsInitiallyInstalled to TRUE if app already installed ******************************************************************************/ BOOL CAppScript::Load( HKEY hKeyParent, LPCWSTR szKeyName) { DWORD err; HKEY hKey;
err = RegOpenKeyExW(hKeyParent, szKeyName, 0, KEY_QUERY_VALUE, &hKey );
if(err == ERROR_SUCCESS) { RegLoadString(hKey, L"KeyName", &m_szKeyName); RegLoadString(hKey, L"InstallScript", &m_szInstallScript); RegLoadString(hKey, L"UninstallScript", &m_szUninstallScript); RegLoadDWORD(hKey, L"NeedReboot", &m_bNeedReboot);
RegCloseKey(hKey);
if(m_szKeyName) { m_bAlreadyInstalled = RegIsKeyExist(HKEY_LOCAL_MACHINE, m_szKeyName); return TRUE; } } return FALSE; }
/******************************************************************************
CAppScript::RunScriptIfApplicable() Purpose: Checks if the application was installed or uninstalled and runs the script. If m_bNeedReboot flag is set - schedules the script to run after reboot. ******************************************************************************/ BOOL CAppScript::RunScriptIfApplicable() { KdPrint(("CAppScript::RunScriptIfApplicable() - ENTER\n"));
const WCHAR szInstallSubDir[] = L"\\Application Compatibility Scripts\\Install"; const WCHAR szUninstallSubDir[] = L"\\Application Compatibility Scripts\\Uninstall"; static WCHAR szInstallDir[MAX_PATH+sizeof(szInstallSubDir)/sizeof(WCHAR)+1] = L""; static WCHAR szUninstallDir[MAX_PATH+sizeof(szUninstallSubDir)/sizeof(WCHAR)+1] = L""; if(!szInstallDir[0]) { //Get the scripts location
//We need to do it only once
//get Windows directory name
if(!GetSystemWindowsDirectoryW(szInstallDir,MAX_PATH)) { KdPrint(("CAppScript::RunScriptIfApplicable() - GetWindowsDirectoryW() FAILED\n")); return FALSE; } StringCchCopy(szUninstallDir, ARRAYSIZE(szUninstallDir), szInstallDir); StringCchCat(szInstallDir, ARRAYSIZE(szInstallDir), szInstallSubDir); StringCchCat(szUninstallDir, ARRAYSIZE(szUninstallDir), szUninstallSubDir); }
if(!m_bAlreadyInstalled && RegIsKeyExist(HKEY_LOCAL_MACHINE, m_szKeyName) && m_szInstallScript) { //Application was installed
if(m_bNeedReboot) { //Setup will continue after reboot
//Create RunOnce entry to run script after system is rebooted
KdPrint(("CAppScript::RunScriptIfApplicable() - PrepareScriptForReboot %ws\n",m_szInstallScript)); if(!PrepareScriptForReboot(szInstallDir, m_szInstallScript)) { KdPrint(("CAppScript::PrepareScriptForReboot() - FAILED\n",m_szInstallScript)); return FALSE; }
} else { KdPrint(("CAppScript::RunScriptIfApplicable() - executing script %ws\n",m_szInstallScript)); if(!RunScript(szInstallDir,m_szInstallScript)) { KdPrint(("CAppScript::RunScriptIfApplicable() - executing script FAILED\n",m_szInstallScript)); return FALSE; } }
m_bAlreadyInstalled = TRUE; } else { if(m_bAlreadyInstalled && !RegIsKeyExist(HKEY_LOCAL_MACHINE, m_szKeyName) && m_szUninstallScript) { //Application was uninstalled
KdPrint(("CAppScript::RunScriptIfApplicable() - executing script %ws\n",m_szUninstallScript)); if(!RunScript(szUninstallDir,m_szUninstallScript)) { KdPrint(("CAppScript::RunScriptIfApplicable() - executing script FAILED\n",m_szUninstallScript)); return FALSE; }
m_bAlreadyInstalled = FALSE; } }
return TRUE; }
/******************************************************************************
CAppScript::RunScript() Purpose: Runs script Waits untill script finishes ******************************************************************************/ BOOL CAppScript::RunScript( LPCWSTR szDir, LPCWSTR szScript) { BOOL bRet = FALSE; WCHAR szCmdLineTemplate[] = L"cmd.exe /C %s"; LPWSTR pszCmdLine; DWORD cchCmdLine = wcslen(szScript) + ARRAYSIZE(szCmdLineTemplate); // null terminator taken care of by ARRAYSIZE()
pszCmdLine = (LPWSTR)LocalAlloc(LPTR, cchCmdLine * sizeof(WCHAR)); if (pszCmdLine) { if (SUCCEEDED(StringCchPrintf(pszCmdLine, cchCmdLine, szCmdLineTemplate, szScript))) { STARTUPINFO si = {0}; PROCESS_INFORMATION pi;
si.cb = sizeof(STARTUPINFO);
bRet = CreateProcessW(NULL, pszCmdLine, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, szDir, &si, &pi); if (bRet) { WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } } LocalFree(pszCmdLine); } return bRet; }
/******************************************************************************
CAppScript::PrepareScriptForReboot() Purpose: Creates CMD file that will change current directory to ...\Application Compatibility Scripts\Install and then run the script. Schedules this CMD file to be run after reboot by creating an entry uder "HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce" key. ******************************************************************************/ BOOL CAppScript::PrepareScriptForReboot( LPCWSTR szInstallDir, LPCWSTR szScript) { BOOL bRet = FALSE; BOOL bCreatedFile = FALSE; WCHAR szFileNameTemplate[] = L"%s\\RunOnce.cmd"; LPWSTR pszFullFileName; size_t cchFullFileName = wcslen(szInstallDir) + ARRAYSIZE(szFileNameTemplate); // null terminator taken care of by ARRAYSIZE()
pszFullFileName = (LPWSTR)LocalAlloc(LPTR, cchFullFileName * sizeof(WCHAR)); if (pszFullFileName) { // Assemble full file name
if (SUCCEEDED(StringCchPrintfW(pszFullFileName, cchFullFileName, szFileNameTemplate, szInstallDir))) { HANDLE hFile = CreateFile(pszFullFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, // only create it if it does not already exist
FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { // File did not exist before - create code and write it into the file.
char szCodeTemplate[] = "cd %S\r\n%%1\r\n"; LPSTR pszFileCode; DWORD cchFileCode = wcslen(szInstallDir) + ARRAYSIZE(szCodeTemplate); // null terminator taken care of by ARRAYSIZE()
pszFileCode = (LPSTR)LocalAlloc(LPTR, cchFileCode * sizeof(char)); if (pszFileCode) { if SUCCEEDED(StringCchPrintfA(pszFileCode, cchFileCode, szCodeTemplate, szInstallDir)) // unicode->ansi conversion taken care of by "%S"
{ DWORD cbToWrite = strlen(pszFileCode); DWORD cbWritten;
if (WriteFile(hFile, (void*)pszFileCode, cbToWrite, &cbWritten, NULL) && (cbToWrite == cbWritten)) { bCreatedFile = TRUE; } }
LocalFree(pszFileCode); }
CloseHandle(hFile);
if (bCreatedFile == FALSE) { DeleteFile(pszFullFileName); } } else { // If file already exists - do only registry changes.
if (GetLastError() == ERROR_FILE_EXISTS) { bCreatedFile = TRUE; } } }
LocalFree(pszFullFileName); }
if (bCreatedFile) { // Registry changes:
WCHAR szCommandTemplate[] = L"\"%s\\RunOnce.cmd\" %s"; LPWSTR pszCommand; DWORD cchCommand = (wcslen(szInstallDir) + wcslen(szScript) + ARRAYSIZE(szCommandTemplate)); // null terminator taken care of by ARRAYSIZE()
pszCommand = (LPWSTR)LocalAlloc(LPTR, cchCommand * sizeof(WCHAR)); if (pszCommand) { if (SUCCEEDED(StringCchPrintfW(pszCommand, cchCommand, szCommandTemplate, szInstallDir, szScript))) { HKEY hKey;
if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS) { DWORD cbCommand = (wcslen(pszCommand) + 1) * sizeof(WCHAR);
if (RegSetValueExW(hKey, L"ZZZAppCompatScript", 0, REG_SZ, (CONST BYTE *)pszCommand, cbCommand) == ERROR_SUCCESS) { bRet = TRUE; }
RegCloseKey(hKey); } }
LocalFree(pszCommand); } }
return bRet; }
///////////////////////////////////////////////////////////////////////////////
//class CAppScriptManager
///////////////////////////////////////////////////////////////////////////////
/******************************************************************************
CAppScriptManager::Init() Purpose: Initialization. Returns FALSE if TS Application Compatibility if OFF or list of supported applications was not found in the registry. ******************************************************************************/ BOOL CAppScriptManager::Init() { //DebugBreak();
KdPrint(("CAppScriptManager::Init() - ENTER\n"));
if(!IsAppCompatOn()) { KdPrint(("CAppScriptManager::Init() - TS App Compat is off!\n")); return FALSE; }
if(!LoadSupportedAppList()) { KdPrint(("CAppScriptManager::Init() - LoadSupportedAppList() FAILED\n")); return FALSE; } KdPrint(("CAppScriptManager::Init() - OK\n")); return TRUE; }
/******************************************************************************
CAppScriptManager::LoadSupportedAppList() Purpose: Loads from the registry the list of applications we care about along with their script names. Save this information in array of APP_SCRIPT structures. ******************************************************************************/ BOOL CAppScriptManager::LoadSupportedAppList() { HKEY hKey; LONG err; DWORD cSubKeys; DWORD cMaxSubKeyLen;
KdPrint(("CAppScriptManager::LoadSupportedAppList() - ENTER\n")); err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Compatibility\\Scripts", 0, KEY_READ, &hKey ); if(err == ERROR_SUCCESS) {
err = RegGetKeyInfo(hKey, &cSubKeys, &cMaxSubKeyLen);
if(err == ERROR_SUCCESS) { cMaxSubKeyLen+=1; //to include terminating NULL character
KdPrint(("CAppScriptManager::LoadSupportedAppList() - %d apps supported\n",cSubKeys)); //Allocate buffer for subkey names
LPWSTR szKeyName = (LPWSTR)LocalAlloc(LPTR,cMaxSubKeyLen*sizeof(WCHAR)); if(!szKeyName) { RegCloseKey(hKey); return FALSE; }
CAppScript *pAppScript = NULL;
for(DWORD i=0;i<cSubKeys;i++) { //Get the key name
err = RegKeyEnum(hKey, i, szKeyName, cMaxSubKeyLen ); if(err != ERROR_SUCCESS) { break; }
KdPrint(("CAppScriptManager::LoadSupportedAppList() - loading %ws\n",szKeyName));
pAppScript = new CAppScript(); if(!pAppScript) { break; } if(pAppScript->Load(hKey, szKeyName)) { m_AppScriptList.push_back(pAppScript); } else { KdPrint(("CAppScriptManager::LoadSupportedAppList() - FAILED to load\n")); delete pAppScript; } }
LocalFree(szKeyName); } RegCloseKey(hKey); } if(err != ERROR_SUCCESS) { return FALSE; } else { return TRUE; } }
/******************************************************************************
CAppScriptManager::RunScripts() Purpose: Runs scripts for all installed or uninstalled applications. ******************************************************************************/ BOOL CAppScriptManager::RunScripts() {
BOOL bInstallMode = FALSE; KdPrint(("CAppScriptManager::RunScripts() - ENTER\n")); //system must be now in INSTALL mode, set EXECUTE mode
if(TermsrvAppInstallMode()) { bInstallMode = TRUE; KdPrint(("CAppScriptManager::RunScripts() - set EXECUTE mode\n")); if(!SetTermsrvAppInstallMode(FALSE)) { KdPrint(("CAppScriptManager::RunScripts() - SetTermsrvAppInstallMode() FAILED\n")); return FALSE; } } CAppScriptList::iterator it; for(it=m_AppScriptList.begin();it!=m_AppScriptList.end(); it++) { (*it)->RunScriptIfApplicable(); }
if(bInstallMode) { //Restore INSTALL mode
KdPrint(("CAppScriptManager::RunScripts() - return to INSTALL mode\n")); if(!SetTermsrvAppInstallMode(TRUE)) { KdPrint(("CAppScriptManager::RunScripts() - SetTermsrvAppInstallMode() FAILED\n")); return FALSE; } }
KdPrint(("CAppScriptManager::RunScripts() - FINISH\n")); return TRUE; }
/******************************************************************************
CAppScriptManager::IsAppCompatOn() Purpose: Checks if TS Application Compatibility mode is enabled. Returns TRUE if enabled, otherwise, as well as in case of any error, returns FALSE. ******************************************************************************/ BOOL CAppScriptManager::IsAppCompatOn() { HKEY hKey; DWORD dwData; BOOL fResult = FALSE; KdPrint(("CAppScriptManager::IsAppCompatOn() - ENTER\n"));
if( RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_CONTROL_TSERVER, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS ) { if(RegLoadDWORD(hKey, L"TSAppCompat", &dwData) == ERROR_SUCCESS ) { KdPrint(("CAppScriptManager::IsAppCompatOn() - OK; Result=%d\n",dwData)); fResult = dwData; } RegCloseKey(hKey); }
return fResult; }
///////////////////////////////////////////////////////////////////////////////
//Functions - helpers.
///////////////////////////////////////////////////////////////////////////////
/******************************************************************************
RegLoadString() Purpose: Loads a REG_SZ value from the registry Allocates buffer. Buffer then need to be freed using LocalFree function. ******************************************************************************/ DWORD RegLoadString( HKEY hKey, LPCWSTR szValueName, LPWSTR *pszValue) { DWORD cbData = 0; *pszValue = NULL;
DWORD err = RegQueryValueExW( hKey, // handle to key
szValueName, // value name
NULL, // reserved
NULL, // type buffer
NULL, // data buffer
&cbData // size of data buffer
); if(err == ERROR_SUCCESS) { *pszValue = (LPWSTR)LocalAlloc(LPTR,cbData); if(!*pszValue) { return GetLastError(); }
err = RegQueryValueExW( hKey, // handle to key
szValueName, // value name
NULL, // reserved
NULL, // type buffer
(LPBYTE)*pszValue, // data buffer
&cbData // size of data buffer
); if(err !=ERROR_SUCCESS) { LocalFree(*pszValue); *pszValue = NULL; }
}
return err; }
/******************************************************************************
RegLoadDWORD() Purpose: Loads a REG_DWORD value from the registry ******************************************************************************/ DWORD RegLoadDWORD( HKEY hKey, LPCWSTR szValueName, DWORD *pdwValue) { DWORD cbData = sizeof(DWORD);
return RegQueryValueExW( hKey, // handle to key
szValueName, // value name
NULL, // reserved
NULL, // type buffer
(LPBYTE)pdwValue, // data buffer
&cbData // size of data buffer
); }
/******************************************************************************
RegIsKeyExist() Purpose: Checks if key exists ******************************************************************************/ BOOL RegIsKeyExist( HKEY hKeyParent, LPCWSTR szKeyName) { LONG err; HKEY hKey; KdPrint(("RegIsKeyExist() - Opening key: hKeyParent=%d Key: %ws\n",hKeyParent,szKeyName)); err = RegOpenKeyExW(hKeyParent, szKeyName, 0, MAXIMUM_ALLOWED, &hKey );
if(err == ERROR_SUCCESS) { KdPrint(("RegIsKeyExist() - Key Exists!\n",err)); RegCloseKey(hKey); return TRUE; } else { KdPrint(("RegIsKeyExist() - err=%d\n",err)); return FALSE; } }
/******************************************************************************
RegGetKeyInfo() Purpose: Gets key's number of sub keys and max sub key name length ******************************************************************************/ DWORD RegGetKeyInfo( HKEY hKey, LPDWORD pcSubKeys, LPDWORD pcMaxNameLen) { return RegQueryInfoKey( hKey, // handle to key
NULL, // class buffer
NULL, // size of class buffer
NULL, // reserved
pcSubKeys, // number of subkeys
pcMaxNameLen, // longest subkey name (in TCHARs)
NULL, // longest class string
NULL, // number of value entries
NULL, // longest value name
NULL, // longest value data
NULL, // descriptor length
NULL // last write time
); }
/******************************************************************************
RegKeyEnum() Purpose: Enumerates sub keys of the registry key ******************************************************************************/ DWORD RegKeyEnum( HKEY hKey, // handle to key to enumerate
DWORD dwIndex, // subkey index
LPWSTR szSubKeyName, // subkey name
DWORD cSubKeyName) { FILETIME ftLastWriteTime;
return RegEnumKeyExW( hKey, // handle to key to enumerate
dwIndex, // subkey index
szSubKeyName, // subkey name
&cSubKeyName, // size of subkey buffer
NULL, // reserved
NULL, // class string buffer
NULL, // size of class string buffer
&ftLastWriteTime // last write time
); }
|