windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upgnt/migmain/wkstamig.c
2020-09-26 16:20:57 +08:00

3134 lines
74 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
wkstamig.c
Abstract:
The functions in this module are called to perform migration of
system-wide settings.
Author:
Jim Schmidt (jimschm) 04-Feb-1997
Revision History:
ovidiut 10-May-1999 Added DoIniActions
jimschm 16-Dec-1998 Changed ATM font migration to use Adobe's
APIs.
jimschm 25-Nov-1998 ATM.INI migration; Win9x hive migration
jimschm 23-Sep-1998 Consolidated memdb saves into usermig.c
jimschm 19-Feb-1998 Added "none" group support, fixed
share problems.
calinn 12-Dec-1997 Added RestoreMMSettings_System
--*/
#include "pch.h"
#include "migmainp.h"
#include "brfcasep.h"
#include <lm.h>
//
// Constants, types, declarations
//
#define W95_ACCESS_READ 0x1
#define W95_ACCESS_WRITE 0x2
#define W95_ACCESS_CREATE 0x4
#define W95_ACCESS_EXEC 0x8
#define W95_ACCESS_DELETE 0x10
#define W95_ACCESS_ATRIB 0x20
#define W95_ACCESS_PERM 0x40
#define W95_ACCESS_FINDFIRST 0x80
#define W95_ACCESS_FULL 0xff
#define W95_ACCESS_GROUP 0x8000
#define W95_GENERIC_READ (W95_ACCESS_READ|W95_ACCESS_FINDFIRST)
#define W95_GENERIC_WRITE (W95_ACCESS_WRITE|W95_ACCESS_CREATE|W95_ACCESS_DELETE|W95_ACCESS_ATRIB)
#define W95_GENERIC_FULL (W95_GENERIC_READ|W95_GENERIC_WRITE|W95_ACCESS_PERM)
#define W95_GENERIC_NONE 0
#define S_DEFAULT_USER_NAME TEXT("DefaultUserName")
#define S_DEFAULT_DOMAIN_NAME TEXT("DefaultDomainName")
// from private\net\svcdlls\srvsvc\server
#define SHARES_REGISTRY_PATH L"LanmanServer\\Shares"
#define SHARES_SECURITY_REGISTRY_PATH L"LanmanServer\\Shares\\Security"
#define CSCFLAGS_VARIABLE_NAME L"CSCFlags"
#define MAXUSES_VARIABLE_NAME L"MaxUses"
#define PATH_VARIABLE_NAME L"Path"
#define PERMISSIONS_VARIABLE_NAME L"Permissions"
#define REMARK_VARIABLE_NAME L"Remark"
#define TYPE_VARIABLE_NAME L"Type"
// Win9x-specific flags for NetShareEnum
#define SHI50F_RDONLY 0x0001
#define SHI50F_FULL 0x0002
#define SHI50F_DEPENDSON (SHI50F_RDONLY|SHI50F_FULL)
#define SHI50F_ACCESSMASK (SHI50F_RDONLY|SHI50F_FULL)
#ifndef UNICODE
#error UNICODE required
#endif
#define DBG_NETSHARES "NetShares"
#define DBG_INIFILES "IniFiles"
#define S_CLEANER_GUID TEXT("{C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}")
#define S_CLEANER_ALL_FILES TEXT("*")
typedef struct {
// Enumeration return data
TCHAR PfmFile[MAX_TCHAR_PATH];
TCHAR PfbFile[MAX_TCHAR_PATH];
TCHAR MmmFile[MAX_TCHAR_PATH];
TCHAR InfName[MAX_TCHAR_PATH];
// Internal state
PTSTR KeyNames;
POOLHANDLE Pool;
} ATM_FONT_ENUM, *PATM_FONT_ENUM;
typedef INT (WINAPI ATMADDFONTEXW) (
IN OUT PWSTR MenuName,
IN OUT PWORD StyleAndType,
IN PCWSTR MetricsFile,
IN PCWSTR FontFile,
IN PCWSTR MMMFile
);
typedef ATMADDFONTEXW * PATMADDFONTEXW;
PATMADDFONTEXW AtmAddFontEx;
typedef VOID (SHUPDATERECYCLEBINICON_PROTOTYPE)(VOID);
typedef SHUPDATERECYCLEBINICON_PROTOTYPE * SHUPDATERECYCLEBINICON_PROC;
typedef struct {
// enumeration output
PCTSTR Source;
PCTSTR Dest;
// private members
POOLHANDLE Pool;
INFSTRUCT is;
} HIVEFILE_ENUM, *PHIVEFILE_ENUM;
#define MAX_KEY_NAME_LIST 32768
typedef struct {
// enumeration output
PCTSTR BrfcaseDb;
// private
MEMDB_ENUM mde;
} BRIEFCASE_ENUM, *PBRIEFCASE_ENUM;
//
// Implementation
//
DWORD
Simplify9xAccessFlags (
IN DWORD Win9xFlags
)
/*++
Routine Description:
Translates the Win9x LanMan flags into NT LanMan flags (for use with Net* APIs).
Full permission requires:
W95_ACCESS_READ
W95_ACCESS_WRITE
W95_ACCESS_CREATE
W95_ACCESS_DELETE
W95_ACCESS_ATRIB
W95_ACCESS_FINDFIRST
Read-only permission requires:
W95_ACCESS_READ
W95_ACCESS_FINDFIRST
Change-only permission requires:
W95_ACCESS_WRITE
W95_ACCESS_CREATE
W95_ACCESS_DELETE
W95_ACCESS_ATRIB
Any other combination results in
The returned flags are currently converted to security flags based on the
following mapping:
0 - Deny All Rights
ACCESS_READ - Read-Only Rights
ACCESS_WRITE - Change-Only Rights
ACCESS_READ|ACCESS_WRITE - Full Rights
See AddAclMember for details.
Arguments:
Flags - The set of Win9x flags as returned by an API on Win9x
Return value:
The NT equivalent of the flags.
--*/
{
DWORD NtFlags = 0;
if (BITSARESET (Win9xFlags, W95_GENERIC_WRITE)) {
NtFlags |= ACCESS_WRITE;
}
if (BITSARESET (Win9xFlags, W95_GENERIC_READ)) {
NtFlags |= ACCESS_READ;
}
DEBUGMSG_IF ((
!NtFlags,
DBG_VERBOSE,
"Unsupported permission %u was translated to disable permission",
Win9xFlags
));
return NtFlags;
}
NET_API_STATUS
MigNetShareAdd (
IN PTSTR ServerName,
IN DWORD Level,
IN PBYTE Buf,
OUT PDWORD ErrParam
)
/*++
Routine Description:
Our private version of NetShareAdd. The real NetShareAdd does not work
in GUI mode. We emulate the real thing carefully because maybe some day
it WILL work and we should use it.
For now, we write directly to the registry. (This function is a reverse-
engineer of the NetShareAdd function.)
Arguments:
ServerName - Always NULL
Level - Always 2
Buf - A pointer to a caller-allocated SHARE_INFO_2 buffer cast
as an PBYTE
ErrParam - Not supported
Return value:
The Win32 result
--*/
{
SHARE_INFO_2 *psi;
DWORD rc;
HKEY hKey = NULL, hKeyShares = NULL;
DWORD DontCare;
GROWBUFFER GrowBuf = GROWBUF_INIT;
//
// This function is for compatibility with NetShareAdd, because one day
// the real NetShareAdd might be improved to work in GUI mode setup.
//
if (Level != 2) {
return ERROR_INVALID_LEVEL;
}
psi = (SHARE_INFO_2 *) Buf;
rc = TrackedRegOpenKeyEx (
HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services",
0,
KEY_WRITE,
&hKey
);
if (rc != ERROR_SUCCESS) {
goto cleanup;
}
rc = TrackedRegCreateKeyEx (
hKey,
SHARES_REGISTRY_PATH,
0,
S_EMPTY,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKeyShares,
&DontCare
);
if (rc != ERROR_SUCCESS) {
goto cleanup;
}
//
// Prepare multisz
//
if (!MultiSzAppendVal (&GrowBuf, CSCFLAGS_VARIABLE_NAME, 0)) {
rc = GetLastError();
goto cleanup;
}
if (!MultiSzAppendVal (&GrowBuf, MAXUSES_VARIABLE_NAME, psi->shi2_max_uses)) {
rc = GetLastError();
goto cleanup;
}
if (!psi->shi2_path || !(*psi->shi2_path)) {
rc = ERROR_INVALID_PARAMETER;
goto cleanup;
}
if (!MultiSzAppendString (&GrowBuf, PATH_VARIABLE_NAME, psi->shi2_path)) {
rc = GetLastError();
goto cleanup;
}
if (!MultiSzAppendVal (&GrowBuf, PERMISSIONS_VARIABLE_NAME, psi->shi2_permissions)) {
rc = GetLastError();
goto cleanup;
}
if (psi->shi2_remark && *psi->shi2_remark) {
// Safety
if (TcharCount (psi->shi2_remark) >= MAXCOMMENTSZ) {
psi->shi2_remark[MAXCOMMENTSZ-1] = 0;
}
if (!MultiSzAppendString (&GrowBuf, REMARK_VARIABLE_NAME, psi->shi2_remark)) {
rc = GetLastError();
goto cleanup;
}
}
if (!MultiSzAppendVal (&GrowBuf, TYPE_VARIABLE_NAME, psi->shi2_type)) {
rc = GetLastError();
goto cleanup;
}
// terminate multi-sz string chain
if (!MultiSzAppend (&GrowBuf, S_EMPTY)) {
rc = GetLastError();
goto cleanup;
}
//
// Save to registry
//
rc = RegSetValueEx (hKeyShares, psi->shi2_netname, 0, REG_MULTI_SZ,
GrowBuf.Buf, GrowBuf.End);
cleanup:
if (hKeyShares) {
CloseRegKey (hKeyShares);
}
if (hKey) {
CloseRegKey (hKey);
}
FreeGrowBuffer (&GrowBuf);
return rc;
}
NET_API_STATUS
MigNetShareSetInfo (
IN PTSTR Server, // ignored
IN PTSTR NetName,
IN DWORD Level,
IN PBYTE Buf,
OUT PDWORD ErrParam // ignored
)
/*++
Routine Description:
MigNetShareSetInfo implements a NetShareSetInfo emulation routine, because
the real routine does not work properly in GUI mode setup. See SDK
documentation for details.
Arguments:
Server - Unused
NetName - Specifies the share name to create.
Level - Specifies the API level (must be 1501)
Buf - Specifies a filled SHARE_INFO_1501 structure.
ErrParam - Unused
Return Value:
The Win32 status code.
--*/
{
SHARE_INFO_1501 *psi;
DWORD rc;
HKEY hKey;
DWORD DontCare;
TCHAR KeyName[MAX_TCHAR_PATH];
DWORD Len;
if (Level != 1501) {
return ERROR_INVALID_LEVEL;
}
psi = (SHARE_INFO_1501 *) Buf;
//
// Verify share exists
//
StringCopyW (
KeyName,
L"SYSTEM\\CurrentControlSet\\Services\\" SHARES_REGISTRY_PATH
);
rc = TrackedRegOpenKeyEx (
HKEY_LOCAL_MACHINE,
KeyName,
0,
KEY_READ,
&hKey
);
if (rc != ERROR_SUCCESS) {
return rc;
}
rc = RegQueryValueEx (hKey, NetName, NULL, NULL, NULL, NULL);
CloseRegKey (hKey);
if (rc != ERROR_SUCCESS) {
if (rc == ERROR_FILE_NOT_FOUND) {
rc = ERROR_INVALID_SHARENAME;
}
return rc;
}
//
// Save security descriptor as binary type in registry
//
StringCopy (
KeyName,
L"SYSTEM\\CurrentControlSet\\Services\\" SHARES_SECURITY_REGISTRY_PATH
);
rc = TrackedRegCreateKeyEx (
HKEY_LOCAL_MACHINE,
KeyName,
0,
S_EMPTY,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&DontCare
);
if (rc != ERROR_SUCCESS) {
return rc;
}
Len = GetSecurityDescriptorLength (psi->shi1501_security_descriptor);
rc = RegSetValueEx (
hKey,
NetName,
0,
REG_BINARY,
(PBYTE) psi->shi1501_security_descriptor,
Len
);
CloseRegKey (hKey);
return rc;
}
BOOL
pCreateNetShare (
IN PCTSTR NetName,
IN PCTSTR Path,
IN PCTSTR Remark,
IN DWORD Type,
IN DWORD Permissions
)
/*++
Routine Description:
pCreateNetShare is a wrapper to the Net APIs.
Arguments:
NetName - Specifies the share name
Path - Specifies the local path to be shared
Remark - Specifies the remark to register with the share
Type - Specifies the share type
Permissions - Specifies the Win9x share permissions, used only for logging
errors.
Return Value:
TRUE if the share was created, FALSE otherwise.
--*/
{
SHARE_INFO_2 si2;
DWORD rc;
PWSTR UnicodePath;
BOOL b = FALSE;
UnicodePath = (PWSTR) CreateUnicode (Path);
MYASSERT (UnicodePath);
__try {
//
// Make NetShareAdd call
//
ZeroMemory (&si2, sizeof (si2));
si2.shi2_netname = (PTSTR) NetName;
si2.shi2_type = (WORD) Type;
si2.shi2_remark = (PTSTR) Remark;
si2.shi2_permissions = 0;
si2.shi2_max_uses = 0xffffffff;
si2.shi2_path = UnicodePath;
si2.shi2_passwd = NULL;
rc = MigNetShareAdd (NULL, 2, (PBYTE) (&si2), NULL);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
DEBUGMSG ((
DBG_ERROR,
"CreateShares: NetShareAdd failed for %s ('%s'), permissions=%x",
NetName,
Path,
Permissions
));
if (Permissions == W95_GENERIC_NONE) {
LOG ((LOG_ERROR, (PCSTR)MSG_UNABLE_TO_CREATE_ACL_SHARE, NetName, Path));
} else if (Permissions != W95_GENERIC_READ) {
LOG ((LOG_ERROR, (PCSTR)MSG_UNABLE_TO_CREATE_RO_SHARE, NetName, Path));
} else {
LOG ((LOG_ERROR, (PCSTR)MSG_UNABLE_TO_CREATE_SHARE, NetName, Path));
}
__leave;
}
b = TRUE;
}
__finally {
DestroyUnicode (UnicodePath);
}
return b;
}
VOID
LogUsersWhoFailed (
PBYTE AclMemberList,
DWORD Members,
PCTSTR Share,
PCTSTR Path
)
/*++
Routine Description:
LogUsersWhoFailed implements logic to log the users who could not be added
to a share. If there are a small number of users, a popup is given with
each name. Otherwise, the share users are logged, and a popup tells the
installer to look in the log for the list.
Arguments:
AclMemberList - Specifies the ACL data structure containing all the user
names that need to be logged.
Members - Specifies the number of members in AclMemberList.
Share - Specifies the share name that could not be added.
Path - Specifies the share path that could not be added.
Return Value:
None.
--*/
{
PACLMEMBER AclMember;
DWORD d;
DWORD GoodCount;
DWORD BadCount;
HWND Parent;
GoodCount = 0;
BadCount = 0;
AclMember = (PACLMEMBER) AclMemberList;
for (d = 0 ; d < Members ; d++) {
if (AclMember->Failed) {
BadCount++;
} else {
GoodCount++;
}
GetNextAclMember (&AclMember);
}
if (!BadCount) {
return;
}
if (BadCount < 5) {
Parent = g_ParentWnd;
} else {
if (!GoodCount) {
LOG ((LOG_ERROR, (PCSTR)MSG_ALL_SIDS_BAD, Share, Path));
} else {
LOG ((LOG_ERROR, (PCSTR)MSG_MANY_SIDS_BAD,
BadCount, BadCount + GoodCount, Share, Path));
}
Parent = NULL;
}
AclMember = (PACLMEMBER) AclMemberList;
for (d = 0 ; d < Members ; d++) {
if (AclMember->Failed) {
LOG ((LOG_ERROR, (PCSTR)MSG_NO_USER_SID, AclMember->UserOrGroup, Share, Path));
}
GetNextAclMember (&AclMember);
}
}
BOOL
SetShareAcl (
IN PCTSTR Share,
IN PCTSTR Path,
IN PBYTE AclMemberList,
IN DWORD MemberCount
)
/*++
Routine Description:
SetShareAcl applies the access control list to a share that was previously
created.
Arguments:
Share - Specifies the share name
Path - Specifies the share path
AclMemberList - Specifies the ACL data structure giving the user(s) with
rights to the share
MemberCount - Specifies the number of members in AclMemberList.
Return Value:
TRUE if the ACL was successfully applied to the share, FALSE otherwise.
--*/
{
BYTE Buf[8192];
PSECURITY_DESCRIPTOR pSD;
SECURITY_DESCRIPTOR desc;
PSID Sid;
PACL Acl;
SHARE_INFO_1501 shi1501;
DWORD rc;
DWORD Size;
PWSTR UnicodeShare;
pSD = (PSECURITY_DESCRIPTOR) Buf;
//
// Get Administrator's SID--they are the owner of the share
//
Sid = GetSidForUser (g_AdministratorsGroupStr);
if (!Sid) {
return FALSE;
}
//
// Start building security descriptor
//
InitializeSecurityDescriptor (&desc, SECURITY_DESCRIPTOR_REVISION);
if (!SetSecurityDescriptorOwner (&desc, Sid, FALSE)) {
LOG ((LOG_ERROR, "Could not set %s as owner", g_AdministratorsGroupStr));
return FALSE;
}
//
// Set the defaulted group to Domain\Domain Users (if it exists),
// otherwise get SID of none
//
Sid = GetSidForUser (g_DomainUsersGroupStr);
if (!Sid) {
Sid = GetSidForUser (g_NoneGroupStr);
}
if (Sid) {
SetSecurityDescriptorGroup (&desc, Sid, FALSE);
}
//
// Create access allowed ACL from member list
//
Acl = CreateAclFromMemberList (AclMemberList, MemberCount);
if (!Acl) {
DEBUGMSG ((DBG_WARNING, "SetShareAcl failed because CreateAclFromMemberList failed"));
return FALSE;
}
__try {
UnicodeShare = (PWSTR) CreateUnicode (Share);
MYASSERT (UnicodeShare);
if (!SetSecurityDescriptorDacl (&desc, TRUE, Acl, FALSE)) {
DEBUGMSG ((DBG_WARNING, "SetShareAcl failed because SetSecurityDescriptorDacl failed"));
return FALSE;
}
//
// Set security descriptor on share
//
Size = sizeof (Buf);
if (!MakeSelfRelativeSD (&desc, pSD, &Size)) {
LOG ((LOG_ERROR, "MakeSelfRelativeSD failed"));
return FALSE;
}
ZeroMemory (&shi1501, sizeof (shi1501));
shi1501.shi1501_security_descriptor = pSD;
rc = MigNetShareSetInfo (NULL, UnicodeShare, 1501, (PBYTE) &shi1501, NULL);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
LOG ((LOG_ERROR, "NetShareSetInfo failed"));
return FALSE;
}
}
__finally {
if (Acl) {
FreeMemberListAcl (Acl);
}
DestroyUnicode (UnicodeShare);
}
return TRUE;
}
VOID
DoCreateShares (
VOID
)
/*++
Routine Description:
DoCreateShares enumerates all the shares registered in memdb by WINNT32.
For each enumeration, a share is created, and permissions or an ACL is
applied.
Arguments:
None.
Return Value:
None.
--*/
{
MEMDB_ENUM e, e2;
TCHAR Path[MEMDB_MAX];
TCHAR Remark[MEMDB_MAX];
// we overlap the Remark stack buffer instead of making another
#define flagKey Remark
TCHAR Password[MEMDB_MAX];
DWORD Flags;
DWORD Members;
DWORD shareType;
GROWBUFFER NameList = GROWBUF_INIT;
PCTSTR pathNT;
//
// Obtain shares from memdb
//
if (MemDbEnumItems (&e, MEMDB_CATEGORY_NETSHARES)) {
do {
//
// Get share attributes
//
Flags = e.dwValue;
if (!MemDbGetEndpointValueEx (
MEMDB_CATEGORY_NETSHARES,
e.szName,
MEMDB_FIELD_PATH,
Path
)) {
DEBUGMSG ((DBG_WARNING, "DoCreateShares: No path found for %s", e.szName));
continue;
}
// IF YOU CHANGE CODE HERE: Note that flagKey is the same variable as Remark
MemDbBuildKey (flagKey, MEMDB_CATEGORY_NETSHARES, e.szName, MEMDB_FIELD_TYPE, NULL);
if (!MemDbGetValue (flagKey, &shareType)) {
DEBUGMSG ((DBG_WARNING, "DoCreateShares: No type found for %s", e.szName));
continue;
}
// IF YOU CHANGE CODE HERE: Note that flagKey is the same variable as Remark
if (!MemDbGetEndpointValueEx (
MEMDB_CATEGORY_NETSHARES,
e.szName,
MEMDB_FIELD_REMARK,
Remark
)) {
Remark[0] = 0;
}
//
// first check if the path changed
//
pathNT = GetPathStringOnNt (Path);
//
// Create the share and set appropriate security
//
if (Flags & SHI50F_ACLS) {
//
// Share has an ACL
//
if (pCreateNetShare (e.szName, pathNT, Remark, shareType, W95_GENERIC_NONE)) {
//
// For each user indexed, put them in an ACL member list
//
Members = 0;
if (MemDbGetValueEx (
&e2,
MEMDB_CATEGORY_NETSHARES,
e.szName,
MEMDB_FIELD_ACCESS_LIST
)) {
do {
//
// On Win9x, per-user flags have a 8 flags that control access. We translate
// them to one of four flavors on NT:
//
// 1. Deny All Access: (Flags == 0)
// 2. Read-Only Access: (Flags & W95_GENERIC_READ) && !(Flags & W95_GENERIC_WRITE)
// 3. Change-Only Access: !(Flags & W95_GENERIC_READ) && (Flags & W95_GENERIC_WRITE)
// 4. Full Access: (Flags & W95_GENERIC_FULL) == W95_GENERIC_FULL
//
DEBUGMSG ((DBG_NETSHARES, "Share %s user %s flags %u", e.szName, e2.szName, e2.dwValue));
if (AddAclMember (
&NameList,
e2.szName,
Simplify9xAccessFlags (e2.dwValue)
)) {
Members++;
}
} while (MemDbEnumNextValue (&e2));
}
//
// Convert member list into a real ACL and apply it to the share
//
if (NameList.Buf) {
SetShareAcl (e.szName, pathNT, NameList.Buf, Members);
LogUsersWhoFailed (NameList.Buf, Members, e.szName, pathNT);
FreeGrowBuffer (&NameList);
}
}
}
else {
//
// Determine if a password is set
//
Password[0] = 0;
if (Flags & SHI50F_RDONLY) {
MemDbGetEndpointValueEx (
MEMDB_CATEGORY_NETSHARES,
e.szName,
MEMDB_FIELD_RO_PASSWORD,
Password
);
}
if (!Password[0] && (Flags & SHI50F_FULL)) {
MemDbGetEndpointValueEx (
MEMDB_CATEGORY_NETSHARES,
e.szName,
MEMDB_FIELD_RW_PASSWORD,
Password
);
}
//
// Enable all permissions for full access
// Enable read-only permissions for read-only shares
// Disable all permissions for no access
//
if (!Password[0]) {
if (Flags & SHI50F_FULL) {
Flags = W95_GENERIC_FULL;
} else if (Flags & SHI50F_RDONLY) {
Flags = W95_GENERIC_READ;
} else if (Flags) {
DEBUGMSG ((DBG_WHOOPS, "Flags (0x%X) is not 0, SHI50F_FULL or SHI50F_RDONLY", Flags));
Flags = W95_GENERIC_NONE;
}
} else {
DEBUGMSG ((DBG_VERBOSE, "Password on share %s is not supported", e.szName));
Flags = W95_GENERIC_NONE;
}
//
// We do not support share-level security with passwords. We
// always create the share, but if a password exists, we
// deny everyone access.
//
pCreateNetShare (e.szName, pathNT, Remark, shareType, Flags);
Members = 0;
if (AddAclMember (
&NameList,
g_EveryoneStr,
Simplify9xAccessFlags (Flags)
)) {
Members++;
}
//
// Convert member list into a real ACL and apply it to the share
//
if (NameList.Buf) {
SetShareAcl (e.szName, pathNT, NameList.Buf, Members);
FreeGrowBuffer (&NameList);
}
}
FreePathString (pathNT);
} while (MemDbEnumNextValue (&e));
}
}
BOOL
pUpdateRecycleBin (
VOID
)
/*++
Routine Description:
Calls SHUpdateRecycleBinIcon to reset the status of the recycle bin. This
operation takes a few seconds as all hard drives are scanned, the recycle
bin database is read, and each entry in the database is verified.
Arguments:
None
Return value:
TRUE - the operation was successful
FALSE - the operation failed (either LoadLibrary or GetProcAddress)
--*/
{
SHUPDATERECYCLEBINICON_PROC Fn;
HINSTANCE LibInst;
BOOL b = TRUE;
LibInst = LoadLibrary (S_SHELL32_DLL);
if (!LibInst) {
return FALSE;
}
Fn = (SHUPDATERECYCLEBINICON_PROC) GetProcAddress (
LibInst,
S_ANSI_SHUPDATERECYCLEBINICON
);
if (Fn) {
//
// Scan all hard disks and validate the recycle bin status
//
Fn();
} else {
b = FALSE;
}
FreeLibrary (LibInst);
return TRUE;
}
VOID
pFixLogonDomainIfUserIsAdministrator (
VOID
)
/*++
Routine Description:
pFixLogonDomainIfUserIsAdministrator handles a special error case where
the logon domain is not equivalent to the computer name, but the user
is named Administrator. In this case, we change the default logon domain
to be the computer name.
Arguments:
None
Return value:
none
--*/
{
PCTSTR AdministratorAcct;
HKEY Key;
PCTSTR Data;
AdministratorAcct = GetStringResource (MSG_ADMINISTRATOR_ACCOUNT);
if (AdministratorAcct) {
Key = OpenRegKeyStr (S_WINLOGON_KEY);
if (Key) {
Data = GetRegValueString (Key, S_DEFAULT_USER_NAME);
if (Data) {
if (!StringCompare (Data, AdministratorAcct)) {
//
// Account name exactly matches our Administrator
// string, so there is a good chance we wrote
// this string. Therefore, we need to write the
// computer name as the default domain.
//
if (g_ComputerName[0]) {
RegSetValueEx (
Key,
S_DEFAULT_DOMAIN_NAME,
0,
REG_SZ,
(PBYTE) g_ComputerName,
SizeOfString (g_ComputerName)
);
}
}
MemFree (g_hHeap, 0, Data);
}
CloseRegKey (Key);
}
FreeStringResource (AdministratorAcct);
}
}
DWORD
ProcessLocalMachine_First (
DWORD Request
)
{
if (Request == REQUEST_QUERYTICKS) {
return TICKS_INI_ACTIONS_FIRST +
TICKS_INI_MOVE +
TICKS_INI_CONVERSION +
TICKS_INI_MIGRATION;
}
//
// We process the local machine in the following order:
//
// Initialization:
// (1) Reload memdb
//
// Ini files conversion and mapping
//
DEBUGMSG ((DBG_INIFILES, "INI Files Actions.First - START"));
DEBUGLOGTIME (("Starting function: DoIniActions"));
if (!DoIniActions (INIACT_WKS_FIRST)) {
LOG ((LOG_ERROR, "Process Local Machine: Could not perform one or more INI Files Actions.First"));
}
DEBUGLOGTIME (("Function complete: DoIniActions"));
TickProgressBarDelta (TICKS_INI_ACTIONS_FIRST);
DEBUGMSG ((DBG_INIFILES, "INI Files Actions.First - STOP"));
DEBUGMSG ((DBG_INIFILES, "INI file moving - START"));
DEBUGLOGTIME (("Starting function: MoveIniSettings"));
if (!MoveIniSettings ()) {
LOG ((LOG_ERROR, "Process Local Machine: Could not move one or more .INI files settings."));
return GetLastError();
}
DEBUGLOGTIME (("Function complete: MoveIniSettings"));
TickProgressBarDelta (TICKS_INI_MOVE);
DEBUGMSG ((DBG_INIFILES, "INI file moving - STOP"));
DEBUGMSG ((DBG_INIFILES, "INI file conversion - START"));
DEBUGLOGTIME (("Starting function: ConvertIniFiles"));
if (!ConvertIniFiles ()) {
LOG ((LOG_ERROR, "Process Local Machine: Could not convert one or more .INI files."));
return GetLastError();
}
DEBUGLOGTIME (("Function complete: ConvertIniFiles"));
TickProgressBarDelta (TICKS_INI_CONVERSION);
DEBUGMSG ((DBG_INIFILES, "INI file conversion - STOP"));
DEBUGMSG ((DBG_INIFILES, "INI file migration - START"));
DEBUGLOGTIME (("Starting function: ProcessIniFileMapping"));
if (!ProcessIniFileMapping (FALSE)) {
LOG ((LOG_ERROR, "Process Local Machine: Could not migrate one or more .INI files."));
return GetLastError();
}
DEBUGLOGTIME (("Function complete: ProcessIniFileMapping"));
TickProgressBarDelta (TICKS_INI_MIGRATION);
DEBUGMSG ((DBG_INIFILES, "INI file migration - STOP"));
return ERROR_SUCCESS;
}
VOID
pTurnOffNetAccountWizard (
VOID
)
/*++
Routine Description:
pTurnOffNetAccountWizard removes the RunNetAccessWizard key to keep the
network account wizard from appearing before the first logon.
Arguments:
None.
Return Value:
None.
--*/
{
HKEY Key;
Key = OpenRegKeyStr (TEXT("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"));
if (Key) {
RegDeleteValue (Key, TEXT("RunNetAccessWizard"));
CloseRegKey (Key);
} else {
DEBUGMSG ((DBG_WARNING, "Could not open key for RunNetAccessWizard value"));
}
}
typedef struct _OLE_CONTROL_DATA {
LPWSTR FullPath;
LPCWSTR RegType;
} OLE_CONTROL_DATA, *POLE_CONTROL_DATA;
DWORD
RegisterIndividualOleControl(
POLE_CONTROL_DATA OleControlData
)
{
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
WCHAR cmdLine [MAX_PATH] = L"";
WCHAR cmdOptions [MAX_PATH] = L"";
DWORD WaitResult;
BOOL b = TRUE;
ZeroMemory (&startupInfo, sizeof (STARTUPINFO));
startupInfo.cb = sizeof (STARTUPINFO);
if (OleControlData->RegType && (*OleControlData->RegType == L'B')) {
// install and register
wcscpy (cmdOptions, L"/s /i");
} else if (OleControlData->RegType && (*OleControlData->RegType == L'R')) {
// register
wcscpy (cmdOptions, L"/s");
} else if (OleControlData->RegType && (*OleControlData->RegType == L'I')) {
// install
wcscpy (cmdOptions, L"/s /i /n");
} else if ((OleControlData->RegType == NULL) || (*OleControlData->RegType == L'\0')) {
// register
wcscpy (cmdOptions, L"/s");
}
wsprintf (cmdLine, L"%s\\regsvr32.exe %s %s", g_System32Dir, cmdOptions, OleControlData->FullPath);
if (CreateProcess (NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &startupInfo, &processInfo)) {
WaitResult = WaitForSingleObject (processInfo.hProcess, 1000 * 60 * 10 );
if (WaitResult == WAIT_TIMEOUT) {
DEBUGMSG ((DBG_ERROR, "Timeout installing and/or registering OLE control %s", OleControlData->FullPath));
b = FALSE;
}
CloseHandle (processInfo.hProcess);
CloseHandle (processInfo.hThread);
}
else {
DEBUGMSG ((DBG_ERROR, "Create process failed: %s", cmdLine));
b = FALSE;
}
return b;
}
typedef struct _KNOWN_DIRS {
PCWSTR DirId;
PCWSTR Translation;
}
KNOWN_DIRSW, *PKNOWN_DIRSW;
KNOWN_DIRSW g_KnownDirsW [] = {
{L"10" , g_WinDir},
{L"11" , g_System32Dir},
{L"24" , g_WinDrive},
{L"16422" , g_ProgramFiles},
{L"16427" , g_ProgramFilesCommon},
{NULL, NULL}
};
BOOL
pConvertDirName (
PCWSTR OldDirName,
PWSTR NewDirName
)
{
PCWSTR OldDirCurr = OldDirName;
PCWSTR OldDirNext;
PKNOWN_DIRSW p;
NewDirName[0] = 0;
OldDirNext = wcschr (OldDirCurr, L'\\');
if (OldDirNext == NULL) {
OldDirNext = wcschr (OldDirCurr, 0);
}
StringCopyABW (NewDirName, OldDirCurr, OldDirNext);
p = g_KnownDirsW;
while (p->DirId!= NULL) {
if (StringIMatchW (NewDirName, p->DirId)) {
StringCopyW (NewDirName, p->Translation);
break;
}
p++;
}
StringCatW (NewDirName, OldDirNext);
return TRUE;
}
BOOL
RegisterOleControls(
VOID
)
{
INFCONTEXT InfLine;
WCHAR DirId [MAX_PATH];
WCHAR SubDir [MAX_PATH];
WCHAR Filename [MAX_PATH];
WCHAR RegType [MAX_PATH];
WCHAR FullPathTemp[MAX_PATH];
WCHAR FullPath[MAX_PATH];
BOOL b;
DWORD d;
UINT Line;
WCHAR OldCD[MAX_PATH];
OLE_CONTROL_DATA OleControlData;
b = TRUE;
Line = 0;
//
// Preserve current directory just in case
//
d = GetCurrentDirectory(MAX_PATH,OldCD);
if(!d || (d >= MAX_PATH)) {
OldCD[0] = 0;
}
if(SetupFindFirstLine(g_WkstaMigInf, L"Win9xUpg_OleControls", NULL, &InfLine)) {
do {
Line++;
if (!SetupGetStringField (&InfLine, 1, DirId, MAX_PATH, NULL) ||
!SetupGetStringField (&InfLine, 2, SubDir, MAX_PATH, NULL) ||
!SetupGetStringField (&InfLine, 3, Filename, MAX_PATH, NULL) ||
!SetupGetStringField (&InfLine, 4, RegType, MAX_PATH, NULL)
) {
DEBUGMSGW ((DBG_ERROR, "Bad line while registering controls %d", Line));
} else {
DEBUGMSG ((DBG_VERBOSE, "SETUP: filename for file to register is %s", Filename));
//
// Get full path to dll
//
if (pConvertDirName (DirId, FullPathTemp)) {
wcscpy (FullPath, FullPathTemp);
if (*SubDir) {
wcscat (FullPath, L"\\");
wcscat (FullPath, SubDir);
}
SetCurrentDirectory(FullPath);
wcscat (FullPath, L"\\");
wcscat (FullPath, Filename);
OleControlData.FullPath = FullPath;
OleControlData.RegType = RegType;
RegisterIndividualOleControl (&OleControlData);
} else {
DEBUGMSG ((DBG_ERROR, "SETUP: dll skipped, bad dirid %s", DirId));
b = FALSE;
}
}
} while(SetupFindNextLine(&InfLine,&InfLine));
}
if(OldCD[0]) {
SetCurrentDirectory(OldCD);
}
return(b);
}
DWORD
ProcessLocalMachine_Last (
DWORD Request
)
{
DWORD rc;
#ifdef VAR_PROGRESS_BAR
CHAR SystemDatPath[MAX_MBCHAR_PATH];
WIN32_FIND_DATAA fd;
HANDLE h;
DWORD SizeKB;
#endif
static LONG g_TicksHklm;
if (Request == REQUEST_QUERYTICKS) {
#ifdef VAR_PROGRESS_BAR
//
// estimate g_TicksHklm function of size of file system.dat
//
StringCopyA (SystemDatPath, g_SystemHiveDir);
StringCatA (SystemDatPath, "system.dat");
h = FindFirstFileA (SystemDatPath, &fd);
if (h != INVALID_HANDLE_VALUE) {
FindClose (h);
MYASSERT (!fd.nFileSizeHigh);
SizeKB = (fd.nFileSizeLow + 511) / 1024;
DEBUGLOGTIME (("ProcessLocalMachine_Last: system.dat size = %lu KB", SizeKB));
//
// statistics show that average time is 243 * (filesize in KB) - 372000
// I'll use 256 instead just to make sure the progress bar will not stop
// at the end looking like it's hanged
// The checked build is much slower (about 1.5 times)
//
#ifdef DEBUG
g_TicksHklm = SizeKB * 400;
#else
g_TicksHklm = SizeKB * 256;
#endif
} else {
//
// what's wrong here?
//
MYASSERT (FALSE);
g_TicksHklm = TICKS_HKLM;
}
#else // !defined VAR_PROGRESS_BAR
g_TicksHklm = TICKS_HKLM;
#endif
return TICKS_INI_MERGE +
g_TicksHklm +
TICKS_SHARES +
TICKS_LINK_EDIT +
TICKS_DOSMIG_SYS +
TICKS_UPDATERECYCLEBIN +
TICKS_STF +
TICKS_RAS +
TICKS_TAPI +
TICKS_MULTIMEDIA +
TICKS_INI_ACTIONS_LAST;
}
//
// We process the local machine in the following order:
//
// Initialization:
// (1) Reload memdb
//
// Local machine registry preparation:
//
// (1) Process wkstamig.inf
// (2) Merge Win95 registry with NT hive
//
// Process instructions written to memdb:
//
// (1) Create Win95 shares
// (2) Process LinkEdit section
//
//
// Load in default MemDb state, or at least delete everything if
// memdb.dat does not exist.
//
MemDbLoad (GetMemDbDat());
DEBUGMSG ((DBG_INIFILES, "INI file merge - START"));
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: MergeIniSettings"));
if (!MergeIniSettings ()) {
LOG ((LOG_ERROR, "Process Local Machine: Could not merge one or more .INI files."));
return GetLastError();
}
TickProgressBarDelta (TICKS_INI_MERGE);
DEBUGMSG ((DBG_INIFILES, "INI file merge - STOP"));
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: MergeIniSettings"));
//
// Process local machine migration rules
//
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: MergeRegistry"));
if (!MergeRegistry (S_WKSTAMIG_INF, NULL)) {
LOG ((LOG_ERROR, "Process Local Machine: MergeRegistry failed for wkstamig.inf"));
return GetLastError();
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: MergeRegistry"));
TickProgressBarDelta (g_TicksHklm);
//
// Process memdb nodes
//
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DoCreateShares"));
DoCreateShares(); // we ignore all errors
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DoCreateShares"));
TickProgressBarDelta (TICKS_SHARES);
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DoLinkEdit"));
if (!DoLinkEdit()) {
LOG ((LOG_ERROR, "Process Local Machine: DoLinkEdit failed."));
return GetLastError();
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DoLinkEdit"));
TickProgressBarDelta (TICKS_LINK_EDIT);
//
// Handle DOS system migration.
//
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DosMigNt_System"));
__try {
if (DosMigNt_System() != EXIT_SUCCESS) {
LOG((LOG_ERROR, "Process Local Machine: DosMigNt_System failed."));
}
}
__except(TRUE) {
DEBUGMSG ((DBG_WHOOPS, "Exception in DosMigNt_System"));
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DosMigNt_System"));
TickProgressBarDelta (TICKS_DOSMIG_SYS);
//
// Make the recycled bin the correct status
//
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: pUpdateRecycleBin"));
if (!pUpdateRecycleBin ()) {
LOG ((LOG_ERROR, "Process Local Machine: Could not update recycle bin."));
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: pUpdateRecycleBin"));
TickProgressBarDelta (TICKS_UPDATERECYCLEBIN);
//
// Migrate all .STF files (ACME Setup)
//
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: ProcessStfFiles"));
if (!ProcessStfFiles()) {
LOG ((LOG_ERROR, "Process Local Machine: Could not migrate one or more .STF files."));
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: ProcessStfFiles"));
TickProgressBarDelta (TICKS_STF);
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: Ras_MigrateSystem"));
if (!Ras_MigrateSystem()) {
LOG ((LOG_ERROR, "Ras MigrateSystem: Error migrating system."));
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: Ras_MigrateSystem"));
TickProgressBarDelta (TICKS_RAS);
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: Tapi_MigrateSystem"));
if (!Tapi_MigrateSystem()) {
LOG ((LOG_ERROR, "Tapi MigrateSystem: Error migrating system TAPI settings."));
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: Tapi_MigrateSystem"));
TickProgressBarDelta (TICKS_TAPI);
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: RestoreMMSettings_System"));
if (!RestoreMMSettings_System ()) {
LOG ((LOG_ERROR, "Process Local Machine: Could not restore multimedia settings."));
return GetLastError();
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: RestoreMMSettings_System"));
TickProgressBarDelta (TICKS_MULTIMEDIA);
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DoIniActions.Last"));
DEBUGMSG ((DBG_INIFILES, "INI Files Actions.Last - START"));
if (!DoIniActions (INIACT_WKS_LAST)) {
LOG ((LOG_ERROR, "Process Local Machine: Could not perform one or more INI Files Actions.Last"));
}
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DoIniActions.Last"));
TickProgressBarDelta (TICKS_INI_ACTIONS_LAST);
DEBUGMSG ((DBG_INIFILES, "INI Files Actions.Last - STOP"));
DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: RegisterOleControls"));
RegisterOleControls ();
DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: RegisterOleControls"));
//
// Blow away the Network Account Wizard (so fast it doesn't need ticks)
//
pTurnOffNetAccountWizard();
//
// Update security for Crypto group
//
rc = SetRegKeySecurity (
TEXT("HKLM\\Software\\Microsoft\\Cryptography\\MachineKeys"),
SF_EVERYONE_FULL,
NULL,
NULL,
TRUE
);
return ERROR_SUCCESS;
}
BOOL
pEnumWin9xHiveFileWorker (
IN OUT PHIVEFILE_ENUM EnumPtr
)
/*++
Routine Description:
pEnumWin9xHiveFileWorker parses wkstamig.inf to get the source path to a
Win9x registry hive, and it gets the destination of where the hive should
be migrated to. The source is tested, and if it doesn't exist, the
function returns FALSE.
Environment variables in both the source or dest are expanded before
this function returns success.
Arguments:
EnumPtr - Specifies partially completed enumeration structure (the
EnumPtr->is member must be valid). Receives the source and
destination of the hive to be migrated.
Return Value:
Returns TRUE if a hive needs to be transfered from EnumPtr->Source to
EnumPtr->Dest, otherwise FALSE.
--*/
{
PCTSTR Source;
PCTSTR Dest;
//
// Get the source and dest from the INF
//
Source = InfGetStringField (&EnumPtr->is, 0);
Dest = InfGetStringField (&EnumPtr->is, 1);
if (!Source || !Dest) {
DEBUGMSG ((DBG_WHOOPS, "wkstamig.inf HiveFilesToConvert is not correct"));
return FALSE;
}
//
// Expand the source and dest
//
if (EnumPtr->Source) {
FreeText (EnumPtr->Source);
}
EnumPtr->Source = ExpandEnvironmentText (Source);
if (EnumPtr->Dest) {
FreeText (EnumPtr->Dest);
}
EnumPtr->Dest = ExpandEnvironmentText (Dest);
//
// The source must exist
//
if (!DoesFileExist (EnumPtr->Source)) {
return FALSE;
}
return TRUE;
}
VOID
pAbortHiveFileEnum (
IN OUT PHIVEFILE_ENUM EnumPtr
)
/*++
Routine Description:
pAbortHiveFileEnum cleans up the allocations from an active enumeration of
Win9x hive files. This routine must be called by the enum first/next when
the enumeration completes, or it must be called by the code using the
enumeration. It is safe to call this routine in both places.
Arguments:
EnumPtr - Specifies the enumeration that needs to be aborted or that has
completed successfully. Receives a zero'd struct.
Return Value:
None.
--*/
{
if (EnumPtr->Pool) {
PoolMemDestroyPool (EnumPtr->Pool);
}
if (EnumPtr->Source) {
FreeText (EnumPtr->Source);
}
if (EnumPtr->Dest) {
FreeText (EnumPtr->Dest);
}
ZeroMemory (EnumPtr, sizeof (HIVEFILE_ENUM));
}
BOOL
pEnumNextWin9xHiveFile (
IN OUT PHIVEFILE_ENUM EnumPtr
)
/*++
Routine Description:
pEnumNextWin9xHiveFile continues the enumeration wksatmig.inf until either
a hive needing migration is found, or no more INF entries are left.
Arguments:
EnumPtr - Specifies an enumeration structure that was initialized by
pEnumFirstWin9xHiveFile. Receives the next hive file source &
dest enumeration if one is available.
Return Value:
TRUE if a Win9x hive file needs to be migrated (its source and dest
specified in EnumPtr). FALSE if no more hive files are to be processed.
--*/
{
do {
if (!InfFindNextLine (&EnumPtr->is)) {
pAbortHiveFileEnum (EnumPtr);
return FALSE;
}
} while (!pEnumWin9xHiveFileWorker (EnumPtr));
return TRUE;
}
BOOL
pEnumFirstWin9xHiveFile (
OUT PHIVEFILE_ENUM EnumPtr
)
/*++
Routine Description:
pEnumFirstWin9xHiveFile begins an enumeration of Win9x registry files that
need to be migrated either to the NT registry, or to an NT registry hive
file.
Arguments:
EnumPtr - Receives the source Win9x hive file and the destination (either a
file or a registry path).
Return Value:
TRUE if a Win9x hive file was found and needs to be migrated, FALSE if no
hive file migration is needed.
--*/
{
ZeroMemory (EnumPtr, sizeof (HIVEFILE_ENUM));
//
// Begin the enumeration of the Hive Files section of wkstamig.inf
//
EnumPtr->Pool = PoolMemInitNamedPool ("Hive File Enum");
InitInfStruct (&EnumPtr->is, NULL, EnumPtr->Pool);
if (!InfFindFirstLine (g_WkstaMigInf, S_WKSTAMIG_HIVE_FILES, NULL, &EnumPtr->is)) {
pAbortHiveFileEnum (EnumPtr);
return FALSE;
}
//
// Attempt to return the first hive
//
if (pEnumWin9xHiveFileWorker (EnumPtr)) {
return TRUE;
}
//
// Hive does not exist, continue enumeration
//
return pEnumNextWin9xHiveFile (EnumPtr);
}
BOOL
pTransferWin9xHiveToRegKey (
IN PCTSTR Win9xHive,
IN PCTSTR NtRootKey
)
/*++
Routine Description:
pTransferWin9xHiveToRegKey maps in a Win9x hive file, enumerates all the
keys and values, and transfers them to the NT registry.
Arguments:
Win9xHive - Specifies the registry hive file (a Win9x hive)
NtRootKey - Specifies the path to the NT registry destination, such as
HKLM\foo.
Return Value:
TRUE if the hive file was transferred without an error, FALSE otherwise.
Use GetLastError to get the error code.
--*/
{
LONG rc;
HKEY DestKey;
BOOL b = FALSE;
REGTREE_ENUM e;
REGVALUE_ENUM e2;
PCTSTR SubKey;
HKEY DestSubKey;
BOOL EnumAbort = FALSE;
PBYTE DataBuf;
GROWBUFFER Data = GROWBUF_INIT;
DWORD Type;
DWORD Size;
BOOL CloseDestSubKey = FALSE;
//
// Map the hive into a temporary key
//
rc = Win95RegLoadKey (
HKEY_LOCAL_MACHINE,
S_HIVE_TEMP,
Win9xHive
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Can't load %s for transfer", Win9xHive));
return FALSE;
}
__try {
DestKey = CreateRegKeyStr (NtRootKey);
if (!DestKey) {
DEBUGMSG ((DBG_ERROR, "Can't create %s", NtRootKey));
__leave;
}
if (EnumFirstRegKeyInTree95 (&e, TEXT("HKLM\\") S_HIVE_TEMP)) {
EnumAbort = TRUE;
do {
//
// Create the NT destination; if SubKey is empty, then
// use the root key for the destination.
//
SubKey = (PCTSTR) ((PBYTE) e.FullKeyName + e.EnumBaseBytes);
if (*SubKey) {
DestSubKey = CreateRegKey (DestKey, SubKey);
if (!DestSubKey) {
DEBUGMSG ((DBG_ERROR, "Can't create subkey %s", SubKey));
__leave;
}
CloseDestSubKey = TRUE;
} else {
DestSubKey = DestKey;
}
//
// Copy all values in 9x key to NT
//
if (EnumFirstRegValue95 (&e2, e.CurrentKey->KeyHandle)) {
do {
Data.End = 0;
DataBuf = GrowBuffer (&Data, e2.DataSize);
if (!DataBuf) {
DEBUGMSG ((DBG_ERROR, "Data size is too big: %s", e2.DataSize));
__leave;
}
Size = e2.DataSize;
rc = Win95RegQueryValueEx (
e2.KeyHandle,
e2.ValueName,
NULL,
&Type,
DataBuf,
&Size
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((
DBG_ERROR,
"Can't read enumerated value:\n"
" %s\n"
" %s [%s]",
Win9xHive,
e.FullKeyName,
e2.ValueName
));
__leave;
}
MYASSERT (Size == e2.DataSize);
rc = RegSetValueEx (
DestSubKey,
e2.ValueName,
0,
e2.Type,
DataBuf,
Size
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((
DBG_ERROR,
"Can't write enumerated value:\n"
" %s\n"
" %s\\%s [%s]",
Win9xHive,
NtRootKey,
SubKey,
e2.ValueName
));
__leave;
}
} while (EnumNextRegValue95 (&e2));
}
if (CloseDestSubKey) {
CloseRegKey (DestSubKey);
CloseDestSubKey = FALSE;
}
} while (EnumNextRegKeyInTree95 (&e));
EnumAbort = FALSE;
}
ELSE_DEBUGMSG ((DBG_WARNING, "%s is empty", Win9xHive));
b = TRUE;
}
__finally {
PushError();
if (CloseDestSubKey) {
CloseRegKey (DestSubKey);
}
if (EnumAbort) {
AbortRegKeyTreeEnum95 (&e);
}
Win95RegUnLoadKey (HKEY_LOCAL_MACHINE, TEXT("$$$"));
if (DestKey) {
CloseRegKey (DestKey);
}
FreeGrowBuffer (&Data);
PopError();
}
return b;
}
BOOL
pTransferWin9xHive (
IN PCTSTR Win9xHive,
IN PCTSTR Destination
)
/*++
Routine Description:
pTransferWin9xHive transfers a Win9x registry hive file (foo.dat) to either
an NT registry file, or a key in the NT registry. The source and
destination can be the same file.
Arguments:
Win9xHive - Specifies the registry hive file path (a Win9x hive file).
Destination - Specifies either a path or NT registry location where the
Win9xHive should be transfered to.
Return Value:
TRUE if the hive was transfered, FALSE otherwise. Call GetLastError for an
error code.
--*/
{
PCTSTR DestHive;
BOOL ToHiveFile;
HKEY Key;
LONG rc;
//
// Determine if destination is a hive file or a reg location
//
if (_istalpha (Destination[0]) && Destination[1] == TEXT(':')) {
ToHiveFile = TRUE;
DestHive = TEXT("HKLM\\") S_TRANSFER_HIVE;
} else {
ToHiveFile = FALSE;
DestHive = Destination;
}
//
// Blast the Win9x hive data to the temp location
//
if (!pTransferWin9xHiveToRegKey (Win9xHive, DestHive)) {
RegDeleteKey (HKEY_LOCAL_MACHINE, S_TRANSFER_HIVE);
return FALSE;
}
//
// Save the key if the destination is a hive file
//
if (ToHiveFile) {
Key = OpenRegKeyStr (DestHive);
if (!Key) {
DEBUGMSG ((DBG_ERROR, "Transfer hive key %s does not exist", DestHive));
return FALSE;
}
rc = RegSaveKey (Key, Destination, NULL);
CloseRegKey (Key);
RegDeleteKey (HKEY_LOCAL_MACHINE, S_TRANSFER_HIVE);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Win9x hive %s could not be saved to %s", Win9xHive, Destination));
return FALSE;
}
}
//
// Delete the source file if the destination is not the same
//
if (!ToHiveFile || !StringIMatch (Win9xHive, DestHive)) {
//
// By adding info to memdb, we must enforce a rule that
// memdb cannot be reloaded. Otherwise we lose our
// changes.
//
DeclareTemporaryFile (Win9xHive);
#ifdef DEBUG
g_NoReloadsAllowed = TRUE;
#endif
}
return TRUE;
}
DWORD
ConvertHiveFiles (
DWORD Request
)
/*++
Routine Description:
ConvertHiveFiles enumerates all the hive files on the system that need
conversion, and calls pTransferWin9xHive to migrate them to the destination
specified in wkstamig.inf.
Arguments:
Request - Specifies the progress bar-driven request.
Return Value:
If Request is REQUEST_QUERYTICKS, the return value is the number of ticks
this routine is expected to take. Otherwise, the return value is
ERROR_SUCCESS.
--*/
{
HIVEFILE_ENUM e;
switch (Request) {
case REQUEST_QUERYTICKS:
return TICKS_HIVE_CONVERSION;
case REQUEST_RUN:
//
// Enumerate all the hives that need to be processed
//
if (pEnumFirstWin9xHiveFile (&e)) {
do {
pTransferWin9xHive (e.Source, e.Dest);
} while (pEnumNextWin9xHiveFile (&e));
}
ELSE_DEBUGMSG ((DBG_NAUSEA, "ConvertHiveFiles: Nothing to do"));
break;
default:
break;
}
return ERROR_SUCCESS;
}
BOOL
pRegisterAtmFont (
IN PCTSTR PfmFile,
IN PCTSTR PfbFile,
IN PCTSTR MmmFile OPTIONAL
)
/*++
Routine Description:
pRegisterAtmFont calls the AtmFontExW API to register an Adobe PS font.
Arguments:
PfmFile - Specifies the path to the PFM file (the font metrics)
PfbFile - Specifies the path to the PFB file (the font bits)
MmmFile - Specifies the path to the MMM file (the new style metrics file)
Return Value:
TRUE if the font was registered, FALSE otherwise.
--*/
{
WORD StyleAndType = 0x2000;
INT Result;
if (AtmAddFontEx == NULL) {
return FALSE;
}
Result = AtmAddFontEx (
NULL,
&StyleAndType,
PfmFile,
PfbFile,
MmmFile
);
DEBUGMSG_IF ((
Result != ERROR_SUCCESS,
Result == - 1 ? DBG_WARNING : DBG_ERROR,
"Font not added, result = %i.\n"
" PFM: %s\n"
" PFB: %s\n"
" MMM: %s\n",
Result,
PfmFile,
PfbFile,
MmmFile
));
return Result == ERROR_SUCCESS;
}
PCTSTR
pGetAtmMultiSz (
POOLHANDLE Pool,
PCTSTR InfName,
PCTSTR SectionName,
PCTSTR KeyName
)
/*++
Routine Description:
pGetAtmMultiSz returns a multi-sz of the ATM font file names in the order
of PFM, PFB and MMM. Profile APIs are used because the key names have
commas, and they are unquoted.
Arguments:
Pool - Specifies the pool where the multi-sz will allocate buffer
space from.
InfName - Specifies the full path to the INF, to be used with the
profile APIs.
SectionName - Specifies the section name in InfName that is being processed.
KeyName - Specifies the key name to process
Return Value:
A pointer to a multi-sz allocated in the specified pool.
--*/
{
PTSTR MultiSz;
PTSTR d;
TCHAR FileBuf[MAX_TCHAR_PATH * 2];
UINT Bytes;
GetPrivateProfileString (
SectionName,
KeyName,
TEXT(""),
FileBuf,
sizeof (FileBuf) / sizeof (FileBuf[0]),
InfName
);
//
// Turn all commas into nuls
//
d = FileBuf;
while (*d) {
if (_tcsnextc (d) == TEXT(',')) {
*d = 0;
}
d = _tcsinc (d);
}
//
// Terminate the multi-sz
//
d++;
*d = 0;
d++;
//
// Transfer to a pool-based allocation and return it
//
Bytes = (UINT) ((PBYTE) d - (PBYTE) FileBuf);
MultiSz = (PTSTR) PoolMemGetAlignedMemory (Pool, Bytes);
CopyMemory (MultiSz, FileBuf, Bytes);
return MultiSz;
}
BOOL
pEnumAtmFontWorker (
IN OUT PATM_FONT_ENUM EnumPtr
)
/*++
Routine Description:
pEnumAtmFontWorker implements the logic of parsing ATM.INI to get the Adobe
font names. This routine completes an enumeration started by
pEnumFirstAtmFont or pEnumNextAtmFont.
Arguments:
EnumPtr - Specifies a partially completed enumeration structure, receives a
fully completed structure.
Return Value:
TRUE if an ATM font was enumerated, FALSE otherwise.
--*/
{
PTSTR p;
PCTSTR MultiSz;
BOOL MetricFileExists;
//
// Get PFM, PFB and MMM files
//
MultiSz = pGetAtmMultiSz (
EnumPtr->Pool,
EnumPtr->InfName,
S_FONTS,
EnumPtr->KeyNames
);
if (!MultiSz) {
return FALSE;
}
if (*MultiSz) {
_tcssafecpy (EnumPtr->PfmFile, MultiSz, MAX_TCHAR_PATH);
MultiSz = GetEndOfString (MultiSz) + 1;
}
if (*MultiSz) {
_tcssafecpy (EnumPtr->PfbFile, MultiSz, MAX_TCHAR_PATH);
} else {
return FALSE;
}
MultiSz = pGetAtmMultiSz (
EnumPtr->Pool,
EnumPtr->InfName,
S_MMFONTS,
EnumPtr->KeyNames
);
if (MultiSz) {
_tcssafecpy (EnumPtr->MmmFile, MultiSz, MAX_TCHAR_PATH);
MultiSz = GetEndOfString (MultiSz) + 1;
if (*MultiSz) {
DEBUGMSG_IF ((
!StringIMatch (MultiSz, EnumPtr->PfbFile),
DBG_ERROR,
"ATM.INI: MMFonts and Fonts specify two different PFBs: %s and %s",
MultiSz,
EnumPtr->PfbFile
));
}
} else {
EnumPtr->MmmFile[0] = 0;
}
//
// Special case: .MMM is listed in [Fonts]
//
p = _tcsrchr (EnumPtr->PfmFile, TEXT('.'));
if (p && p < _tcschr (p, TEXT('\\'))) {
p = NULL;
}
if (p && StringIMatch (p, TEXT(".mmm"))) {
EnumPtr->PfmFile[0] = 0;
}
//
// Special case: .MMM exists but is not listed in atm.ini
//
if (!EnumPtr->MmmFile[0]) {
StringCopy (EnumPtr->MmmFile, EnumPtr->PfmFile);
p = _tcsrchr (EnumPtr->MmmFile, TEXT('.'));
if (p && p < _tcschr (p, TEXT('\\'))) {
p = NULL;
}
if (p) {
StringCopy (p, TEXT(".mmm"));
if (!DoesFileExist (EnumPtr->MmmFile)) {
EnumPtr->MmmFile[0] = 0;
}
} else {
EnumPtr->MmmFile[0] = 0;
}
}
//
// Verify all files exist
//
MetricFileExists = FALSE;
if (EnumPtr->PfmFile[0] && DoesFileExist (EnumPtr->PfmFile)) {
MetricFileExists = TRUE;
}
if (EnumPtr->MmmFile[0] && DoesFileExist (EnumPtr->MmmFile)) {
MetricFileExists = TRUE;
}
if (!DoesFileExist (EnumPtr->PfbFile) || !MetricFileExists) {
DEBUGMSG ((
DBG_VERBOSE,
"At least one file is missing: %s, %s or %s",
EnumPtr->PfmFile[0] ? EnumPtr->PfmFile : TEXT("(no PFM specified)"),
EnumPtr->MmmFile[0] ? EnumPtr->MmmFile : TEXT("(no MMM specified)"),
EnumPtr->PfbFile
));
return FALSE;
}
return TRUE;
}
VOID
pAbortAtmFontEnum (
IN OUT PATM_FONT_ENUM EnumPtr
)
/*++
Routine Description:
pAbortAtmFontEnum cleans up an enumeration structure after enumeration
completes or if enumeration needs to be aborted. This routine can safely be
called multiple times on the same structure.
Arguments:
EnumPtr - Specifies an initialized and possibly used enumeration structure.
Return Value:
None.
--*/
{
if (EnumPtr->Pool) {
PoolMemDestroyPool (EnumPtr->Pool);
}
ZeroMemory (EnumPtr, sizeof (ATM_FONT_ENUM));
}
BOOL
pEnumNextAtmFont (
IN OUT PATM_FONT_ENUM EnumPtr
)
/*++
Routine Description:
pEnumNextAtmFont continues enumeration, returning either another set of ATM
font paths, or FALSE.
Arguments:
EnumPtr - Specifies the enumeration structure started by pEnumNextAtmFont.
Return Value:
TRUE if another set of font paths is available, FALSE otherwise.
--*/
{
if (!EnumPtr->KeyNames || !(*EnumPtr->KeyNames)) {
pAbortAtmFontEnum (EnumPtr);
return FALSE;
}
//
// Continue enumeration, looping until a font path set was found,
// or there are no more atm.ini lines to enumerate.
//
do {
EnumPtr->KeyNames = GetEndOfString (EnumPtr->KeyNames) + 1;
if (!(*EnumPtr->KeyNames)) {
pAbortAtmFontEnum (EnumPtr);
return FALSE;
}
} while (!pEnumAtmFontWorker (EnumPtr));
return TRUE;
}
BOOL
pEnumFirstAtmFont (
OUT PATM_FONT_ENUM EnumPtr
)
/*++
Routine Description:
pEnumFirstAtmFont begins the enumeration of font path sets in ATM.INI.
Arguments:
EnumPtr - Receivies the first set of font paths found (if any).
Return Value:
TRUE if a font path set was found, FALSE otherwise.
--*/
{
TCHAR AtmIni[MAX_TCHAR_PATH];
PTSTR FilePart;
PTSTR KeyNames;
UINT Bytes;
//
// Init structure
//
ZeroMemory (EnumPtr, sizeof (ATM_FONT_ENUM));
//
// Find full path to atm.ini (usually in %windir%)
//
if (!SearchPath (NULL, TEXT("atm.ini"), NULL, MAX_TCHAR_PATH, AtmIni, &FilePart)) {
DEBUGMSG ((DBG_VERBOSE, "ATM.INI not found in search path"));
return FALSE;
}
StringCopy (EnumPtr->InfName, AtmIni);
//
// Establish processing pool and get all key names in [Fonts]
//
EnumPtr->Pool = PoolMemInitNamedPool ("ATM Font Enum");
MYASSERT (EnumPtr->Pool);
KeyNames = MemAlloc (g_hHeap, 0, MAX_KEY_NAME_LIST);
GetPrivateProfileString (
S_FONTS,
NULL,
TEXT(""),
KeyNames,
MAX_KEY_NAME_LIST,
AtmIni
);
Bytes = SizeOfMultiSz (KeyNames);
EnumPtr->KeyNames = (PTSTR) PoolMemGetAlignedMemory (EnumPtr->Pool, Bytes);
CopyMemory (EnumPtr->KeyNames, KeyNames, Bytes);
MemFree (g_hHeap, 0, KeyNames);
//
// Begin enumeration
//
if (!(*EnumPtr->KeyNames)) {
pAbortAtmFontEnum (EnumPtr);
return FALSE;
}
if (pEnumAtmFontWorker (EnumPtr)) {
return TRUE;
}
return pEnumNextAtmFont (EnumPtr);
}
DWORD
MigrateAtmFonts (
DWORD Request
)
/*++
Routine Description:
MigrateAtmFonts is called by the progress bar to query ticks or to migrate
ATM fonts.
Arguments:
Request - Specifies the reason the progress bar is calling the routine.
Return Value:
If Request is REQUEST_QUERYTICKS, then the return value is the number of
estimated ticks needed to complete processing. Otherwise the return value
is ERROR_SUCCESS.
--*/
{
ATM_FONT_ENUM e;
static HANDLE AtmLib;
TCHAR AtmIniPath[MAX_TCHAR_PATH];
if (Request == REQUEST_QUERYTICKS) {
//
// Dynamically load atmlib.dll
//
AtmAddFontEx = NULL;
AtmLib = LoadLibrary (TEXT("atmlib.dll"));
if (!AtmLib) {
DEBUGMSG ((DBG_ERROR, "Cannot load entry point from atmlib.dll!"));
} else {
(FARPROC) AtmAddFontEx = GetProcAddress (AtmLib, "ATMAddFontExW");
DEBUGMSG_IF ((!AtmAddFontEx, DBG_ERROR, "Cannot get entry point ATMAddFontExW in atmlib.dll!"));
}
return AtmAddFontEx ? TICKS_ATM_MIGRATION : 0;
} else if (Request != REQUEST_RUN) {
return ERROR_SUCCESS;
}
if (AtmAddFontEx) {
//
// Do the ATM font migration
//
if (pEnumFirstAtmFont (&e)) {
StringCopy (AtmIniPath, e.InfName);
do {
if (pRegisterAtmFont (e.PfmFile, e.PfbFile, e.MmmFile)) {
DEBUGMSG ((DBG_VERBOSE, "ATM font registered %s", e.PfbFile));
}
} while (pEnumNextAtmFont (&e));
DeclareTemporaryFile (AtmIniPath);
#ifdef DEBUG
g_NoReloadsAllowed = TRUE;
#endif
}
//
// Clean up use of atmlib.dll - we're finished
//
FreeLibrary (AtmLib);
AtmLib = NULL;
AtmAddFontEx = NULL;
}
return ERROR_SUCCESS;
}
DWORD
RunSystemExternalProcesses (
IN DWORD Request
)
{
LONG Count;
if (Request == REQUEST_QUERYTICKS) {
//
// Count the number of entries and multiply by a constant
//
Count = SetupGetLineCount (g_WkstaMigInf, S_EXTERNAL_PROCESSES);
#ifdef PROGRESS_BAR
DEBUGLOGTIME (("RunSystemExternalProcesses: ExternalProcesses=%ld", Count));
#endif
if (Count < 1) {
return 0;
}
return Count * TICKS_SYSTEM_EXTERN_PROCESSES;
}
if (Request != REQUEST_RUN) {
return ERROR_SUCCESS;
}
//
// Loop through the processes and run each of them
//
RunExternalProcesses (g_WkstaMigInf, NULL);
return ERROR_SUCCESS;
}
BOOL
pEnumFirstWin9xBriefcase (
OUT PBRIEFCASE_ENUM e
)
{
if (!MemDbGetValueEx (&e->mde, MEMDB_CATEGORY_BRIEFCASES, NULL, NULL)) {
return FALSE;
}
e->BrfcaseDb = e->mde.szName;
return TRUE;
}
BOOL
pEnumNextWin9xBriefcase (
IN OUT PBRIEFCASE_ENUM e
)
{
if (!MemDbEnumNextValue (&e->mde)) {
return FALSE;
}
e->BrfcaseDb = e->mde.szName;
return TRUE;
}
BOOL
pMigrateBriefcase (
IN PCTSTR BriefcaseDatabase,
IN PCTSTR BriefcaseDir
)
{
HBRFCASE hbrfcase;
PTSTR NtPath;
BOOL Save, Success;
TWINRESULT tr;
BRFPATH_ENUM e;
BOOL Result = TRUE;
__try {
g_BrfcasePool = PoolMemInitNamedPool ("Briefcase");
if (!g_BrfcasePool) {
return FALSE;
}
tr = OpenBriefcase (BriefcaseDatabase, OB_FL_OPEN_DATABASE, NULL, &hbrfcase);
if (tr == TR_SUCCESS) {
if (EnumFirstBrfcasePath (hbrfcase, &e)) {
Save = FALSE;
Success = TRUE;
do {
if (StringIMatch (BriefcaseDir, e.PathString)) {
//
// ignore this path
//
continue;
}
NtPath = GetPathStringOnNt (e.PathString);
MYASSERT (NtPath);
if (!StringIMatch (NtPath, e.PathString)) {
//
// try to replace Win9x path with NT path
//
if (!ReplaceBrfcasePath (&e, NtPath)) {
Success = FALSE;
break;
}
Save = TRUE;
}
FreePathString (NtPath);
} while (EnumNextBrfcasePath (&e));
if (!Success || Save && SaveBriefcase (hbrfcase) != TR_SUCCESS) {
Result = FALSE;
}
}
CloseBriefcase(hbrfcase);
}
}
__finally {
PoolMemDestroyPool (g_BrfcasePool);
g_BrfcasePool = NULL;
}
return Result;
}
DWORD
MigrateBriefcases (
DWORD Request
)
/*++
Routine Description:
MigrateBriefcases is called by the progress bar to query ticks or to migrate
briefcases.
Arguments:
Request - Specifies the reason the progress bar is calling the routine.
Return Value:
If Request is REQUEST_QUERYTICKS, then the return value is the number of
estimated ticks needed to complete processing. Otherwise the return value
is ERROR_SUCCESS.
--*/
{
BRIEFCASE_ENUM e;
TCHAR BrfcaseDir[MAX_PATH + 2];
PTSTR p;
PTSTR BrfcaseDbOnNt;
switch (Request) {
case REQUEST_QUERYTICKS:
return TICKS_MIGRATE_BRIEFCASES;
case REQUEST_RUN:
//
// Enumerate all the briefcases that need to be processed
//
if (pEnumFirstWin9xBriefcase (&e)) {
do {
BrfcaseDbOnNt = GetPathStringOnNt (e.BrfcaseDb);
MYASSERT (BrfcaseDbOnNt);
//
// get directory name first
//
if (CharCount (BrfcaseDbOnNt) <= MAX_PATH) {
StringCopy (BrfcaseDir, BrfcaseDbOnNt);
p = _tcsrchr (BrfcaseDir, TEXT('\\'));
if (p) {
*p = 0;
if (!pMigrateBriefcase (BrfcaseDbOnNt, BrfcaseDir)) {
LOG ((
LOG_WARNING,
(PCSTR)MSG_ERROR_MIGRATING_BRIEFCASE,
BrfcaseDir
));
}
}
}
FreePathString (BrfcaseDbOnNt);
} while (pEnumNextWin9xBriefcase (&e));
}
ELSE_DEBUGMSG ((DBG_NAUSEA, "MigrateBriefcases: Nothing to do"));
break;
}
return ERROR_SUCCESS;
}
DWORD
RunSystemUninstallUserProfileCleanupPreparation (
IN DWORD Request
)
{
LONG Count;
if (Request == REQUEST_QUERYTICKS) {
//
// Count the number of entries and multiply by a constant
//
Count = SetupGetLineCount (g_WkstaMigInf, S_UNINSTALL_PROFILE_CLEAN_OUT);
#ifdef PROGRESS_BAR
DEBUGLOGTIME (("RunSystemUninstallUserProfileCleanupPreparation: FileNumber=%ld", Count));
#endif
if (Count < 1) {
return 1;
}
return Count * TICKS_SYSTEM_UNINSTALL_CLEANUP;
}
if (Request != REQUEST_RUN) {
return ERROR_SUCCESS;
}
//
// Loop through the files and mark them to be deleted during uninstall
//
UninstallUserProfileCleanupPreparation (g_WkstaMigInf, NULL, TRUE);
return ERROR_SUCCESS;
}
DWORD
AddOptionsDiskCleaner (
DWORD Request
)
{
HKEY key = NULL;
HKEY subKey = NULL;
PCTSTR optionsPath;
LONG rc;
PCTSTR descText = NULL;
DWORD d;
if (Request == REQUEST_QUERYTICKS) {
return 1;
}
if (Request != REQUEST_RUN) {
return ERROR_SUCCESS;
}
optionsPath = JoinPaths (g_WinDir, TEXT("OPTIONS"));
__try {
if (!DoesFileExist (optionsPath)) {
__leave;
}
key = OpenRegKeyStr (TEXT("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VolumeCaches"));
if (!key) {
DEBUGMSG ((DBG_ERROR, "Can't open VolumeCaches"));
__leave;
}
subKey = CreateRegKey (key, TEXT("Options Folder"));
if (!subKey) {
DEBUGMSG ((DBG_ERROR, "Can't create Options Folder"));
__leave;
}
rc = RegSetValueEx (
subKey,
TEXT(""),
0,
REG_SZ,
(PBYTE) S_CLEANER_GUID,
sizeof (S_CLEANER_GUID)
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Can't write default value to Options Folder key"));
}
descText = GetStringResource (MSG_OPTIONS_CLEANER);
rc = RegSetValueEx (
subKey,
TEXT("Description"),
0,
REG_SZ,
(PBYTE) descText,
SizeOfString (descText)
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Can't write Description value to Options Folder key"));
}
FreeStringResource (descText);
descText = GetStringResource (MSG_OPTIONS_CLEANER_TITLE);
rc = RegSetValueEx (
subKey,
TEXT("Display"),
0,
REG_SZ,
(PBYTE) descText,
SizeOfString (descText)
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Can't write Display value to Options Folder key"));
}
rc = RegSetValueEx (
subKey,
TEXT("FileList"),
0,
REG_SZ,
(PBYTE) S_CLEANER_ALL_FILES,
sizeof (S_CLEANER_ALL_FILES)
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Can't write FileList value to Options Folder key"));
}
rc = RegSetValueEx (
subKey,
TEXT("Folder"),
0,
REG_SZ,
(PBYTE) optionsPath,
SizeOfString (optionsPath)
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Can't write Folder value to Options Folder key"));
}
d = 0x17F; // see shell\applets\cleaner\dataclen\common.h DDEVCF_* flags
rc = RegSetValueEx (
subKey,
TEXT("Flags"),
0,
REG_DWORD,
(PBYTE) &d,
sizeof (d)
);
if (rc != ERROR_SUCCESS) {
DEBUGMSG ((DBG_ERROR, "Can't write flags to Options Folder key"));
}
}
__finally {
FreePathString (optionsPath);
if (key) {
CloseRegKey (key);
}
if (subKey) {
CloseRegKey (subKey);
}
FreeStringResource (descText);
}
return ERROR_SUCCESS;
}