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.
1925 lines
55 KiB
1925 lines
55 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dll.c
|
|
|
|
Abstract:
|
|
|
|
main file for the cross language migration tool
|
|
|
|
Author:
|
|
|
|
Xiaofeng Zang (xiaoz) 17-Sep-2001 Created
|
|
|
|
Revision History:
|
|
|
|
<alias> <date> <comments>
|
|
|
|
--*/
|
|
|
|
|
|
#include "StdAfx.h"
|
|
#include "clmt.h"
|
|
#include <winbase.h>
|
|
#include "clmtmsg.h"
|
|
|
|
|
|
BOOL CheckOS(DWORD);
|
|
HRESULT DoRegistryAnalyze();
|
|
LONG AddEventSource(VOID);
|
|
LONG CLMTReportEvent(WORD, WORD, DWORD, WORD, LPCTSTR*);
|
|
void Deinit(BOOL);
|
|
HRESULT DoCLMTCureProgramFiles();
|
|
HRESULT DoCLMTCleanUpAfterFirstReboot();
|
|
HRESULT DoCLMTCleanUpAfterDotNetUpgrade();
|
|
HRESULT DeleteUnwantedFilesPerUser(HKEY, LPCTSTR, LPCTSTR, LPTSTR);
|
|
HRESULT DeleteUnwantedFiles(HINF, LPCTSTR);
|
|
HRESULT AddRunValueToRegistry(LPCTSTR);
|
|
INT DoCLMTDisplayAccountChangeDialog();
|
|
BOOL CALLBACK AccountChangeDlgProc(HWND, UINT, WPARAM, LPARAM);
|
|
HRESULT UpdateHardLinkInfoPerUser(HKEY, LPCTSTR, LPCTSTR, LPTSTR);
|
|
VOID RemoveFromRunKey(LPCTSTR);
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
main entry point for the program
|
|
|
|
Arguments:
|
|
if dwUndo != 0, we are in undo mode, otherwise...
|
|
|
|
Return Value:
|
|
|
|
TRUE - if succeeds
|
|
--*/
|
|
|
|
LONG
|
|
DoMig(DWORD dwMode)
|
|
{
|
|
HRESULT hr;
|
|
HINF hMigrateInf = INVALID_HANDLE_VALUE;
|
|
TCHAR szInfFile[MAX_PATH],*p;
|
|
UINT iYesNo;
|
|
DWORD dwRunStatus;
|
|
DWORD dwCurrentState;
|
|
DWORD dwNextState;
|
|
TCHAR szProfileRoot[MAX_PATH];
|
|
DWORD cchLen;
|
|
BOOL bOleInit = FALSE;
|
|
DWORD dwOrgWinstationsState = 1;
|
|
TCHAR szBackupDir[2*MAX_PATH];
|
|
TCHAR szRunOnce[2*MAX_PATH];
|
|
TCHAR szRun[2 * MAX_PATH];
|
|
HANDLE hExe = GetModuleHandle(NULL);
|
|
UINT nRet;
|
|
BOOL bMsgPopuped = FALSE;
|
|
BOOL bWinStationChanged = FALSE;
|
|
ULONG uErrMsgID;
|
|
LONG err;
|
|
TCHAR szSecDatabase[MAX_PATH];
|
|
BOOL bCleanupFailed;
|
|
INT iRet;
|
|
LCID lcid;
|
|
|
|
DPF(APPmsg, L"DoMig with dwMode = %d", dwMode);
|
|
|
|
//
|
|
// Only one instance of CLMT is allowed to run on the system
|
|
//
|
|
if (!IsOneInstance())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Only users with admin privilege can run the tool
|
|
//
|
|
if (!CheckAdminPrivilege())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if there are other tasks running on the system, quit CLMT
|
|
//
|
|
if (dwMode == CLMT_DOMIG && (!IsDebuggerPresent() && !g_fNoAppChk) && DisplayTaskList())
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Display Start Up dialog
|
|
//
|
|
if (dwMode == CLMT_DOMIG)
|
|
{
|
|
iRet = ShowStartUpDialog();
|
|
if (iRet == ID_STARTUP_DLG_CANCEL)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to see if the operation is legal or not
|
|
//
|
|
hr = CheckCLMTStatus(&dwCurrentState, &dwNextState, &uErrMsgID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (hr == S_OK)
|
|
{
|
|
DPF(dlInfo,
|
|
TEXT("Operation [0x%X] is legal with current machine state [0x%X]"),
|
|
g_dwRunningStatus,
|
|
dwCurrentState);
|
|
|
|
SetCLMTStatus(g_dwRunningStatus);
|
|
}
|
|
else
|
|
{
|
|
DPF(dlFail,
|
|
TEXT("Operation [0x%X] is illegal with current machine state [0x%X]"),
|
|
g_dwRunningStatus,
|
|
dwCurrentState);
|
|
|
|
DoMessageBox(GetConsoleWindow(),
|
|
uErrMsgID,
|
|
IDS_MAIN_TITLE,
|
|
MB_OK | MB_SYSTEMMODAL);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPF(dlError,
|
|
TEXT("Error occurred when trying to check CLMT and machine status - hr = 0x%X"),
|
|
hr);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify the system if it is eligible to run CLMT
|
|
//
|
|
if (!CheckSystemCriteria())
|
|
{
|
|
DPF(APPerr, TEXT("System Verification Failed!"));
|
|
hr = E_FAIL;
|
|
bMsgPopuped = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = InitDebugSupport(dwMode);
|
|
if (FAILED(hr))
|
|
{
|
|
DPF (APPerr, L"DLL.C: InitDebugSupport! Error: %d (%#x)", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
if (FAILED(hr))
|
|
{
|
|
DPF (APPerr, L"DLL.C: CoInitialize Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
bOleInit = TRUE;
|
|
}
|
|
|
|
// Initialize global variables
|
|
if (!InitGlobals(dwMode))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
DPF (APPerr, L"DLL.C: InitGlobal failure, out of memory!");
|
|
goto Exit;
|
|
}
|
|
|
|
//we do not care the return value for LogMachineInfo
|
|
LogMachineInfo();
|
|
|
|
// Block new TS connections to be made during running CLMT
|
|
hr = DisableWinstations(1, &dwOrgWinstationsState);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bWinStationChanged = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: Block new TS session failed: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
if (g_dwRunningStatus == CLMT_REMINDER)
|
|
{
|
|
BOOL bIsNTFS;
|
|
hr = IsSysVolNTFS(&bIsNTFS);
|
|
if ( (S_OK == hr) && !bIsNTFS)
|
|
{
|
|
//make sure hr is S_FALSE so that it will not pop up reboot dlg
|
|
hr = S_FALSE;
|
|
DoMessageBox(GetConsoleWindow(), IDS_ASKING_CONVERT_TO_NTFS, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
goto Exit;
|
|
}
|
|
hr = S_FALSE;
|
|
DoMessageBox(GetConsoleWindow(), IDS_REMIND_HARDLINK, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
goto Exit;
|
|
}
|
|
else if ( (g_dwRunningStatus == CLMT_CURE_PROGRAM_FILES)
|
|
|| (g_dwRunningStatus == CLMT_CURE_ALL)
|
|
|| (g_dwRunningStatus == CLMT_CURE_AND_CLEANUP) )
|
|
|
|
{
|
|
hr = EnsureDoItemInfFile(g_szToDoINFFileName,ARRAYSIZE(g_szToDoINFFileName));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WritePrivateProfileSection(TEXT("Folder.ObjectRename"),NULL,g_szToDoINFFileName);
|
|
WritePrivateProfileSection(TEXT("REG.Update.Sys"),NULL,g_szToDoINFFileName);
|
|
WritePrivateProfileSection(TEXT("UserGrp.ObjectRename"),NULL,g_szToDoINFFileName);
|
|
WritePrivateProfileSection(TEXT("LNK"),NULL,g_szToDoINFFileName);
|
|
}
|
|
LoopUser(UpdateHardLinkInfoPerUser);
|
|
|
|
hr = DoCLMTCureProgramFiles();
|
|
if (hr == S_OK)
|
|
{
|
|
// We are done with curing Program Files hard link
|
|
CLMTSetMachineState(CLMT_STATE_PROGRAMFILES_CURED);
|
|
|
|
// remove "/CURE" rom Run registry key
|
|
RemoveFromRunKey(TEXT("/CURE"));
|
|
|
|
// Make sure hr = S_FALSE so that it will not pop up reboot dlg
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
if (g_dwRunningStatus == CLMT_CURE_AND_CLEANUP)
|
|
{
|
|
// Do the cleanup also if machine is already upgraded to .NET
|
|
// This scenario will happen only when Win2K FAT --> .NET FAT
|
|
// then run /CURE /FINAL in .NET
|
|
|
|
if (IsDotNet())
|
|
{
|
|
CheckCLMTStatus(&dwCurrentState, &dwNextState, &uErrMsgID);
|
|
|
|
hr = DoCLMTCleanUpAfterDotNetUpgrade();
|
|
if (hr == S_OK)
|
|
{
|
|
CLMTSetMachineState(dwNextState);
|
|
|
|
// Remove "/FINAL" from Run registry key
|
|
RemoveFromRunKey(TEXT("/FINAL"));
|
|
|
|
// Make sure hr = S_FALSE so that it will not pop up reboot dlg
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
else if (g_dwRunningStatus == CLMT_CLEANUP_AFTER_UPGRADE)
|
|
{
|
|
hr = DoCLMTCleanUpAfterDotNetUpgrade();
|
|
if (hr == S_OK)
|
|
{
|
|
// If the cleanup finished successfully
|
|
CLMTSetMachineState(dwNextState);
|
|
|
|
// Make sure hr = S_FALSE so that it will not pop up reboot dlg
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
hr = GetInfFilePath(szInfFile, ARRAYSIZE(szInfFile));
|
|
if (FAILED(hr))
|
|
{
|
|
DPF(APPerr,TEXT("[CLMT : get inf file name failed !"));
|
|
DoMessageBox(GetConsoleWindow(), IDS_CREATE_INF_FAILURE, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
bMsgPopuped = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = UpdateINFFileSys(szInfFile);
|
|
if (FAILED(hr))
|
|
{
|
|
DPF(APPerr,TEXT("CLMT : can not update per system settings in %s!"),szInfFile);
|
|
switch (hr)
|
|
{
|
|
case E_NOTIMPL:
|
|
DoMessageBox(GetDesktopWindow(), IDS_LANG_NOTSUPPORTED, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
break;
|
|
default:
|
|
DoMessageBox(GetConsoleWindow(), IDS_GENERAL_WRITE_FAILURE, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
break;
|
|
}
|
|
bMsgPopuped = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
hMigrateInf = SetupOpenInfFile(szInfFile,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
if (hMigrateInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
g_hInf = hMigrateInf;
|
|
}
|
|
else
|
|
{
|
|
DPF(APPerr,TEXT("CLMT : can not open inf file %s!"),szInfFile);
|
|
DoMessageBox(GetConsoleWindow(), IDS_OPEN_INF_FAILURE, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
hr = E_FAIL;
|
|
bMsgPopuped = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
//Copy myself to %windir%\$CLMT_BACKUP$ for future use, eg, runonce
|
|
if (!GetSystemWindowsDirectory(szBackupDir, ARRAYSIZE(szBackupDir)))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF(APPerr, TEXT("Failed to get WINDIR"));
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
ConcatenatePaths(szBackupDir,CLMT_BACKUP_DIR,ARRAYSIZE(szBackupDir));
|
|
if (S_OK != (hr = CopyMyselfTo(szBackupDir)))
|
|
{
|
|
//If can not copy, we will bail out, since most likely error is disk full
|
|
DPF(APPerr,TEXT("CLMT : can not copy clmt.exe to %s, error code = %d"),szBackupDir,HRESULT_CODE(hr));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Run Winnt32 /Checkupgrade to check the system compatibility
|
|
//
|
|
if (g_fRunWinnt32)
|
|
{
|
|
if (!IsUserOKWithCheckUpgrade())
|
|
{
|
|
hr = S_FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
hr = EnsureDoItemInfFile(g_szToDoINFFileName,MAX_PATH);
|
|
if (FAILED(hr))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF(APPerr,TEXT("CLMT : can not create global Todo list INF file !"));
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef NEVER
|
|
err = SDBCleanup(szSecDatabase,ARRAYSIZE(szSecDatabase),&bCleanupFailed);
|
|
if ( (err != ERROR_SUCCESS) || bCleanupFailed )
|
|
{
|
|
TCHAR szErrorMessage[2*MAX_PATH],szErrorTemplate[MAX_PATH],szCaption[MAX_PATH];
|
|
LoadString((HINSTANCE)g_hInstDll, IDS_SDBERROR, szErrorTemplate, ARRAYSIZE(szErrorTemplate)-1);
|
|
hr = StringCchPrintf(szErrorMessage,ARRAYSIZE(szErrorMessage),szErrorTemplate,szSecDatabase);
|
|
LoadString(g_hInstDll, IDS_MAIN_TITLE, szCaption, ARRAYSIZE(szCaption)-1);
|
|
MessageBox(GetConsoleWindow(),szErrorMessage,szCaption, MB_OK|MB_SYSTEMMODAL);
|
|
bMsgPopuped = TRUE;
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
#ifdef CONSOLE_UI
|
|
wprintf(TEXT("Analyzing all user shell folders ......\n"));
|
|
#endif
|
|
|
|
hr = DoShellFolderRename(hMigrateInf,NULL,TEXT("System"));
|
|
if (FAILED(hr))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: DoShellFolderRename Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef CONSOLE_UI
|
|
wprintf(TEXT("Analyzing per user shell folders ......\n"));
|
|
#endif
|
|
if (!LoopUser(MigrateShellPerUser))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: LoopUser with MigrateShellPerUser Failed");
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef CONSOLE_UI
|
|
wprintf(TEXT("Analyzing user and group name changes ......\n"));
|
|
#endif
|
|
hr = UsrGrpAndDoc_and_SettingsRename(hMigrateInf,TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: UsrGrpAndDoc_and_SettingsRename Failed! Error: %d (%#x)", hr, hr);
|
|
goto Exit;
|
|
}
|
|
if (!LoopUser(MigrateRegSchemesPerUser))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: LoopUser with MigrateRegSchemesPerUser Failed");
|
|
goto Exit;
|
|
}
|
|
|
|
hr = MigrateRegSchemesPerSystem(hMigrateInf);
|
|
if (FAILED(hr))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: MigrateRegSchemesPerSystem Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef CONSOLE_UI
|
|
wprintf(TEXT("Analyzing IIS metabase ......\n"));
|
|
#endif
|
|
|
|
hr = MigrateMetabaseSettings(hMigrateInf);
|
|
if (FAILED(hr))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: MigrateMetabaseSettings! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
hr = MetabaseAnalyze(NULL, &g_StrReplaceTable, TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: MetabaseAnalyze Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
// This EnumUserProfile will be enable after RC 1
|
|
//
|
|
//hr = EnumUserProfile(AnalyzeMiscProfilePathPerUser);
|
|
//if (FAILED(hr))
|
|
//{
|
|
// DPF (APPerr, L"DLL.C: EnumUserProfile with AnalyzeMiscProfilePathPerUser Failed");
|
|
// goto Exit;
|
|
//}
|
|
|
|
#ifdef CONSOLE_UI
|
|
wprintf(TEXT("Analyzing the entire registry ......\n"));
|
|
#endif
|
|
hr = DoRegistryAnalyze();
|
|
if (FAILED(hr))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
DPF (APPerr, L"DLL.C: DoRegistryAnalyze Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef CONSOLE_UI
|
|
//wprintf(TEXT("Analyzing lnk files under profile directories ......\n"));
|
|
wprintf(TEXT("Analyzing LNK files under profile directories, please wait as this may\n"));
|
|
wprintf(TEXT("take a few minutes ......\n"));
|
|
#endif
|
|
//make sure the link file under profile dirrectory is updated
|
|
cchLen = ARRAYSIZE(szProfileRoot);
|
|
if (GetProfilesDirectory(szProfileRoot,&cchLen))
|
|
{
|
|
if (!MyEnumFiles(szProfileRoot,TEXT("lnk"),LnkFileUpdate))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
DPF (APPerr, L"DLL.C: EnumFiles Lnk File Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (GetEnvironmentVariable(L"windir", szProfileRoot, MAX_PATH))
|
|
{
|
|
hr = StringCchCat(szProfileRoot, MAX_PATH, TEXT("\\security\\templates"));
|
|
if (!MyEnumFiles(szProfileRoot,TEXT("inf"),SecTempUpdate))
|
|
{
|
|
//BUGBUG:Xiaoz:Add DLG pop up for failure
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
DPF (APPerr, L"DLL.C: EnumFiles security template File Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
hr = FolderMove(hMigrateInf,TEXT("Generic.Folder.ObjectRename.PerSystem"),TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
DPF (APPerr, L"DLL.C: FolderMove Failed! Error: %d (%#x)\n", hr, hr);
|
|
goto Exit;
|
|
}
|
|
|
|
FRSUpdate();
|
|
Ex2000Update();
|
|
|
|
// Analyze the services reconfiguration
|
|
DoServicesAnalyze();
|
|
|
|
// Add event log source to registry
|
|
AddEventSource();
|
|
|
|
// Log an event into event log
|
|
CLMTReportEvent(EVENTLOG_INFORMATION_TYPE,
|
|
STATUS_SEVERITY_INFORMATIONAL,
|
|
MSG_CLMT_STARTED,
|
|
0,
|
|
NULL);
|
|
|
|
|
|
// Display the Administrator Account Change dialog
|
|
GetSavedInstallLocale(&lcid);
|
|
if (lcid != 0x411)
|
|
{
|
|
// we ignore displaying this dialog on JPN
|
|
iRet = DoCLMTDisplayAccountChangeDialog();
|
|
|
|
if (iRet == ID_STARTUP_DLG_CANCEL)
|
|
{
|
|
iRet = DoMessageBox(GetConsoleWindow(),
|
|
IDS_CONFIRM_OPERATION,
|
|
IDS_MAIN_TITLE,
|
|
MB_OKCANCEL);
|
|
if (iRet == IDCANCEL)
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do the critical system changes here...
|
|
//
|
|
nRet = (UINT) DialogBoxParam(hExe,
|
|
MAKEINTRESOURCE(IDD_UPDATESYSTEM),
|
|
GetConsoleWindow(),
|
|
(DLGPROC) DoCriticalDlgProc,
|
|
(LPARAM) NULL);
|
|
if (nRet == ID_UPDATE_DONE)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
// Set machine state after operation is done
|
|
if (hr == S_OK)
|
|
{
|
|
// Add cure program files switch to Run key
|
|
AddRunValueToRegistry(TEXT("/CURE /FINAL"));
|
|
|
|
// Set machine to next state
|
|
CLMTSetMachineState(dwNextState);
|
|
|
|
// Tool has finished, report to event log
|
|
CLMTReportEvent(EVENTLOG_INFORMATION_TYPE,
|
|
STATUS_SEVERITY_INFORMATIONAL,
|
|
MSG_CLMT_FINISHED,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
Exit:
|
|
if (bWinStationChanged)
|
|
{
|
|
// Return the WinStations status to original state
|
|
DisableWinstations(dwOrgWinstationsState, NULL);
|
|
}
|
|
|
|
if (hMigrateInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
SetupCloseInfFile(hMigrateInf);
|
|
g_hInf = INVALID_HANDLE_VALUE ;
|
|
}
|
|
Deinit(bOleInit);
|
|
CloseDebug();
|
|
EnablePrivilege(SE_SHUTDOWN_NAME,FALSE);
|
|
EnablePrivilege(SE_BACKUP_NAME,FALSE);
|
|
EnablePrivilege(SE_RESTORE_NAME,FALSE);
|
|
EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,FALSE);
|
|
if (hr == S_OK)
|
|
{
|
|
ReStartSystem(EWX_REBOOT);
|
|
}
|
|
else if ( FAILED(hr) && !bMsgPopuped)
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_FATALERROR, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
}
|
|
return HRESULT_CODE(hr);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Function: MigrateShellPerUser
|
|
//
|
|
// Synopsis: Rename shell folders for each user
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// History: 03/08/2002 Rerkboos Add log + code clean up
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT MigrateShellPerUser(
|
|
HKEY hKeyUser,
|
|
LPCTSTR UserName,
|
|
LPCTSTR DomainName,
|
|
LPTSTR UserSid
|
|
)
|
|
{
|
|
TCHAR InfoBuff[1000];
|
|
HINF hMigrateInf = INVALID_HANDLE_VALUE;
|
|
TCHAR szInfFile[MAX_PATH];
|
|
HRESULT hr;
|
|
|
|
DPF(APPmsg, TEXT("Enter MigrateShellPerUser:"));
|
|
|
|
// Get per-user temporary INF file name
|
|
hr = GetInfFilePath(szInfFile, ARRAYSIZE(szInfFile));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Update per-syste data to temp INF file
|
|
hr = UpdateINFFileSys(szInfFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Update per-user data to temp INF file
|
|
hr = UpdateINFFilePerUser(szInfFile, UserName, UserSid, FALSE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DPF(APPmsg, TEXT("Per-user INF file was updated successfully"));
|
|
}
|
|
else
|
|
{
|
|
DPF(APPerr, TEXT("Failed to update per-user data in INF"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPF(APPerr, TEXT("Failed to update per-system data in INF"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPF(APPerr, TEXT("Faild to get per-user INF file name"));
|
|
}
|
|
|
|
|
|
#ifdef CONSOLE_UI
|
|
wprintf(TEXT("analyzing settings for user %s \n"), UserName);
|
|
#endif
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Open per-user INF file
|
|
hMigrateInf = SetupOpenInfFile(szInfFile,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
if (hMigrateInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
// Rename shell folders for the user
|
|
hr = DoShellFolderRename(hMigrateInf, hKeyUser, (LPTSTR) UserName);
|
|
|
|
SetupCloseInfFile(hMigrateInf);
|
|
DeleteFile(szInfFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DPF(APPmsg, TEXT("Rename per-user shell folders successfully"));
|
|
}
|
|
else
|
|
{
|
|
DPF(APPerr, TEXT("Rename per-user shell folders Failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
DPF(APPerr, TEXT("Failed to open per-user INF file"));
|
|
}
|
|
}
|
|
|
|
DPF(APPmsg, TEXT("Exit MigrateShellPerUser:"));
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the global variables used in the program
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE - if succeeds
|
|
--*/
|
|
BOOL InitGlobals(DWORD dwRunStatus)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
int i, n;
|
|
DWORD dwMachineState;
|
|
HRESULT hr;
|
|
|
|
// Get the module handle to ourself
|
|
g_hInstDll = GetModuleHandle(NULL);
|
|
|
|
// Check if the machine has not run CLMT yet
|
|
hr = CLMTGetMachineState(&dwMachineState);
|
|
g_bBeforeMig = (SUCCEEDED(hr) && dwMachineState == CLMT_STATE_ORIGINAL);
|
|
|
|
//Init the global string search-replacement table
|
|
if(!InitStrRepaceTable())
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_OUT_OF_MEMORY, IDS_MAIN_TITLE, MB_OK|MB_SYSTEMMODAL);
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks the various OS properties to make sure that the tool cab be run
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the clmt tool can be run on the current platform .
|
|
--*/
|
|
|
|
|
|
BOOL CheckOS(DWORD dwMode)
|
|
{
|
|
TCHAR Text[MAX_PATH];
|
|
BOOL bRet = TRUE;
|
|
LCID lcid;
|
|
HRESULT hr;
|
|
BOOL bIsAdmin;
|
|
OSVERSIONINFOEX osviex;
|
|
|
|
if (FAILED(StringCchPrintf (Text, ARRAYSIZE(Text), TEXT("Global\\%s"), TEXT("CLMT Is Running"))))
|
|
{
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
g_hMutex = CreateMutex(NULL,FALSE,Text);
|
|
|
|
if ((g_hMutex == NULL) && (GetLastError() == ERROR_PATH_NOT_FOUND))
|
|
{
|
|
g_hMutex = CreateMutex(NULL,FALSE,TEXT("CLMT Is Running"));
|
|
if(g_hMutex == NULL)
|
|
{
|
|
//
|
|
// An error (like out of memory) has occurred.
|
|
// Bail now.
|
|
//
|
|
DoMessageBox(GetConsoleWindow(), IDS_OUT_OF_MEMORY, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure we are the only process with a handle to our named mutex.
|
|
//
|
|
if ((g_hMutex == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS))
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_ALREADY_RUNNING, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (dwMode == CLMT_DOMIG)
|
|
{
|
|
if (!IsNT5())
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_NT5, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (IsDomainController())
|
|
{
|
|
//
|
|
// If this machine is a domain controller, we need W2K SP2
|
|
//
|
|
ZeroMemory(&osviex, sizeof(OSVERSIONINFOEX));
|
|
osviex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
GetVersionEx((LPOSVERSIONINFO) &osviex);
|
|
|
|
bRet = (osviex.wServicePackMajor >= 2 ? TRUE : FALSE);
|
|
if (!bRet)
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_NT5SP2, IDS_MAIN_TITLE, MB_OK);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Also pop up the message asking admin to take machine
|
|
// off the network if it is in DC replication servers
|
|
//
|
|
DoMessageBox(GetConsoleWindow(),
|
|
IDS_DC_REPLICA_OFFLINE,
|
|
IDS_MAIN_TITLE,
|
|
MB_OK);
|
|
}
|
|
}
|
|
else if (dwMode == CLMT_CLEANUP_AFTER_UPGRADE)
|
|
{
|
|
if (!IsDotNet())
|
|
{
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//for undo code here
|
|
//BUGBUG:XIAOZ Adding code here
|
|
}
|
|
|
|
|
|
if (IsNEC98())
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_NEC98, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (IsIA64())
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_IA64, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (IsOnTSClient())
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_ON_TS_CLIENT, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//if (IsTSServiceRunning() && IsTSConnectionEnabled())
|
|
//{
|
|
// DoMessageBox(GetConsoleWindow(), IDS_TS_ENABLED, IDS_MAIN_TITLE, MB_OK);
|
|
// bRet = FALSE;
|
|
// goto Cleanup;
|
|
//}
|
|
|
|
if (IsOtherSessionOnTS())
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_TS_CLOSE_SESSION, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
bIsAdmin = IsAdmin();
|
|
|
|
if (dwMode == CLMT_DOMIG)
|
|
{
|
|
if (!bIsAdmin)
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_ADMIN, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (g_fRunWinnt32)
|
|
{
|
|
if (!IsUserOKWithCheckUpgrade())
|
|
{
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
hr = GetSavedInstallLocale(&lcid);
|
|
if (HRESULT_CODE(hr) == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
hr = SaveInstallLocale();
|
|
if (FAILED(hr))
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else if ( (dwMode == CLMT_CURE_PROGRAM_FILES)
|
|
|| (dwMode == CLMT_CURE_ALL) )
|
|
{
|
|
if (!bIsAdmin)
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_ADMIN, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if (dwMode == CLMT_CLEANUP_AFTER_UPGRADE)
|
|
{
|
|
if (!bIsAdmin)
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_ADMIN_LOGON_DOTNET, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if(!DoesUserHavePrivilege(SE_SHUTDOWN_NAME)
|
|
|| !DoesUserHavePrivilege(SE_BACKUP_NAME)
|
|
|| !DoesUserHavePrivilege(SE_RESTORE_NAME)
|
|
|| !DoesUserHavePrivilege(SE_SYSTEM_ENVIRONMENT_NAME))
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_ADMIN, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
if(!EnablePrivilege(SE_SHUTDOWN_NAME,TRUE)
|
|
|| !EnablePrivilege(SE_BACKUP_NAME,TRUE)
|
|
|| !EnablePrivilege(SE_RESTORE_NAME,TRUE)
|
|
|| !EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE))
|
|
{
|
|
DoMessageBox(GetConsoleWindow(), IDS_ADMIN, IDS_MAIN_TITLE, MB_OK);
|
|
bRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//else //This means undo which we do not need user to provide .net CD
|
|
//{
|
|
// DWORD dwStatusinReg;
|
|
|
|
// hr = CLMTGetMachineState(&dwStatusinReg);
|
|
// if ( (hr != S_OK) || (CLMT_STATE_MIGRATION_DONE != dwStatusinReg))
|
|
// {
|
|
// DPF (APPerr, L"DLL.C: can not get the CLMT status from registry or you have not run the clmt tools!");
|
|
// //BUGBUG:XIAOZ:ADD a DLG here
|
|
// bRet = FALSE;
|
|
// goto Cleanup;
|
|
// }
|
|
//}
|
|
Cleanup:
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does system wide registry search and replace, the string replace table
|
|
is in global variable g_StrReplaceTable
|
|
|
|
Arguments:
|
|
|
|
hKeyUser - user registry key handle
|
|
UserName - user name that hKeyUser belongs to
|
|
DomainName - domain name the UserName belongs to
|
|
Return Value:
|
|
|
|
TRUE - if succeeds.
|
|
--*/
|
|
|
|
HRESULT DoRegistryAnalyze()
|
|
{
|
|
LPTSTR lpUser,lpSearchStr,lpReplaceStr,lpFullPath;
|
|
UINT i;
|
|
//TCHAR szExcludeList[] = TEXT("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\0\0");
|
|
TCHAR szExcludeList[] = TEXT("HKLM\\Software\\Microsoft\\Shared Tools\\Stationery\0\0");
|
|
LPTSTR lpszExcludeList = NULL;
|
|
HRESULT hr ;
|
|
|
|
|
|
|
|
{
|
|
HKEY hkey;
|
|
LONG lRes;
|
|
lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("SOFTWARE\\Microsoft\\DRM"),
|
|
0,KEY_ALL_ACCESS,&hkey);
|
|
if (ERROR_SUCCESS == lRes)
|
|
{
|
|
hr = RegistryAnalyze(hkey,NULL,NULL,&g_StrReplaceTable,lpszExcludeList,REG_SZ,
|
|
TEXT("HKLM\\SOFTWARE\\Microsoft\\DRM"),TRUE);
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
if (!LoopUser(UpdateRegPerUser))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
lpszExcludeList = malloc(MultiSzLen(szExcludeList)*sizeof(TCHAR));
|
|
if (lpszExcludeList)
|
|
{
|
|
memmove((LPBYTE)lpszExcludeList,(LPBYTE)szExcludeList,MultiSzLen(szExcludeList)*sizeof(TCHAR));
|
|
//hr = RegistryAnalyze(HKEY_LOCAL_MACHINE,NULL,NULL,&g_StrReplaceTable,lpszExcludeList,FALSE,NULL);
|
|
hr = RegistryAnalyze(HKEY_LOCAL_MACHINE,NULL,NULL,&g_StrReplaceTable,NULL,FALSE,NULL,TRUE);
|
|
free(lpszExcludeList);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: AddEventSource
|
|
//
|
|
// Descrip: Add EventLog source to registry
|
|
//
|
|
// Returns: Win32 Error Code
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 03/05/2002 rerkboos Created
|
|
//
|
|
// Notes: none.
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
LONG AddEventSource(VOID)
|
|
{
|
|
HKEY hKey;
|
|
LONG lRet;
|
|
TCHAR szMessageFile[MAX_PATH+1];
|
|
|
|
if (GetModuleFileName(NULL, szMessageFile, ARRAYSIZE(szMessageFile)-1))
|
|
{
|
|
szMessageFile[ARRAYSIZE(szMessageFile)-1] = TEXT('\0');
|
|
lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\CLMT"),
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
lRet = RegSetValueEx(hKey,
|
|
TEXT("EventMessageFile"),
|
|
0,
|
|
REG_EXPAND_SZ,
|
|
(LPBYTE) szMessageFile,
|
|
sizeof(szMessageFile));
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwData = EVENTLOG_ERROR_TYPE |
|
|
EVENTLOG_WARNING_TYPE |
|
|
EVENTLOG_INFORMATION_TYPE;
|
|
|
|
lRet = RegSetValueEx(hKey,
|
|
TEXT("TypesSupported"),
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE) &dwData,
|
|
sizeof(dwData));
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: ReportEvent
|
|
//
|
|
// Descrip: Report event to Event Log
|
|
//
|
|
// Returns: Win32 Error Code
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 03/05/2002 rerkboos Created
|
|
//
|
|
// Notes: none.
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
LONG CLMTReportEvent(
|
|
WORD wType, // Event type
|
|
WORD wCategory, // Event category
|
|
DWORD dwEventID, // Event identifier
|
|
WORD wNumSubstitute, // Number of strings to merge
|
|
LPCTSTR *lplpMessage // Pointer to message string array
|
|
)
|
|
{
|
|
HANDLE hEventLog;
|
|
LONG lRet;
|
|
TCHAR szUserName[UNLEN + 1];
|
|
DWORD cchUserName = ARRAYSIZE(szUserName);
|
|
|
|
hEventLog = RegisterEventSource(NULL, TEXT("CLMT"));
|
|
if (hEventLog)
|
|
{
|
|
// Get the user name who run the tool
|
|
if (GetUserName(szUserName, &cchUserName))
|
|
{
|
|
LPVOID lpSidCurrentUser;
|
|
DWORD cbSid;
|
|
TCHAR szDomainName[MAX_PATH];
|
|
DWORD cbDomainName = ARRAYSIZE(szDomainName) * sizeof(TCHAR);
|
|
SID_NAME_USE sidNameUse;
|
|
|
|
// Allocate enough memory for largest possible SID
|
|
cbSid = SECURITY_MAX_SID_SIZE;
|
|
lpSidCurrentUser = MEMALLOC(cbSid);
|
|
|
|
if (lpSidCurrentUser)
|
|
{
|
|
if (LookupAccountName(NULL,
|
|
szUserName,
|
|
(PSID) lpSidCurrentUser,
|
|
&cbSid,
|
|
szDomainName,
|
|
&cbDomainName,
|
|
&sidNameUse))
|
|
{
|
|
if (ReportEvent(hEventLog,
|
|
wType,
|
|
wCategory,
|
|
dwEventID,
|
|
(PSID) lpSidCurrentUser,
|
|
wNumSubstitute,
|
|
0,
|
|
lplpMessage,
|
|
NULL))
|
|
{
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
}
|
|
|
|
MEMFREE(lpSidCurrentUser);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ReportEvent(hEventLog,
|
|
wType,
|
|
wCategory,
|
|
dwEventID,
|
|
NULL,
|
|
wNumSubstitute,
|
|
0,
|
|
lplpMessage,
|
|
NULL))
|
|
{
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
}
|
|
}
|
|
|
|
DeregisterEventSource(hEventLog);
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
|
|
|
|
void Deinit(BOOL bOleInit)
|
|
{
|
|
if (g_hMutex)
|
|
{
|
|
CloseHandle(g_hMutex);
|
|
}
|
|
if (INVALID_HANDLE_VALUE != g_hInf)
|
|
{
|
|
SetupCloseInfFile(g_hInf);
|
|
}
|
|
DeInitStrRepaceTable();
|
|
|
|
if (bOleInit)
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: DoCLMTCleanUpAfterFirstReboot
|
|
//
|
|
// Descrip: Do the clean up after the machine has been run CLMT and
|
|
// reboot (before upgraded to .NET)
|
|
//
|
|
// Returns: S_OK if cure Program Files successfully
|
|
// S_FALSE if Program Files cannot be cured (no error)
|
|
// Else if error occurred
|
|
//
|
|
// History: 07/18/2002 rerkboos Created
|
|
//
|
|
// Notes: none.
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT DoCLMTCureProgramFiles()
|
|
{
|
|
HRESULT hr;
|
|
BOOL bIsNTFS;
|
|
LONG lRet;
|
|
HKEY hRunKey;
|
|
|
|
hr = IsSysVolNTFS(&bIsNTFS);
|
|
if ((S_OK == hr) && !bIsNTFS)
|
|
{
|
|
hr = S_FALSE;
|
|
DoMessageBox(GetConsoleWindow(),
|
|
IDS_ASKING_CONVERT_TO_NTFS,
|
|
IDS_MAIN_TITLE,
|
|
MB_OK | MB_SYSTEMMODAL);
|
|
goto EXIT;
|
|
}
|
|
|
|
hr = INFCreateHardLink(INVALID_HANDLE_VALUE, FOLDER_CREATE_HARDLINK, TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
DPF(APPerr, L"DLL.C: INFCreateHardLink returned error: %d (%#x)\n", hr, hr);
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
EXIT:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: DoCLMTCleanUpAfterFirstReboot
|
|
//
|
|
// Descrip: Do the clean up after the machine has been run CLMT and
|
|
// reboot (before upgraded to .NET)
|
|
//
|
|
// Returns: S_OK if no error occured
|
|
//
|
|
// History: 07/18/2002 rerkboos Created
|
|
//
|
|
// Notes: none.
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT DoCLMTCleanUpAfterFirstReboot()
|
|
{
|
|
HRESULT hr;
|
|
TCHAR szInfFile[MAX_PATH];
|
|
HKEY hRunKey;
|
|
LONG lRet;
|
|
|
|
g_hInf = INVALID_HANDLE_VALUE;
|
|
|
|
// Load INF
|
|
hr = GetInfFilePath(szInfFile, ARRAYSIZE(szInfFile));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = UpdateINFFileSys(szInfFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
g_hInf = SetupOpenInfFile(szInfFile,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
if (g_hInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// do the per-system clean up stuffs here...
|
|
//
|
|
|
|
// Close the current INF file to update settings in INF
|
|
// Here, each callback function of LoopUser() must call UpdateINFFilePerUser
|
|
// to update per-user settings
|
|
SetupCloseInfFile(g_hInf);
|
|
g_hInf = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// do the per-user clean up stuffs here...
|
|
//
|
|
LoopUser(DeleteUnwantedFilesPerUser);
|
|
|
|
// Cleanup the variables
|
|
if (g_hInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
SetupCloseInfFile(g_hInf);
|
|
g_hInf = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
// Return S_FALSE because we don't want to reboot the machine
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: DoCLMTCleanUpAfterDotNetUpgrade
|
|
//
|
|
// Descrip: Do the clean up after the machine has been run CLMT and
|
|
// upgraded to .NET
|
|
//
|
|
// Returns: S_OK if no error occured
|
|
//
|
|
// History: 07/09/2002 rerkboos Created
|
|
//
|
|
// Notes: none.
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT DoCLMTCleanUpAfterDotNetUpgrade()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR szInfFile[MAX_PATH];
|
|
TCHAR szToDoInfFile[MAX_PATH];
|
|
HKEY hRunKey;
|
|
LONG lRet;
|
|
|
|
g_hInf = INVALID_HANDLE_VALUE;
|
|
g_hInfDoItem = INVALID_HANDLE_VALUE;
|
|
|
|
DPF(APPmsg, TEXT("[Enter CleanupAfterDotNetUpgrade]"));
|
|
//
|
|
// Load Migrate INF
|
|
//
|
|
hr = GetInfFilePath(szInfFile, ARRAYSIZE(szInfFile));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = UpdateINFFileSys(szInfFile);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
g_hInf = SetupOpenInfFile(szInfFile,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
if (g_hInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_hInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Load ClmtDo.inf
|
|
//
|
|
if (GetSystemWindowsDirectory(szToDoInfFile, ARRAYSIZE(szToDoInfFile)))
|
|
{
|
|
if (ConcatenatePaths(szToDoInfFile, CLMT_BACKUP_DIR, ARRAYSIZE(szToDoInfFile)))
|
|
{
|
|
if (ConcatenatePaths(szToDoInfFile, TEXT("CLMTDO.INF"), ARRAYSIZE(szToDoInfFile)))
|
|
{
|
|
g_hInfDoItem = SetupOpenInfFile(szToDoInfFile,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_hInfDoItem == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Do the CLMT Cleanup (Per System) stuffs here...
|
|
//
|
|
ResetServicesStatus(g_hInfDoItem, TEXT_SERVICE_STATUS_CLEANUP_SECTION);
|
|
ResetServicesStartUp(g_hInfDoItem, TEXT_SERVICE_STARTUP_CLEANUP_SECTION);
|
|
|
|
DeleteUnwantedFiles(g_hInf, TEXT("Folders.PerSystem.Cleanup"));
|
|
|
|
INFVerifyHardLink(g_hInfDoItem,TEXT("Folder.HardLink"));
|
|
|
|
|
|
// Close the current INF file to update settings in INF
|
|
// Each Call back function of LoopUser() must call UpdateINFFilePerUser
|
|
// to update per-user settings
|
|
SetupCloseInfFile(g_hInf);
|
|
g_hInf = INVALID_HANDLE_VALUE;
|
|
|
|
// Close ClmtDo.inf handle, as we don't need it anymore
|
|
SetupCloseInfFile(g_hInfDoItem);
|
|
|
|
//
|
|
// Do the CLMT Cleanup (Per User) stuffs here...
|
|
//
|
|
LoopUser(DeleteUnwantedFilesPerUser);
|
|
|
|
// Remove CLMT from registry Run key
|
|
lRet = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT_RUN_KEY, &hRunKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
RemoveFromRunKey(TEXT("/FINAL"));
|
|
RegCloseKey(hRunKey);
|
|
}
|
|
|
|
//
|
|
// Cleanup the variables
|
|
//
|
|
if (g_hInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
SetupCloseInfFile(g_hInf);
|
|
}
|
|
|
|
DPF(APPmsg, TEXT("[Exit CleanupAfterDotNetUpgrade]"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: DeleteUnwantedFiles
|
|
//
|
|
// Descrip: Delete unwanted files and directories after the machine
|
|
// has been upgraded to .NET. The list of files/directories
|
|
// are listed in INF.
|
|
// File/directory will be deleted if and only if Loc file name
|
|
// does not match the expected English file name. This prevents
|
|
// deleting English file/directory.
|
|
//
|
|
// Returns: S_OK if no error occured
|
|
//
|
|
// History: 07/09/2002 rerkboos Created
|
|
//
|
|
// Notes: Format in INF:
|
|
// <FileType>, <Loc File to be deleted>, <Expected Eng File>
|
|
//
|
|
// FileType:- 0 = Directory
|
|
// 1 = File
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT DeleteUnwantedFiles(
|
|
HINF hInf,
|
|
LPCTSTR lpInfSection
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bRet = TRUE;
|
|
LONG lLineCount;
|
|
LONG lLineIndex;
|
|
INT iFileType;
|
|
TCHAR szFileName[2 * MAX_PATH];
|
|
TCHAR szEngFileName[2 * MAX_PATH];
|
|
INFCONTEXT context;
|
|
|
|
if (hInf == INVALID_HANDLE_VALUE || lpInfSection == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Read the list of services to be reset from INF
|
|
lLineCount = SetupGetLineCount(hInf, lpInfSection);
|
|
if (lLineCount >= 0)
|
|
{
|
|
for (lLineIndex = 0 ; lLineIndex < lLineCount && bRet ; lLineIndex++)
|
|
{
|
|
bRet = SetupGetLineByIndex(hInf,
|
|
lpInfSection,
|
|
(DWORD) lLineIndex,
|
|
&context);
|
|
if (bRet)
|
|
{
|
|
bRet = SetupGetIntField(&context, 1, &iFileType)
|
|
&& SetupGetStringField(&context,
|
|
2,
|
|
szFileName,
|
|
ARRAYSIZE(szFileName),
|
|
NULL)
|
|
&& SetupGetStringField(&context,
|
|
3,
|
|
szEngFileName,
|
|
ARRAYSIZE(szEngFileName),
|
|
NULL);
|
|
if (bRet
|
|
&& MyStrCmpI(szFileName, szEngFileName) != LSTR_EQUAL)
|
|
{
|
|
switch (iFileType)
|
|
{
|
|
case 0:
|
|
// Directories
|
|
hr = DeleteDirectory(szFileName);
|
|
if (FAILED(hr) && HRESULT_CODE(hr) != ERROR_PATH_NOT_FOUND)
|
|
{
|
|
goto EXIT;
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
// Files
|
|
hr = MyDeleteFile(szFileName);
|
|
if (FAILED(hr) && HRESULT_CODE(hr) != ERROR_PATH_NOT_FOUND)
|
|
{
|
|
goto EXIT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = (bRet ? S_OK : HRESULT_FROM_WIN32(GetLastError()));
|
|
|
|
EXIT:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: DeleteUnwantedFilesPerUser
|
|
//
|
|
// Descrip: This is a call back function for LoopUser().
|
|
//
|
|
// Returns: S_OK if no error occured
|
|
//
|
|
// History: 07/09/2002 rerkboos Created
|
|
//
|
|
// Notes: Format in INF:
|
|
// <FileType>, <Loc File to be deleted>, <Expected Eng File>
|
|
//
|
|
// FileType:- 0 = Directory
|
|
// 1 = File
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT DeleteUnwantedFilesPerUser(
|
|
HKEY hKeyUser,
|
|
LPCTSTR UserName,
|
|
LPCTSTR DomainName,
|
|
LPTSTR UserSid
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = UpdateINFFilePerUser(g_szInfFile, UserName ,UserSid , FALSE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
g_hInf = SetupOpenInfFile(g_szInfFile,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
if (g_hInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
// Delete files/directories here
|
|
hr = DeleteUnwantedFiles(g_hInf, TEXT("Folders.PerUser.Cleanup"));
|
|
|
|
// Close Inf file for this user
|
|
SetupCloseInfFile(g_hInf);
|
|
g_hInf = INVALID_HANDLE_VALUE;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: AddRunValueToRegistry
|
|
//
|
|
// Descrip: This will add "CLMT /switch" to Run key. So CLMT can
|
|
// do the cleanup stuffs after next reboot.
|
|
//
|
|
// Returns: S_OK if no error occured
|
|
//
|
|
// History: 07/29/2002 rerkboos Created
|
|
//
|
|
// Notes: lpCmdSwitch should be supplied in format "/something"
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
HRESULT AddRunValueToRegistry(
|
|
LPCTSTR lpCmdSwitch
|
|
)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
TCHAR szBackupDir[MAX_PATH];
|
|
TCHAR szRun[MAX_PATH];
|
|
|
|
DPF(dlInfo, TEXT("Add CLMT with switch '%s' to Run key"), lpCmdSwitch);
|
|
|
|
if (GetSystemWindowsDirectory(szBackupDir, ARRAYSIZE(szBackupDir)))
|
|
{
|
|
if (ConcatenatePaths(szBackupDir,
|
|
CLMT_BACKUP_DIR,
|
|
ARRAYSIZE(szBackupDir)))
|
|
{
|
|
hr = StringCchCopy(szRun, ARRAYSIZE(szRun), szBackupDir);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (ConcatenatePaths(szRun, TEXT("\\CLMT.EXE "), ARRAYSIZE(szRun)))
|
|
{
|
|
hr = StringCchCat(szRun, ARRAYSIZE(szRun), lpCmdSwitch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetRunValue(TEXT_CLMT_RUN_VALUE, szRun);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// Function: DoCLMTDisplayAccountChangeDialog
|
|
//
|
|
// Descrip: Display the dialog notify user of Administrator account
|
|
// name change.
|
|
//
|
|
// Returns: n/a
|
|
//
|
|
// History: 07/29/2002 rerkboos Created
|
|
//
|
|
// Notes: none.
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
INT DoCLMTDisplayAccountChangeDialog()
|
|
{
|
|
return (INT) DialogBoxParam(GetModuleHandle(NULL),
|
|
MAKEINTRESOURCE(IDD_STARTUP_DLG),
|
|
GetConsoleWindow(),
|
|
(DLGPROC) AccountChangeDlgProc,
|
|
(LPARAM) NULL);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Function: AccountChangeDlgProc
|
|
//
|
|
// Synopsis: Dialog box procedure
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 9/02/2002 rerkboos created
|
|
//
|
|
// Notes: none
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL
|
|
CALLBACK
|
|
AccountChangeDlgProc(
|
|
HWND hwndDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
BOOL bRet;
|
|
DWORD dwErr;
|
|
TCHAR szOldAdminName[64];
|
|
TCHAR szAdminChange[1024];
|
|
LPTSTR lpArgs[1];
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
// Init the dialog
|
|
ShowWindow(hwndDlg, SW_SHOWNORMAL);
|
|
|
|
bRet = GetUserNameChangeLog(TEXT("Administrator"),
|
|
szOldAdminName,
|
|
ARRAYSIZE(szOldAdminName));
|
|
if (bRet)
|
|
{
|
|
lpArgs[0] = szOldAdminName;
|
|
|
|
dwErr = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
NULL,
|
|
MSG_CLMT_ADMIN_ACCT_CHANGE,
|
|
0,
|
|
szAdminChange,
|
|
ARRAYSIZE(szAdminChange),
|
|
(va_list *) lpArgs);
|
|
}
|
|
else
|
|
{
|
|
dwErr = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
NULL,
|
|
MSG_CLMT_ACCT_CHANGE,
|
|
0,
|
|
szAdminChange,
|
|
ARRAYSIZE(szAdminChange),
|
|
NULL);
|
|
}
|
|
|
|
SendMessage(GetDlgItem(hwndDlg, ID_STARTUP_DLG_INFO),
|
|
WM_SETTEXT,
|
|
wParam,
|
|
(LPARAM) szAdminChange);
|
|
|
|
case WM_COMMAND:
|
|
// Handle command buttons
|
|
switch (wParam)
|
|
{
|
|
case ID_STARTUP_DLG_NEXT:
|
|
EndDialog(hwndDlg, ID_STARTUP_DLG_NEXT);
|
|
break;
|
|
|
|
case ID_STARTUP_DLG_CANCEL:
|
|
EndDialog(hwndDlg, ID_STARTUP_DLG_CANCEL);
|
|
break;
|
|
|
|
case ID_STARTUP_DLG_README:
|
|
ShowReadMe();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
EndDialog(hwndDlg, ID_STARTUP_DLG_CANCEL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
HRESULT UpdateHardLinkInfoPerUser(
|
|
HKEY hKeyUser,
|
|
LPCTSTR UserName,
|
|
LPCTSTR DomainName,
|
|
LPTSTR UserSid)
|
|
{
|
|
HRESULT hr;
|
|
HINF hInf;
|
|
|
|
if (!MyStrCmpI(UserSid,TEXT("Default_User_SID")))
|
|
{
|
|
return S_OK;
|
|
}
|
|
hr = EnsureDoItemInfFile(g_szToDoINFFileName,ARRAYSIZE(g_szToDoINFFileName));
|
|
if (FAILED(hr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
hr = UpdateINFFilePerUser(g_szToDoINFFileName, UserName , UserSid, FALSE);
|
|
hInf = SetupOpenInfFile(g_szToDoINFFileName, NULL, INF_STYLE_WIN4,NULL);
|
|
if (hInf != INVALID_HANDLE_VALUE)
|
|
{
|
|
INT LineCount,LineNo;
|
|
INFCONTEXT InfContext;
|
|
|
|
LineCount = (UINT)SetupGetLineCount(hInf,TEXT("Folder.HardLink.Peruser"));
|
|
if ((LONG)LineCount > 0)
|
|
{
|
|
for (LineNo = 0; LineNo < LineCount; LineNo++)
|
|
{
|
|
BOOL b0, b1, b2, b3;
|
|
TCHAR szKeyName[MAX_PATH], szType[10],
|
|
szFileName[MAX_PATH+1], szExistingFileName[MAX_PATH+1];
|
|
|
|
if (!SetupGetLineByIndex(hInf,TEXT("Folder.HardLink.Peruser"),LineNo,&InfContext))
|
|
{
|
|
continue;
|
|
}
|
|
b0 = SetupGetStringField(&InfContext,0,szKeyName,ARRAYSIZE(szKeyName),NULL);
|
|
b1 = SetupGetStringField(&InfContext,1,szType,ARRAYSIZE(szType),NULL);
|
|
b2 = SetupGetStringField(&InfContext,2,szFileName,ARRAYSIZE(szFileName),NULL);
|
|
b3 = SetupGetStringField(&InfContext,3,szExistingFileName,ARRAYSIZE(szExistingFileName),NULL);
|
|
if (!b0 || !b1 || !b2 || !b3)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHardLinkEntry(szFileName,szExistingFileName,szType,NULL,NULL,szKeyName);
|
|
}
|
|
}
|
|
SetupCloseInfFile(hInf);
|
|
}
|
|
Cleanup :
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
VOID RemoveFromRunKey(
|
|
LPCTSTR lpCLMTOption // Option to be deleted from Run key
|
|
)
|
|
{
|
|
HKEY hRunKey;
|
|
LONG lRet;
|
|
TCHAR szRunValue[MAX_PATH];
|
|
DWORD cbRunValue;
|
|
DWORD dwType;
|
|
|
|
// Remove CLMT from registry Run key
|
|
lRet = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT_RUN_KEY, &hRunKey);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
cbRunValue = sizeof(szRunValue);
|
|
|
|
lRet = RegQueryValueEx(hRunKey,
|
|
TEXT_CLMT_RUN_VALUE,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) szRunValue,
|
|
&cbRunValue);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
RemoveSubString(szRunValue, lpCLMTOption);
|
|
|
|
// Search if there is another option or not
|
|
// If none, we can safely delete Run key
|
|
if (StrChr(szRunValue, TEXT('/')) == NULL)
|
|
{
|
|
RegDeleteValue(hRunKey, TEXT_CLMT_RUN_VALUE);
|
|
}
|
|
else
|
|
{
|
|
// Other option exists, save the new Run value to registry
|
|
RegSetValueEx(hRunKey,
|
|
TEXT_CLMT_RUN_VALUE,
|
|
0,
|
|
REG_SZ,
|
|
(CONST BYTE *) szRunValue,
|
|
lstrlen(szRunValue) * sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hRunKey);
|
|
}
|
|
}
|
|
|
|
|