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

3590 lines
84 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
migdll9x.c
Abstract:
Implements migration DLL interface for the Win9x side of the upgrade.
Author:
Jim Schmidt (jimschm) 13-Jan-1998
Revision History:
jimschm 23-Sep-1998 Updated for new IPC mechanism
--*/
#include "pch.h"
#include "plugin.h"
#include "migdllp.h"
#include "dbattrib.h"
#include <ntverp.h>
// This file has mixed mbcs and tchar code; this was because
// some code was ported from the original MikeCo implementation,
// and it is now clear that this file will always be an ANSI compile.
#ifdef UNICODE
#error UNICODE cannot be defined
#endif
#define DBG_MIGDLLS "MigDLLs"
//
// Globals
//
PVOID g_DllTable;
PVOID g_DllFileTable;
POOLHANDLE g_MigDllPool;
PSTR g_MessageBuf;
PMIGRATION_DLL_PROPS g_HeadDll;
CHAR g_MigDllAnswerFile[MAX_MBCHAR_PATH];
CHAR g_MigrateInfTemplate[MAX_MBCHAR_PATH];
GROWBUFFER g_SourceDirList = GROWBUF_INIT;
UINT g_MigDllsAlive;
HANDLE g_AbortDllEvent;
BOOL g_ProgressBarExists;
WINVERIFYTRUST WinVerifyTrustApi;
HANDLE g_WinTrustDll;
UINT g_TotalDllsToProcess;
BOOL g_MediaDllsQueried;
HASHTABLE g_ExcludedMigDlls = NULL;
GROWLIST g_ExcludedMigDllsByInfo = GROWLIST_INIT;
#define MAX_MESSAGE 8192
#define S_HARDWARE_IN_WACKS "\\Hardware\\"
#define S_HARDWARE_CHARS 10
typedef struct {
UINT MsgId;
PCSTR Variable;
PCSTR LocalizedName;
} MSG_VARIABLE, *PMSG_VARIABLE;
MSG_VARIABLE g_MsgVarTable[] = {
{ 0, "%OriginalOsName%", g_Win95Name },
{ MSG_SHORT_OS_NAME, "%ShortTargetOsName%", NULL },
{ MSG_NORMAL_OS_NAME, "%TargetOsName%", NULL },
{ MSG_FULL_OS_NAME, "%CompleteOsName%", NULL },
{ 0, NULL, NULL }
};
#define MESSAGE_VARIABLES ((sizeof (g_MsgVarTable) / sizeof (g_MsgVarTable[0])) - 1)
PMAPSTRUCT g_MsgVariableMap;
//
// Implementation
//
BOOL
WINAPI
MigDll9x_Entry (
IN HINSTANCE DllInstance,
IN DWORD Reason,
IN PVOID Reserved
)
/*++
Routine Description:
This is a DllMain-like init funciton, called at process attach and detach.
Arguments:
DllInstance - (OS-supplied) instance handle for the DLL
Reason - (OS-supplied) indicates attach or detatch from process or
thread
Reserved - unused
Return Value:
TRUE if initialization succeeded, or FALSE if it failed.
--*/
{
TCHAR PathBuf[16384];
TCHAR CurDir[MAX_TCHAR_PATH];
PTSTR p;
if (g_ToolMode) {
return TRUE;
}
switch (Reason) {
case DLL_PROCESS_ATTACH:
if(!pSetupInitializeUtils()) {
return FALSE;
}
g_DllTable = pSetupStringTableInitializeEx (sizeof (PMIGRATION_DLL_PROPS), 0);
if (!g_DllTable) {
return FALSE;
}
g_DllFileTable = pSetupStringTableInitializeEx (sizeof (PMIGRATION_DLL_PROPS), 0);
if (!g_DllFileTable) {
return FALSE;
}
g_MigDllPool = PoolMemInitNamedPool ("Migration DLLs - 95 side");
if (!g_MigDllPool) {
return FALSE;
}
g_MessageBuf = PoolMemGetMemory (g_MigDllPool, MAX_MESSAGE);
if (!g_MessageBuf) {
return FALSE;
}
g_HeadDll = NULL;
g_WinTrustDll = LoadLibrary ("wintrust.dll");
if (g_WinTrustDll) {
(FARPROC) WinVerifyTrustApi = GetProcAddress (g_WinTrustDll, "WinVerifyTrust");
}
GetModuleFileName (g_hInst, CurDir, MAX_TCHAR_PATH);
p = _tcsrchr (CurDir, TEXT('\\'));
MYASSERT (p);
if (p) {
MYASSERT (StringIMatch (p + 1, TEXT("w95upg.dll")));
*p = 0;
}
if (!GetEnvironmentVariable (
TEXT("Path"),
PathBuf,
sizeof (PathBuf) / sizeof (PathBuf[0])
)) {
StackStringCopy (PathBuf, CurDir);
} else {
p = (PTSTR) ((PBYTE) PathBuf + sizeof (PathBuf) - MAX_TCHAR_PATH);
*p = 0;
p = _tcsrchr (PathBuf, TEXT(';'));
if (!p || p[1]) {
StringCat (PathBuf, TEXT(";"));
}
StringCat (PathBuf, CurDir);
}
SetEnvironmentVariable (TEXT("Path"), PathBuf);
break;
case DLL_PROCESS_DETACH:
if (g_DllTable) {
pSetupStringTableDestroy (g_DllTable);
g_DllTable = NULL;
}
if (g_DllFileTable) {
pSetupStringTableDestroy (g_DllFileTable);
g_DllFileTable = NULL;
}
if (g_MigDllPool) {
PoolMemDestroyPool (g_MigDllPool);
g_MigDllPool = NULL;
}
if (g_WinTrustDll) {
FreeLibrary (g_WinTrustDll);
g_WinTrustDll = NULL;
}
DestroyStringMapping (g_MsgVariableMap);
pSetupUninitializeUtils();
break;
}
return TRUE;
}
BOOL
pTextToInt (
IN PCTSTR Text,
OUT PINT Number
)
{
return _stscanf (Text, TEXT("%i"), Number) == 1;
}
BOOL
BeginMigrationDllProcessing (
VOID
)
/*++
Routine Description:
BeginMigrationDllProcessing initializes the global variables needed to
implement the migration DLL spec. It is called during deferred init.
Arguments:
none
Return Value:
TRUE if init succeeded, or FALSE if an error occurred.
--*/
{
HANDLE h;
CHAR Buffer[4096];
UINT i;
PGROWBUFFER MsgAllocTable;
INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
PTSTR productId, versionStr;
UINT version;
#ifdef PRERELEASE
if (g_ConfigOptions.DiffMode) {
TakeSnapShot();
}
#endif
if (InfFindFirstLine (g_Win95UpgInf, S_EXCLUDEDMIGRATIONDLLS, NULL, &is)) {
g_ExcludedMigDlls = HtAllocWithData (sizeof (UINT));
if (!g_ExcludedMigDlls) {
return FALSE;
}
do {
productId = InfGetStringField (&is, 1);
versionStr = InfGetStringField (&is, 2);
if (!productId || !*productId ||
!versionStr || !(version = _ttol (versionStr))
) {
DEBUGMSG ((DBG_ERROR, "Error in win95upg.inf section %s", S_EXCLUDEDMIGRATIONDLLS));
continue;
}
HtAddStringAndData (g_ExcludedMigDlls, productId, &version);
} while (InfFindNextLine (&is));
InfCleanUpInfStruct (&is);
}
if (InfFindFirstLine (g_Win95UpgInf, S_EXCLUDEDMIGDLLSBYATTR, NULL, &is)) {
do {
PCTSTR Attributes;
PMIGDB_ATTRIB migDbAttrib = NULL;
Attributes = InfGetMultiSzField(&is, 1);
if (!Attributes) {
DEBUGMSG ((DBG_ERROR, "Error in win95upg.inf section %s line %u", S_EXCLUDEDMIGDLLSBYATTR, is.Context.Line));
continue;
}
migDbAttrib = LoadAttribData(Attributes, g_MigDllPool);
if(!migDbAttrib){
MYASSERT(FALSE);
continue;
}
GrowListAppend (&g_ExcludedMigDllsByInfo, (PBYTE)&migDbAttrib, sizeof(PMIGDB_ATTRIB));
} while (InfFindNextLine (&is));
InfCleanUpInfStruct (&is);
}
//
// Fill in all the resource strings
//
g_MsgVariableMap = CreateStringMapping();
MsgAllocTable = CreateAllocTable();
MYASSERT (MsgAllocTable);
for (i = 0 ; g_MsgVarTable[i].Variable ; i++) {
if (g_MsgVarTable[i].MsgId) {
MYASSERT (!g_MsgVarTable[i].LocalizedName);
g_MsgVarTable[i].LocalizedName = GetStringResourceEx (
MsgAllocTable,
g_MsgVarTable[i].MsgId
);
MYASSERT (g_MsgVarTable[i].LocalizedName);
if (g_MsgVarTable[i].LocalizedName) {
AddStringMappingPair (
g_MsgVariableMap,
g_MsgVarTable[i].Variable,
g_MsgVarTable[i].LocalizedName
);
}
} else {
AddStringMappingPair (
g_MsgVariableMap,
g_MsgVarTable[i].Variable,
g_MsgVarTable[i].LocalizedName
);
}
}
DestroyAllocTable (MsgAllocTable);
//
// Global init
//
g_MigDllsAlive = 0;
//
// Build source dirs
//
for (i = 0 ; i < SOURCEDIRECTORYCOUNT(); i++) {
MultiSzAppend (&g_SourceDirList, SOURCEDIRECTORY(i));
}
//
// Generate a private copy of the answer file
//
wsprintf (g_MigDllAnswerFile, "%s\\unattend.tmp", g_TempDir);
if (g_UnattendScriptFile && *g_UnattendScriptFile && **g_UnattendScriptFile) {
if (!CopyFile (*g_UnattendScriptFile, g_MigDllAnswerFile, FALSE)) {
LOG ((LOG_ERROR, "Can't copy %s to %s", *g_UnattendScriptFile, g_MigDllAnswerFile));
return FALSE;
}
} else {
h = CreateFile (
g_MigDllAnswerFile,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (h == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR, "Unable to create %s", g_MigDllAnswerFile));
return FALSE;
}
WriteFileString (h, "[Version]\r\nSignature = $Windows NT$\r\n\r\n");
CloseHandle (h);
}
//
// Generate stub of migrate.inf
//
wsprintf (g_MigrateInfTemplate, "%s\\migrate.tmp", g_TempDir);
h = CreateFile (
g_MigrateInfTemplate,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (h == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR, "Unable to create %s", g_MigrateInfTemplate));
return FALSE;
}
//
// Generate header of migrate.inf
//
MYASSERT (g_ProductFlavor);
wsprintf (
Buffer,
"[Version]\r\n"
"Signature = $Windows NT$\r\n"
"SetupOS = %s\r\n"
"SetupPlatform = %s\r\n"
"SetupSKU = %s\r\n"
"SetupBuild = %u\r\n"
,
VER_PRODUCTNAME_STR,
S_WORKSTATIONA,
*g_ProductFlavor == PERSONAL_PRODUCTTYPE ? S_PERSONALA : S_PROFESSIONALA,
VER_PRODUCTBUILD
);
WriteFileString (h, Buffer);
CloseHandle (h);
return TRUE;
}
BOOL
pEndMigrationDllProcessing (
VOID
)
/*++
Routine Description:
EndMigrationDllProcessing cleans up all the resources used to process
migration DLLs. It is called before the incompatibility report is
displayed, and after all DLLs have been processed.
Arguments:
none
Return Value:
TRUE if processing completed, or FALSE if an error occurred.
--*/
{
MIGDLL_ENUM e;
CHAR FullPath[MAX_MBCHAR_PATH];
UINT Sequencer = 0;
CHAR SeqStr[16];
BOOL b = FALSE;
PCTSTR group;
INT i;
PMIGDB_ATTRIB * ppMigDBattrib;
__try {
g_ProgressBarExists = TRUE;
if (g_ExcludedMigDlls) {
HtFree (g_ExcludedMigDlls);
g_ExcludedMigDlls = NULL;
}
for(i = GrowListGetSize(&g_ExcludedMigDllsByInfo) - 1; i >= 0; i--){
ppMigDBattrib = (PMIGDB_ATTRIB *)GrowListGetItem(&g_ExcludedMigDllsByInfo, i);
MYASSERT(ppMigDBattrib);
FreeAttribData(g_MigDllPool, *ppMigDBattrib);
}
FreeGrowList (&g_ExcludedMigDllsByInfo);
//
// Write list of DLLs to memdb
//
if (EnumFirstMigrationDll (&e)) {
do {
if (e.AllDllProps->WantsToRunOnNt) {
wsprintf (FullPath, "%s\\migrate.dll", e.AllDllProps->WorkingDir);
wsprintf (SeqStr, "%u", Sequencer);
Sequencer++;
MemDbSetValueEx (
MEMDB_CATEGORY_MIGRATION_DLL,
SeqStr,
MEMDB_FIELD_DLL,
FullPath,
0,
NULL
);
MemDbSetValueEx (
MEMDB_CATEGORY_MIGRATION_DLL,
SeqStr,
MEMDB_FIELD_WD,
e.AllDllProps->WorkingDir,
0,
NULL
);
MemDbSetValueEx (
MEMDB_CATEGORY_MIGRATION_DLL,
SeqStr,
MEMDB_FIELD_DESC,
e.AllDllProps->ProductId,
0,
NULL
);
MemDbSetValueEx (
MEMDB_CATEGORY_MIGRATION_DLL,
SeqStr,
MEMDB_FIELD_COMPANY_NAME,
e.AllDllProps->VendorInfo->CompanyName,
0,
NULL
);
if (*e.AllDllProps->VendorInfo->SupportNumber) {
MemDbSetValueEx (
MEMDB_CATEGORY_MIGRATION_DLL,
SeqStr,
MEMDB_FIELD_SUPPORT_PHONE,
e.AllDllProps->VendorInfo->SupportNumber,
0,
NULL
);
}
if (*e.AllDllProps->VendorInfo->SupportUrl) {
MemDbSetValueEx (
MEMDB_CATEGORY_MIGRATION_DLL,
SeqStr,
MEMDB_FIELD_SUPPORT_URL,
e.AllDllProps->VendorInfo->SupportUrl,
0,
NULL
);
}
if (*e.AllDllProps->VendorInfo->InstructionsToUser) {
MemDbSetValueEx (
MEMDB_CATEGORY_MIGRATION_DLL,
SeqStr,
MEMDB_FIELD_SUPPORT_INSTRUCTIONS,
e.AllDllProps->VendorInfo->InstructionsToUser,
0,
NULL
);
}
if (g_ConfigOptions.ShowPacks) {
//
// Add a message in the incompatibility report for the pack.
//
group = BuildMessageGroup (
MSG_INSTALL_NOTES_ROOT,
MSG_RUNNING_MIGRATION_DLLS_SUBGROUP,
e.AllDllProps->ProductId
);
if (group) {
MsgMgr_ObjectMsg_Add (
e.AllDllProps->ProductId,
group,
S_EMPTY
);
FreeText (group);
}
}
}
} while (EnumNextMigrationDll (&e));
}
if (!MergeMigrationDllInf (g_MigDllAnswerFile)) {
__leave;
}
b = TRUE;
}
__finally {
DeleteFile (g_MigDllAnswerFile);
DeleteFile (g_MigrateInfTemplate);
FreeGrowBuffer (&g_SourceDirList);
g_ProgressBarExists = FALSE;
}
#ifdef PRERELEASE
if (g_ConfigOptions.DiffMode) {
CHAR szMigdllDifPath[] = "c:\\migdll.dif";
if (ISPC98()) {
szMigdllDifPath[0] = (CHAR)g_SystemDir[0];
}
GenerateDiffOutputA (szMigdllDifPath, NULL, FALSE);
}
#endif
return b;
}
DWORD
EndMigrationDllProcessing (
IN DWORD Request
)
{
switch (Request) {
case REQUEST_QUERYTICKS:
return TICKS_END_MIGRATION_DLL_PROCESSING;
case REQUEST_RUN:
if (!pEndMigrationDllProcessing ()) {
return GetLastError ();
}
else {
return ERROR_SUCCESS;
}
default:
DEBUGMSG ((DBG_ERROR, "Bad parameter in EndMigrationDllProcessing"));
}
return 0;
}
BOOL
pIsPathLegal (
PCTSTR Path
)
{
UINT Chars;
CHARTYPE ch;
static UINT TempDirLen = 0;
// loop optimization, relies on fact that we never change g_TempDir
if (!TempDirLen) {
TempDirLen = CharCount (g_TempDir);
}
//
// Determine if path is a containment case of the setup temp dir
//
Chars = min (CharCount (Path), TempDirLen);
if (StringIMatchCharCount (Path, g_TempDir, Chars)) {
ch = _tcsnextc (CharCountToPointer (Path, Chars));
if (!ch || ch == TEXT('\\')) {
return FALSE;
}
}
return TRUE;
}
UINT
ScanPathForMigrationDlls (
IN PCSTR PathSpec,
IN HANDLE CancelEvent, OPTIONAL
OUT PBOOL MatchFound OPTIONAL
)
/*++
Routine Description:
ScanPathForMigrationDlls searches the specified path, including all
subdirectories, for migrate.dll. If found, the entry points are
verified, and if all exist, QueryVersion is called. When Queryversion
succeeds, the DLL is added to the list of DLLs and is moved to
local storage.
Arguments:
PathSpec - Specifies the directory to search. Must be a complete
path.
CancelEvent - Specifies the handle of an event that causes migration DLL
searching to be canceled.
MatchFound - Receives TRUE if a migrate.dll was found and QueryVersion
was called, or FALSE if no migrate.dll was found. This is
used to distinguish between loaded DLLs and unneeded DLLs.
Return Value:
The number of migrate.dll modules successfully loaded.
--*/
{
TREE_ENUMA e;
UINT DllsFound = 0;
DWORD rc = ERROR_SUCCESS;
g_AbortDllEvent = CancelEvent;
if (MatchFound) {
*MatchFound = FALSE;
}
if (EnumFirstFileInTree (&e, PathSpec, "migrate.dll", FALSE)) {
do {
//
// Check for user cancel
//
if (CancelEvent) {
if (WaitForSingleObject (CancelEvent, 0) == WAIT_OBJECT_0) {
rc = ERROR_CANCELLED;
break;
}
}
if (CANCELLED()) {
rc = ERROR_CANCELLED;
break;
}
if (e.Directory) {
continue;
}
//
// Don't allow scan of our temp dir!
//
if (!pIsPathLegal (e.FullPath)) {
continue;
}
//
// Found DLL -- see if it's real, then move it to local
// storage.
//
DEBUGMSG ((DBG_MIGDLLS, "Found DLL: %hs", e.FullPath));
if (pValidateAndMoveDll (e.FullPath, MatchFound)) {
DllsFound++;
if (g_ProgressBarExists) {
TickProgressBar ();
}
} else {
rc = GetLastError();
if (rc != ERROR_SUCCESS) {
break;
}
}
} while (EnumNextFileInTree (&e));
if (rc != ERROR_SUCCESS) {
AbortEnumFileInTree (&e);
}
}
g_AbortDllEvent = NULL;
if (g_ProgressBarExists) {
TickProgressBar ();
}
SetLastError (rc);
return DllsFound;
}
BOOL
pProcessAllLocalDlls (
VOID
)
/*++
Routine Description:
ProcessAllLocalDlls processes all the DLLs that were moved to local
storage. It enumerates each DLL, then calls ProcessDll. This
function also allows the user to cancel Setup in the middle of
processing.
Arguments:
none
Return Value:
Returns TRUE if all DLLs were processed, or FALSE if an error occurred.
If FALSE is returned, call GetLastError to determine the reason for
failure.
--*/
{
MIGDLL_ENUM e;
UINT DllsProcessed = 0;
g_ProgressBarExists = TRUE;
if (EnumFirstMigrationDll (&e)) {
do {
if (!ProgressBar_SetSubComponent (e.ProductId)) {
SetLastError (ERROR_CANCELLED);
return FALSE;
}
if (!ProcessDll (&e)) {
e.AllDllProps->WantsToRunOnNt = FALSE;
if (GetLastError() != ERROR_SUCCESS) {
return FALSE;
}
}
TickProgressBarDelta (TICKS_MIGDLL_DELTA);
DllsProcessed++;
} while (EnumNextMigrationDll (&e));
}
// Adjust for difference of DLLs on media that were not processed
TickProgressBarDelta ((g_TotalDllsToProcess - DllsProcessed) * TICKS_MIGDLL_DELTA);
g_ProgressBarExists = FALSE;
return TRUE;
}
DWORD
ProcessAllLocalDlls (
IN DWORD Request
)
{
switch (Request) {
case REQUEST_QUERYTICKS:
g_TotalDllsToProcess = GetTotalMigrationDllCount();
return (g_TotalDllsToProcess * TICKS_MIGDLL_DELTA);
case REQUEST_RUN:
if (!pProcessAllLocalDlls ()) {
return GetLastError ();
}
else {
return ERROR_SUCCESS;
}
default:
DEBUGMSG ((DBG_ERROR, "Bad parameter in ProcessAllLocalDlls"));
}
return 0;
}
BOOL
pEnumPreLoadedDllWorker (
IN OUT PPRELOADED_DLL_ENUM e
)
{
PCTSTR Data;
BOOL b = FALSE;
PTSTR p;
//
// Suppressed?
//
MemDbBuildKey (
e->Node,
MEMDB_CATEGORY_DISABLED_MIGDLLS,
NULL, // no item
NULL, // no field
e->eValue.ValueName
);
if (!MemDbGetValue (e->Node, NULL)) {
//
// Not suppressed. Contains legal path?
//
Data = GetRegValueString (e->Key, e->eValue.ValueName);
if (Data) {
_tcssafecpy (e->Path, Data, MAX_TCHAR_PATH);
p = _tcsrchr (e->Path, TEXT('\\'));
if (p && StringIMatch (_tcsinc (p), TEXT("migrate.dll"))) {
*p = 0;
}
MemFree (g_hHeap, 0, Data);
b = pIsPathLegal (e->Path);
}
}
return b;
}
BOOL
EnumFirstPreLoadedDll (
OUT PPRELOADED_DLL_ENUM e
)
{
ZeroMemory (e, sizeof (PRELOADED_DLL_ENUM));
e->Key = OpenRegKeyStr (S_PREINSTALLED_MIGRATION_DLLS);
if (!e->Key) {
return FALSE;
}
if (!EnumFirstRegValue (&e->eValue, e->Key)) {
AbortPreLoadedDllEnum (e);
return FALSE;
}
//
// Find first reg value that has a legal path
//
while (!pEnumPreLoadedDllWorker (e)) {
if (!EnumNextRegValue (&e->eValue)) {
AbortPreLoadedDllEnum (e);
return FALSE;
}
}
return TRUE;
}
BOOL
EnumNextPreLoadedDll (
IN OUT PPRELOADED_DLL_ENUM e
)
{
do {
if (!EnumNextRegValue (&e->eValue)) {
AbortPreLoadedDllEnum (e);
return FALSE;
}
} while (!pEnumPreLoadedDllWorker (e));
return TRUE;
}
VOID
AbortPreLoadedDllEnum (
IN OUT PPRELOADED_DLL_ENUM e
)
{
if (e->Key) {
CloseRegKey (e->Key);
}
ZeroMemory (e, sizeof (PRELOADED_DLL_ENUM));
}
BOOL
pProcessDllsOnCd (
VOID
)
/*++
Routine Description:
ProcessDllsOnCd scans all source directories for migration DLLs.
Each one found is moved to local storage.
Arguments:
none
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
UINT u;
CHAR Path[MAX_MBCHAR_PATH];
PCSTR p;
BOOL b = FALSE;
PRELOADED_DLL_ENUM e;
INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
PTSTR Subdir = NULL;
#ifdef PRERELEASE
if (g_ConfigOptions.DiffMode) {
return TRUE;
}
#endif
//
// Build path for each source dir, scan the dir for migration DLLs,
// and watch for any failures.
//
g_ProgressBarExists = TRUE;
__try {
//
// Process cmdline DLLs the very first
//
p = g_ConfigOptions.MigrationDlls;
if (p) {
while (*p) {
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
__leave;
}
ScanPathForMigrationDlls (p, NULL, NULL);
if (GetLastError() != ERROR_SUCCESS) {
__leave;
}
p = GetEndOfString (p) + 1;
}
}
//
// Process pre-loaded DLLs first to give them a chance to register stuff
// before "standard" migdlls run
//
if (EnumFirstPreLoadedDll (&e)) {
do {
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
__leave;
}
ScanPathForMigrationDlls (e.Path, NULL, NULL);
if (GetLastError() != ERROR_SUCCESS) {
__leave;
}
} while (EnumNextPreLoadedDll (&e));
}
for (u = 0 ; u < SOURCEDIRECTORYCOUNT() ; u++) {
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
__leave;
}
//
// We look for migration dlls in all of the directories listed in
// win95upg.inf [MigrationDllPaths].
//
if (InfFindFirstLine (g_Win95UpgInf, S_CD_MIGRATION_DLLS, NULL, &is)) {
do {
Subdir = InfGetStringField (&is, 0);
if (!Subdir) {
continue;
}
wsprintf (Path, "%s\\%s", SOURCEDIRECTORY(u), Subdir);
if (GetFileAttributes (Path) == INVALID_ATTRIBUTES) {
//
// Try the non-cd layout.
//
wsprintf (Path, "%s\\WINNT32\\%s", SOURCEDIRECTORY(u), Subdir);
if (GetFileAttributes (Path) == INVALID_ATTRIBUTES) {
continue;
}
}
SetLastError (ERROR_SUCCESS);
ScanPathForMigrationDlls (Path, NULL, NULL);
} while (InfFindNextLine (&is));
}
if (GetLastError() != ERROR_SUCCESS && GetLastError() != ERROR_LINE_NOT_FOUND) {
__leave;
}
}
InfCleanUpInfStruct (&is);
b = TRUE;
}
__finally {
g_ProgressBarExists = FALSE;
g_MediaDllsQueried = TRUE;
}
return b;
}
DWORD
ProcessDllsOnCd (
IN DWORD Request
)
{
switch (Request) {
case REQUEST_QUERYTICKS:
#ifdef PRERELEASE
if (g_ConfigOptions.DiffMode) {
return 0;
}
#endif
return (GetMediaMigrationDllCount() * TICKS_MIGDLL_QUERYVERSION);
case REQUEST_RUN:
if (!pProcessDllsOnCd ()) {
return GetLastError ();
}
else {
return ERROR_SUCCESS;
}
default:
DEBUGMSG ((DBG_ERROR, "Bad parameter in ProcessDllsOnCd"));
}
return 0;
}
UINT
pCountMigrateDllsInPath (
IN PCSTR Path
)
/*++
Routine Description:
pCountMigrateDllsInPath scans a path for files named migrate.dll
and returns the number found.
Arguments:
Path - Specifies root of the path to search
Return Value:
The number of migrate.dll modules found in the path.
--*/
{
TREE_ENUM e;
UINT Count = 0;
if (EnumFirstFileInTree (&e, Path, "migrate.dll", FALSE)) {
do {
if (CANCELLED()) {
return 0;
}
if (!e.Directory) {
Count++;
}
} while (EnumNextFileInTree (&e));
}
return Count;
}
UINT
GetMediaMigrationDllCount (
VOID
)
/*++
Routine Description:
GetMediaMigrationDllCount scans all the source directories, registry and
unattended directories and returns the number of migrate.dll files found.
Arguments:
none
Return Value:
The number of migrate.dll modules found in the source directories and
directories supplied by the answer file.
--*/
{
UINT u;
CHAR Path[MAX_MBCHAR_PATH];
PCSTR p;
BOOL TurnItOff = FALSE;
PRELOADED_DLL_ENUM e;
static UINT MediaDlls = 0;
if (MediaDlls) {
return MediaDlls;
}
//
// Build path for each source dir, scan the dir for migration DLLs,
// and watch for any failures.
//
__try {
p = g_ConfigOptions.MigrationDlls;
if (SOURCEDIRECTORYCOUNT() > 1 || (p && *p)) {
TurnOnWaitCursor();
TurnItOff = TRUE;
}
for (u = 0 ; u < SOURCEDIRECTORYCOUNT() ; u++) {
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
__leave;
}
wsprintf (Path, "%s\\win9xmig", SOURCEDIRECTORY(u));
MediaDlls += pCountMigrateDllsInPath (Path);
}
if (p) {
while (*p) {
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
__leave;
}
MediaDlls += pCountMigrateDllsInPath (p);
p = GetEndOfString (p) + 1;
}
}
//
// Count pre-loaded DLLs
//
if (EnumFirstPreLoadedDll (&e)) {
do {
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
__leave;
}
MediaDlls += pCountMigrateDllsInPath (e.Path);
} while (EnumNextPreLoadedDll (&e));
}
}
__finally {
if (TurnItOff) {
TurnOffWaitCursor();
}
}
return MediaDlls;
}
UINT
GetMigrationDllCount (
VOID
)
/*++
Routine Description:
GetMigrationDllCount returns the number of migration DLLs in local storage.
Arguments:
none
Return Value:
The number of migrate.dll modules successfully moved to local storage.
--*/
{
return g_MigDllsAlive;
}
UINT
GetTotalMigrationDllCount (
VOID
)
/*++
Routine Description:
GetTotalMigrationDllCount returns the number of DLLs that will be
processed. This includes media-based DLLs, DLLs supplied by the wizard
page UI, DLLs specified in the registry, and DLLs specified in the answer file.
Arguments:
None.
Return Value:
The total number of DLLs to be processed.
--*/
{
UINT DllCount;
if (g_MediaDllsQueried) {
DllCount = 0;
} else {
DllCount = GetMediaMigrationDllCount();
}
DllCount += g_MigDllsAlive;
return DllCount;
}
BOOL
pVerifyDllIsTrusted (
IN PCSTR DllPath
)
/*++
Routine Description:
pVerifyDllIsTrusted determines if the specified DLL is digitally signed
and is trusted by the system.
Arguments:
none
Return Value:
The number of migrate.dll modules successfully moved to local storage.
The caller uses GetLastError when the return value is FALSE to determine
if Setup was cancelled via UI.
--*/
{
static BOOL TrustAll = FALSE;
UINT Status;
if (TrustAll) {
return TRUE;
}
if (!IsDllSigned (WinVerifyTrustApi, DllPath)) {
Status = UI_UntrustedDll (DllPath);
if (Status == IDC_TRUST_IT) {
return TRUE;
} else if (Status == IDC_TRUST_ANY) {
TrustAll = TRUE;
return TRUE;
} else if (Status == IDCANCEL) {
SetLastError (ERROR_CANCELLED);
return FALSE;
}
SetLastError (ERROR_SUCCESS);
return FALSE;
}
return TRUE;
}
BOOL
pMatchAttributes(
IN DBATTRIB_PARAMS * pdbAttribParams,
IN MIGDB_ATTRIB * pMigDbAttrib
)
{
BOOL bResult = TRUE;
while (pMigDbAttrib != NULL) {
pdbAttribParams->ExtraData = pMigDbAttrib->ExtraData;
if (!CallAttribute (pMigDbAttrib, pdbAttribParams)) {
bResult = FALSE;
break;
}
pMigDbAttrib = pMigDbAttrib->Next;
}
return bResult;
}
BOOL
pValidateAndMoveDll (
IN PCSTR DllPath,
IN OUT PBOOL MatchFound OPTIONAL
)
/*++
Routine Description:
pValidateAndMoveDll calls the DLL's QueryVersion function, and if the DLL
returns success, it is moved to local storage, along with all the files
associated with it.
Arguments:
DllPath - Specifies full path to the migrate.dll to process
MatchFound - Specifies an initialized BOOL, which may be either TRUE or
FALSE depending on whether another valid migration DLL has
been processed by the caller. Receives TRUE if the
migrate.dll is valid, otherwise the value is not changed.
Return Value:
TRUE if the DLL specified by DllPath is valid and needs to run, or FALSE
if not. GetLastError is used by the caller to detect fatal errors.
--*/
{
PCSTR ProductId = NULL;
UINT DllVersion = 0;
PCSTR ExeNamesBuf = NULL;
CHAR WorkingDir[MAX_MBCHAR_PATH];
PVENDORINFO VendorInfo = NULL;
UINT SizeNeeded;
CHAR QueryVersionDir[MAX_MBCHAR_PATH];
PSTR p;
BOOL b;
LONG rc;
PMIGRATION_DLL_PROPS ExistingDll;
UINT version;
UINT i;
UINT listSize;
//
// Verify trust of DLL
//
if (!pVerifyDllIsTrusted (DllPath)) {
DEBUGMSG ((DBG_WARNING, "DLL %s is not trusted and will not be processed", DllPath));
//
// Implicit: SetLastError (ERROR_SUCCESS); (may be ERROR_CANCELLED if user
// cancelled via UI)
//
return FALSE;
}
//
// verify if this is one of the excluded migdlls, based on their file characteristics
//
if (GrowListGetSize (&g_ExcludedMigDllsByInfo)) {
WIN32_FIND_DATA fd;
HANDLE h;
FILE_HELPER_PARAMS params;
DBATTRIB_PARAMS dbAttribParams;
PMIGDB_ATTRIB * ppMigDBattrib;
h = FindFirstFile (DllPath, &fd);
if (h != INVALID_HANDLE_VALUE) {
CloseHandle (h);
ZeroMemory (&params, sizeof(params));
params.FindData = &fd;
params.FullFileSpec = DllPath;
ZeroMemory (&dbAttribParams, sizeof(dbAttribParams));
dbAttribParams.FileParams = &params;
for(i = 0, listSize = GrowListGetSize(&g_ExcludedMigDllsByInfo); i < listSize; i++){
ppMigDBattrib = (PMIGDB_ATTRIB *)GrowListGetItem(&g_ExcludedMigDllsByInfo, i);
MYASSERT(ppMigDBattrib);
if(pMatchAttributes(&dbAttribParams, *ppMigDBattrib)){
LOG ((
LOG_INFORMATION,
TEXT("Found upgrade pack %s, but it is excluded from processing [%s]"),
DllPath,
S_EXCLUDEDMIGDLLSBYATTR
));
SetLastError (ERROR_SUCCESS);
return FALSE;
}
}
}
}
//
// Copy base of DLL to QueryVersionDir, and trim off migrate.dll
//
StackStringCopyA (QueryVersionDir, DllPath);
p = _mbsrchr (QueryVersionDir, '\\');
MYASSERT (StringIMatch (p, "\\migrate.dll"));
if (p) {
*p = 0;
}
SizeNeeded = pCalculateSizeOfTree (QueryVersionDir);
if (!pCreateWorkingDir (WorkingDir, QueryVersionDir, SizeNeeded)) {
return FALSE;
}
//
// Call QueryVersion directly from the suppling media
//
b = OpenMigrationDll (DllPath, WorkingDir);
if (b) {
rc = CallQueryVersion (
WorkingDir,
&ProductId,
&DllVersion,
&ExeNamesBuf,
&VendorInfo
);
if (g_ProgressBarExists) {
TickProgressBarDelta (TICKS_MIGDLL_QUERYVERSION);
}
if (rc != ERROR_SUCCESS) {
b = FALSE;
if (rc == ERROR_NOT_INSTALLED) {
if (MatchFound) {
*MatchFound = TRUE;
}
rc = ERROR_SUCCESS;
} else if (rc != ERROR_SUCCESS) {
if (rc == RPC_S_CALL_FAILED) {
rc = ERROR_NOACCESS;
}
pMigrationDllFailedMsg (
NULL,
DllPath,
0,
MSG_MIGDLL_QV_FAILED_LOG,
rc
);
rc = ERROR_SUCCESS;
}
} else {
//
// QueryVersion was called and it returned success (it wants to run)
// but first check if this migration dll is intentionally excluded
//
if (g_ExcludedMigDlls &&
HtFindStringAndData (g_ExcludedMigDlls, ProductId, &version) &&
DllVersion <= version
) {
LOG ((
LOG_INFORMATION,
TEXT("Found upgrade pack %s (ProductId=%s, Version=%u), but it is excluded from processing"),
DllPath,
ProductId,
DllVersion
));
b = FALSE;
} else {
if (MatchFound) {
*MatchFound = TRUE;
}
}
}
} else {
//
// Don't pass errors on.
//
if (g_ProgressBarExists) {
TickProgressBarDelta (TICKS_MIGDLL_QUERYVERSION); // early out, account for DLL not being processed
} // (see similar case below in finally block)
rc = ERROR_SUCCESS;
}
if (!b) {
DEBUGMSG ((DBG_MIGDLLS, "%hs will not be processed", DllPath));
CloseMigrationDll();
pDestroyWorkingDir (WorkingDir);
SetLastError (rc);
return FALSE;
}
//
// We have found a DLL that wants to run. Try moving it to local storage.
//
DEBUGMSG ((DBG_MIGDLLS, "Moving DLL for %s to local storage: %s", ProductId, DllPath));
__try {
b = FALSE;
//
// Look for existing version of DLL
//
ExistingDll = pFindMigrationDll (ProductId);
if (ExistingDll && ExistingDll->Version >= DllVersion) {
DEBUGMSG_IF ((
ExistingDll->Version > DllVersion,
DBG_MIGDLLS,
"%hs will not be processed because it is an older version",
DllPath
));
SetLastError (ERROR_SUCCESS);
__leave;
}
if (ExistingDll) {
RemoveDllFromList (ExistingDll->Id);
}
//
// Add the DLL to the list of loaded DLLs, and move all of the files
//
if (!pAddDllToList (
QueryVersionDir,
WorkingDir,
ProductId,
DllVersion,
ExeNamesBuf,
VendorInfo
)) {
pDestroyWorkingDir (WorkingDir);
__leave;
}
//
// DLL is now on a local drive and has returned success from QueryVersion.
//
b = TRUE;
}
__finally {
DEBUGMSG ((DBG_MIGDLLS, "Done with %s", ProductId));
CloseMigrationDll();
}
return b;
}
BOOL
pCreateWorkingDir (
OUT PSTR WorkingDir,
IN PCSTR QueryVersionDir,
IN UINT SizeNeeded
)
/*++
Routine Description:
pCreateWorkingDir generates a working directory name and creates it.
The directory will have enough space to hold the size requested.
Arguments:
WorkingDir - Receives the full path to the working directory
QueryVersionDir - Specifies the version where the migration DLL is
when QueryVersion is called
SizeNeeded - Specifies the number of bytes, rounded up to the next
cluster size, indicating the total space to be occupied
by migration DLL files
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
static UINT Sequencer = 1;
//
// For now, just put the files in %windir%\setup\temp
//
wsprintf (WorkingDir, "%s\\dll%05u", g_PlugInTempDir, Sequencer);
Sequencer++;
//
// Establish the directory
//
if (CreateEmptyDirectory (WorkingDir) != ERROR_SUCCESS) {
LOG ((LOG_ERROR, "pCreateWorkingDir: Can't create %hs", WorkingDir));
return FALSE;
}
return TRUE;
}
VOID
pDestroyWorkingDir (
IN PCSTR WorkingDir
)
/*++
Routine Description:
pDestroyWorkingDir cleans up the specified working directory
Arguments:
WorkingDir - Specifies the name of the directory to clean up
Return Value:
none
--*/
{
BOOL b;
BOOL TurnItOff = FALSE;
UINT Files = 0;
TREE_ENUM e;
//
// Count the number of things that will be deleted, and if there
// are more than 20, turn on the wait cursor. (This keeps the
// UI responsive.)
//
if (EnumFirstFileInTree (&e, WorkingDir, NULL, FALSE)) {
do {
Files++;
if (Files > 30) {
AbortEnumFileInTree (&e);
TurnOnWaitCursor();
TurnItOff = TRUE;
break;
}
} while (EnumNextFileInTree (&e));
}
b = DeleteDirectoryContents (WorkingDir);
b &= RemoveDirectory (WorkingDir);
if (!b) {
LOG ((LOG_ERROR, "Unable to delete %hs", WorkingDir));
}
if (TurnItOff) {
TurnOffWaitCursor();
}
}
PMIGRATION_DLL_PROPS
pFindMigrationDll (
IN PCSTR ProductId
)
/*++
Routine Description:
pFindMigrationDll searches the private data structures for the specified
ProductId. The ProductId is in a string table, so lookup is fast.
Arguments:
ProductId - Specifies the ID of the DLL to find
Return Value:
A pointer to the DLL's property struct, or NULL if the product ID does
not match any of the DLLs.
--*/
{
PMIGRATION_DLL_PROPS Props;
LONG rc;
rc = pSetupStringTableLookUpStringEx (
g_DllTable,
(PTSTR) ProductId,
STRTAB_CASE_INSENSITIVE,
&Props,
sizeof (Props)
);
if (rc == -1) {
return NULL;
}
return Props;
}
PMIGRATION_DLL_PROPS
pFindMigrationDllById (
IN LONG Id
)
/*++
Routine Description:
pFindMigrationDllById returns the migration DLL property structure for
a DLL ID. The ID is the same ID returned by the DLL enumeration routines.
Arguments:
Id - Specifies the ID to find properties for
Return Value:
A pointer to the DLL's property struct, or NULL if the ID is not valid.
--*/
{
PMIGRATION_DLL_PROPS Props;
if (Id == -1) {
return NULL;
}
if (!pSetupStringTableGetExtraData (
g_DllTable,
Id,
&Props,
sizeof (Props)
)) {
return NULL;
}
return Props;
}
UINT
pGetMaxClusterSize (
VOID
)
/*++
Routine Description:
pGetMaxClusterSize determines the maximum cluster size of all disks
that are candidates for working directories.
Arguments:
none
Return Value:
The number of bytes per cluster.
--*/
{
ACCESSIBLE_DRIVE_ENUM e;
static DWORD MaxSize = 0;
if (MaxSize) {
return MaxSize;
}
if (GetFirstAccessibleDriveEx (&e, TRUE)) {
do {
MaxSize = max (MaxSize, e->ClusterSize);
} while (GetNextAccessibleDrive (&e));
}
MYASSERT (MaxSize);
return MaxSize;
}
UINT
pCalculateSizeOfTree (
IN PCSTR PathSpec
)
/*++
Routine Description:
pCalculateSizeOfTree enumerates the specified path and calculates
the number of physical bytes occupied by the files and directory
structures.
Arguments:
PathSpec - Specifies root of path to find
Return Value:
The number of bytes physically occupied by the path
--*/
{
TREE_ENUM e;
UINT Size = 0;
UINT ClusterSize;
ClusterSize = pGetMaxClusterSize();
if (EnumFirstFileInTree (&e, PathSpec, NULL, FALSE)) {
do {
//
// We assume the migrate.dll will never pack more than 4G of
// files.
//
if (!e.Directory) {
MYASSERT (Size + e.FindData->nFileSizeLow >= Size);
MYASSERT (!e.FindData->nFileSizeHigh);
Size += e.FindData->nFileSizeLow + ClusterSize -
(e.FindData->nFileSizeLow % ClusterSize);
} else {
// Add a fudge factor here, we don't know the exact size
e.Directory += ClusterSize;
}
} while (EnumNextFileInTree (&e));
}
return Size;
}
BOOL
pEnumMigrationDllWorker (
IN OUT PMIGDLL_ENUM EnumPtr,
IN PMIGRATION_DLL_PROPS Props
)
/*++
Routine Description:
pEnumMigrationDllWorker is a common routine that completes the
enumeration of a DLL. It fills in the EnumPtr data members
and returns TRUE. Also, it screens out invalid DLL structures
(those that have been disabled by RemoveDllFromList).
Arguments:
EnumPtr - Specifies the partially completed enumeration structure,
receives the complete information.
Props - Specifies the properties of the item that was enumerated.
Return Value:
FALSE if all remaining of the properties are invalid, or TRUE otherwise
--*/
{
while (Props && Props->Id == -1) {
Props = Props->Next;
}
if (!Props) {
return FALSE;
}
EnumPtr->ProductId = Props->ProductId;
EnumPtr->VendorInfo = Props->VendorInfo;
EnumPtr->CurrentDir = Props->WorkingDir;
EnumPtr->AllDllProps = Props;
EnumPtr->Id = Props->Id;
return TRUE;
}
BOOL
EnumFirstMigrationDll (
OUT PMIGDLL_ENUM EnumPtr
)
/*++
Routine Description:
EnumFirstMigrationDll begins an enumeration of migration DLLs.
Callers can then use the enumerated information to fill list
boxes or any other processing.
Arguments:
EnumPtr - Receives the first enumerated DLL's properties
Return Value:
TRUE if a DLL was enumerated, or FALSE if not.
--*/
{
ZeroMemory (EnumPtr, sizeof (MIGDLL_ENUM));
return pEnumMigrationDllWorker (EnumPtr, g_HeadDll);
}
BOOL
EnumNextMigrationDll (
IN OUT PMIGDLL_ENUM EnumPtr
)
/*++
Routine Description:
EnumNextMigrationDll continues enumeration started by EnumFirstMigrationDll.
Arguments:
EnumPtr - Specifies the last enumerated item, receives the next enumerated
item.
Return Value:
TRUE if another DLL was enumerated, or FALSE if not.
--*/
{
if (EnumPtr->AllDllProps->Next) {
return pEnumMigrationDllWorker (EnumPtr, EnumPtr->AllDllProps->Next);
}
return FALSE;
}
BOOL
pAddDllToList (
IN PCSTR MediaDir,
IN PCSTR WorkingDir,
IN PCSTR ProductId,
IN UINT Version,
IN PCSTR ExeNamesBuf, OPTIONAL
IN PVENDORINFO VendorInfo
)
/*++
Routine Description:
pAddDllToList adds the supplied properties to the private data structures
used to organize the migration DLLs. The ProductId is placed in a string
table, the ExeNamesBuf is put in a list of files, and the rest of the
members are duplciated into a memory pool.
Arguments:
MediaDir - Specifies the directory containing the migration DLL
WorkingDir - Specifies the working directory assigned to the DLL on local
storage
ProductId - Specifies the display name of the migration DLL
Version - Specifies the DLL's version number
ExeNamesBuf - Specifies a multi-sz listing file names that need to
be located
VendorInfo - Specifies the vendor info provided by the migration DLL
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
PMIGRATION_DLL_PROPS Props;
CHAR MigrateInfPath[MAX_MBCHAR_PATH];
PCSTR p;
HANDLE File;
//
// Copy the DLL into the working directory
//
if (!CopyTree (
MediaDir,
WorkingDir,
0,
COPYTREE_DOCOPY | COPYTREE_NOOVERWRITE,
ENUM_ALL_LEVELS,
FILTER_ALL,
NULL,
NULL,
NULL
)) {
LOG ((LOG_ERROR, "Error while copying files for migration.dll."));
return FALSE;
}
//
// Generate a new DLL struct
//
Props = (PMIGRATION_DLL_PROPS) PoolMemGetMemory (g_MigDllPool, sizeof (MIGRATION_DLL_PROPS));
//
// Link props to list of all DLLs
//
Props->Next = g_HeadDll;
g_HeadDll = Props;
//
// Add product ID to string table for quick lookup
//
Props->Id = pSetupStringTableAddStringEx (
g_DllTable,
(PTSTR) ProductId,
STRTAB_CASE_INSENSITIVE|STRTAB_NEW_EXTRADATA,
&Props,
sizeof (Props)
);
if (Props->Id == -1) {
LOG ((LOG_ERROR, "Error adding migration.dll to list."));
return FALSE;
}
//
// Fill in the rest of the DLL properties
//
Props->ProductId = PoolMemDuplicateString (g_MigDllPool, ProductId);
Props->VendorInfo = (PVENDORINFO) PoolMemDuplicateMemory (g_MigDllPool, (PBYTE) VendorInfo, sizeof (VENDORINFO));
Props->WorkingDir = PoolMemDuplicateString (g_MigDllPool, WorkingDir);
Props->Version = Version;
Props->OriginalDir = PoolMemDuplicateString (g_MigDllPool, MediaDir);
wsprintf (MigrateInfPath, "%s\\migrate.inf", Props->WorkingDir);
Props->MigrateInfPath = PoolMemDuplicateString (g_MigDllPool, MigrateInfPath);
Props->WantsToRunOnNt = FALSE; // MigrateUser9x or MigrateSystem9x must return success for this to be TRUE
Props->MigInfAppend = INVALID_HANDLE_VALUE;
//
// Dump vendor info to log
//
LOG ((
LOG_INFORMATION,
"Upgrade Pack: %s\n"
"Version: %u\n"
"Company Name: %s\n"
"Support Number: %s\n"
"Support URL: %s\n"
"Instructions: %s\n",
Props->ProductId,
Props->Version,
VendorInfo->CompanyName,
VendorInfo->SupportNumber,
VendorInfo->SupportUrl,
VendorInfo->InstructionsToUser
));
//
// Add all search files to string table
//
p = ExeNamesBuf;
if (p) {
while (*p) {
pAddFileToSearchTable (p, Props);
p = GetEndOfStringA (p) + 1;
}
}
//
// Copy migrate.inf to DLL dir
//
SetFileAttributes (Props->MigrateInfPath, FILE_ATTRIBUTE_NORMAL);
CopyFile (g_MigrateInfTemplate, Props->MigrateInfPath, FALSE);
File = CreateFile (Props->MigrateInfPath, GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (File != INVALID_HANDLE_VALUE) {
SetFilePointer (File, 0, NULL, FILE_END);
WriteFileString (File, TEXT("\r\n; "));
WriteFileString (File, Props->ProductId);
WriteFileString (File, TEXT("\r\n"));
CloseHandle (File);
} else {
LOG ((LOG_ERROR, "Cannot open %s", Props->MigrateInfPath));
}
g_MigDllsAlive++;
return TRUE;
}
VOID
RemoveDllFromList (
IN LONG ItemId
)
/*++
Routine Description:
RemoveDllFromList disables the data structures for the specified DLL and
removes it from local storage.
Arguments:
ItemId - Specifies the ID of the migration DLL to remove
Return Value:
none
--*/
{
PMIGRATION_DLL_PROPS Prev, This;
PMIGRATION_DLL_PROPS Props;
Props = pFindMigrationDllById (ItemId);
if (!Props) {
DEBUGMSG ((DBG_WHOOPS, "Cannot remove migration DLL id %i", ItemId));
return;
}
//
// Delete the linkage
//
Prev = NULL;
This = g_HeadDll;
while (This && This != Props) {
Prev = This;
This = This->Next;
}
if (Prev) {
Prev->Next = Props->Next;
} else {
g_HeadDll = Props->Next;
}
//
// Delete the string table entry by making the item data NULL
//
This = NULL;
pSetupStringTableSetExtraData (
g_DllTable,
ItemId,
&This,
sizeof (This)
);
//
// Set Id to -1 so any search files are ignored
//
Props->Id = -1;
pDestroyWorkingDir (Props->WorkingDir);
g_MigDllsAlive--;
}
BOOL
pAddFileToSearchTable (
IN PCSTR File,
IN PMIGRATION_DLL_PROPS Props
)
/*++
Routine Description:
pAddFileToSearchTable adds the specified file name to the global lookup
table used to quickly find the DLL (or DLLs) that want the location
of the file.
Arguments:
File - Specifies the long file name of the file to find
Props - Specifies the properties of the DLL that wants the location of
File
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
PFILETOFIND IndexedFile;
PFILETOFIND NewFile;
LONG rc;
LONG Offset;
//
// Allocate a new file struct
//
NewFile = (PFILETOFIND) PoolMemGetMemory (g_MigDllPool, sizeof (FILETOFIND));
if (!NewFile) {
return FALSE;
}
NewFile->Next = NULL;
NewFile->Dll = Props;
//
// Does a struct already exist in string table?
//
Offset = pSetupStringTableLookUpStringEx (
g_DllFileTable,
(PTSTR) File,
STRTAB_CASE_INSENSITIVE,
&IndexedFile,
sizeof (IndexedFile)
);
if (Offset == -1) {
//
// No, add it now
//
rc = pSetupStringTableAddStringEx (
g_DllFileTable,
(PTSTR) File,
STRTAB_CASE_INSENSITIVE,
&NewFile,
sizeof (NewFile)
);
} else {
//
// Yes, put it at the head of the list
//
rc = pSetupStringTableSetExtraData (
g_DllFileTable,
Offset,
&NewFile,
sizeof (NewFile)
);
IndexedFile->Next = NewFile;
}
return rc != -1;
}
BOOL
pWriteStringToEndOfInf (
IN OUT PMIGRATION_DLL_PROPS Dll,
IN PCSTR String,
IN PCSTR HeaderString, OPTIONAL
IN BOOL WriteLineFeed
)
/*++
Routine Description:
pWriteStringToEndOfInf writes the specified string to migrate.inf.
This routine also opens migrate.inf if it has not already been
opened.
If HeaderString is provided and migrate.inf needs to be opened,
the header string is written to the file, ahead of String.
Arguments:
Dll - Specifies the DLL associated with the migrate.inf
String - Specifies the string to write
HeaderString - Specifies text that is written when migrate.inf is
opened for writing for the first time.
WriteLineFeed - Specifies TRUE if a \r\n sequence should be written
after String, or FALSE if not.
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
if (Dll->MigInfAppend == INVALID_HANDLE_VALUE) {
//
// Flush the profile APIs
//
WritePrivateProfileString(
NULL,
NULL,
NULL,
Dll->MigrateInfPath
);
//
// Open the file for writing
//
Dll->MigInfAppend = CreateFile (
Dll->MigrateInfPath,
GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
} else {
HeaderString = NULL;
}
if (Dll->MigInfAppend == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR, "Cannot open %s", Dll->MigrateInfPath));
return FALSE;
}
SetFilePointer (Dll->MigInfAppend, 0, NULL, FILE_END);
if (HeaderString) {
if (!WriteFileString (Dll->MigInfAppend, HeaderString)) {
return FALSE;
}
}
if (!WriteFileString (Dll->MigInfAppend, String)) {
return FALSE;
}
if (WriteLineFeed) {
if (!WriteFileString (Dll->MigInfAppend, "\r\n")) {
return FALSE;
}
}
return TRUE;
}
BOOL
UpdateFileSearch (
IN PCSTR FullFileSpec,
IN PCSTR FileOnly
)
/*++
Routine Description:
UpdateFileSearch is called for every file on the machine being upgraded,
and any file that the DLL wants the location file will receive its path
in the DLL's migrate.inf.
Arguments:
FullFileSpec - specifies the full path to the file, in long name format
FileOnly - Specifies only the file name and must match the file in
FullFileSpec.
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
PFILETOFIND FileWanted;
LONG rc;
//
// Look in string table for an indexed file, and if found, enumerate
// all the DLLs that want to know where the file is.
//
rc = pSetupStringTableLookUpStringEx (
g_DllFileTable,
(PTSTR) FileOnly,
STRTAB_CASE_INSENSITIVE,
&FileWanted,
sizeof (FileWanted)
);
if (rc == -1) {
return TRUE;
}
while (FileWanted) {
if (FileWanted->Dll->Id != -1) {
//
// Append path to the end of the file
//
if (!pWriteStringToEndOfInf (
FileWanted->Dll,
FullFileSpec,
"\r\n[Migration Paths]\r\n",
TRUE
)) {
return FALSE;
}
}
FileWanted = FileWanted->Next;
}
return TRUE;
}
VOID
pMigrationDllFailedMsg (
IN PMIGRATION_DLL_PROPS Dll, OPTIONAL
IN PCSTR Path, OPTIONAL
IN UINT PopupId, OPTIONAL
IN UINT LogId, OPTIONAL
IN LONG rc
)
/*++
Routine Description:
pMigrationDllFailedMsg presents a popup for DLLs that fail to run,
and also logs the failure to setuperr.log.
The system's last error is preserved. Also, no output is generated
if the user has chosen to cancel or if rc is ERROR_SUCCESS.
Arguments:
Dll - Specifies the DLL that failed. If Dll is not provided, PopupId
must be zero.
Path - Specifies the path to the DLL media
PopupId - Specifies the message ID for the popup dialog box
LogId - Specifies the message ID for the log
rc - Specifies the failure code
Return Value:
None.
--*/
{
CHAR ErrorCode[16];
PCTSTR FixupPhone;
PCTSTR FixupUrl;
PCTSTR FixupInstructions;
PCTSTR LineBreak = S_EMPTY;
PCTSTR ArgArray[1];
PushError();
if (!CANCELLED() && rc != ERROR_SUCCESS && rc != ERROR_CANCELLED) {
wsprintf (ErrorCode, "%u", rc);
if (Dll) {
//
// Generate fixup strings
//
if (Dll->VendorInfo->SupportNumber[0]) {
ArgArray[0] = Dll->VendorInfo->SupportNumber;
FixupPhone = ParseMessageID (MSG_MIGDLL_SUPPORT_PHONE_FIXUP, ArgArray);
LineBreak = TEXT("\n");
} else {
FixupPhone = S_EMPTY;
}
if (Dll->VendorInfo->SupportUrl[0]) {
ArgArray[0] = Dll->VendorInfo->SupportUrl;
FixupUrl = ParseMessageID (MSG_MIGDLL_SUPPORT_URL_FIXUP, ArgArray);
LineBreak = TEXT("\n");
} else {
FixupUrl = S_EMPTY;
}
if (Dll->VendorInfo->InstructionsToUser[0]) {
ArgArray[0] = Dll->VendorInfo->InstructionsToUser;
FixupInstructions = ParseMessageID (MSG_MIGDLL_INSTRUCTIONS_FIXUP, ArgArray);
LineBreak = TEXT("\n");
} else {
FixupInstructions = S_EMPTY;
}
LOG ((
LOG_ERROR,
(PCSTR) LogId,
Dll->WorkingDir,
Dll->ProductId,
ErrorCode,
Dll->LastFnName,
Dll->VendorInfo->CompanyName,
FixupPhone,
FixupUrl,
FixupInstructions,
LineBreak
));
LOG ((
LOG_ERROR,
(PCSTR) PopupId,
Dll->WorkingDir,
Dll->ProductId,
ErrorCode,
Dll->LastFnName,
Dll->VendorInfo->CompanyName,
FixupPhone,
FixupUrl,
FixupInstructions,
LineBreak
));
} else {
MYASSERT (!PopupId);
LOG ((
LOG_ERROR,
(PCSTR) LogId,
Path ? Path : S_EMPTY,
S_EMPTY,
ErrorCode,
TEXT("QueryVersion")
));
}
}
PopError();
}
PCTSTR
pQuoteMe (
IN PCTSTR String
)
{
static TCHAR QuotedString[1024];
QuotedString[0] = TEXT('\"');
_tcssafecpy (&QuotedString[1], String, 1022);
StringCat (QuotedString, TEXT("\""));
return QuotedString;
}
BOOL
ProcessDll (
IN PMIGDLL_ENUM EnumPtr
)
/*++
Routine Description:
ProcessDll calls the Initialize9x, MigrateUser9x and MigrateSystem9x
entry points of the DLL. The DLL's migrate.inf is then processed.
ProcessDll must NOT be called more than once for the same DLL.
Arguments:
EnumPtr - Specifies the DLL to process, as enumerated by
EnumFirstMigrationDll/EnumNextMigrationDll.
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
If an error occurred, GetLastError will contain the failure. If the
error is ERROR_SUCCESS, the DLL should be abandoned. If the error
is something else, Setup should terminate.
--*/
{
CHAR DllPath[MAX_MBCHAR_PATH];
PMIGRATION_DLL_PROPS Dll;
MEMDB_ENUM e;
PSTR End;
LONG rc;
BOOL result;
Dll = EnumPtr->AllDllProps;
//
// Write the path exclusions now
//
if (!pWriteStringToEndOfInf (Dll, "\r\n[Excluded Paths]", NULL, TRUE)) {
return FALSE;
}
if (MemDbGetValueEx (
&e,
MEMDB_CATEGORY_FILEENUM,
g_ExclusionValueString,
MEMDB_FIELD_FE_PATHS
)) {
do {
End = GetEndOfStringA (e.szName);
MYASSERT (End);
End = _mbsdec2 (e.szName, End);
if (End && *End == '*') {
*End = 0;
}
if (!pWriteStringToEndOfInf (Dll, pQuoteMe (e.szName), NULL, TRUE)) {
return FALSE;
}
} while (MemDbEnumNextValue(&e));
}
if (MemDbGetValueEx (
&e,
MEMDB_CATEGORY_FILEENUM,
g_ExclusionValueString,
MEMDB_FIELD_FE_FILES
)) {
do {
if (!pWriteStringToEndOfInf (Dll, pQuoteMe (e.szName), NULL, TRUE)) {
return FALSE;
}
} while (MemDbEnumNextValue (&e));
}
//
// Make sure the migrate.inf file is closed now
//
if (Dll->MigInfAppend != INVALID_HANDLE_VALUE) {
CloseHandle (Dll->MigInfAppend);
Dll->MigInfAppend = INVALID_HANDLE_VALUE;
}
//
// Open the migrate.dll
//
wsprintf (DllPath, "%s\\migrate.dll", Dll->WorkingDir);
if (!OpenMigrationDll (DllPath, Dll->WorkingDir)) {
LOG ((LOG_ERROR, "Can't open %s", DllPath));
return FALSE;
}
result = FALSE;
__try {
//
// Call the entry points
//
if (!pProcessInitialize9x (Dll) ||
!pProcessUser9x (Dll) ||
!pProcessSystem9x (Dll) ||
!pProcessMigrateInf (Dll)
) {
rc = GetLastError();
if (rc == RPC_S_CALL_FAILED) {
rc = ERROR_NOACCESS;
}
pMigrationDllFailedMsg (Dll, NULL, MSG_MIGDLL_FAILED_POPUP, MSG_MIGDLL_FAILED_LOG, rc);
SetLastError (ERROR_SUCCESS);
__leave;
}
TickProgressBar ();
result = TRUE;
}
__finally {
PushError();
//
// Close the DLL
//
CloseMigrationDll();
PopError();
}
return result;
}
BOOL
pProcessInitialize9x (
IN PMIGRATION_DLL_PROPS Dll
)
/*++
Routine Description:
pProcessInitialize9x calls the Initialize9x entry point of the
specified DLL.
Arguments:
Dll - Specifies the properties of the DLL to call
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
LONG rc;
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
return FALSE;
}
Dll->LastFnName = "Initialize9x";
//
// Call the entry points
//
rc = CallInitialize9x (
Dll->WorkingDir,
(PCSTR) g_SourceDirList.Buf,
(PVOID) Dll->OriginalDir,
SizeOfString (Dll->OriginalDir)
);
//
// If DLL returns ERROR_NOT_INSTALLED, do not call it any further
// If DLL returns something other than ERROR_SUCCESS, abandon the DLL
//
if (rc == ERROR_NOT_INSTALLED) {
SetLastError (ERROR_SUCCESS);
return FALSE;
} else if (rc != ERROR_SUCCESS) {
SetLastError (rc);
DEBUGMSG ((DBG_MIGDLLS, "DLL failed with rc=%u", rc));
return FALSE;
}
return TRUE;
}
BOOL
pProcessUser9x (
IN PMIGRATION_DLL_PROPS Dll
)
/*++
Routine Description:
pProcessUser9x calls the MigrateUser9x for every user that will be migrated.
Arguments:
Dll - Specifies the properites of the DLL to call
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
USERENUM e;
LONG rc;
Dll->LastFnName = "MigrateUser9x";
//
// Enumerate all the users
//
if (EnumFirstUser (&e, ENUMUSER_ENABLE_NAME_FIX)) {
do {
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
return FALSE;
}
if (e.AccountType & INVALID_ACCOUNT) {
continue;
}
rc = CallMigrateUser9x (
g_ParentWnd,
e.FixedUserName,
g_MigDllAnswerFile,
NULL,
0
);
if (rc == ERROR_SUCCESS) {
Dll->WantsToRunOnNt = TRUE;
} else if (rc != ERROR_NOT_INSTALLED) {
EnumUserAbort (&e);
SetLastError (rc);
DEBUGMSG ((DBG_MIGDLLS, "DLL failed with rc=%u", rc));
return FALSE;
}
} while (EnumNextUser (&e));
}
return TRUE;
}
BOOL
pProcessSystem9x (
IN PMIGRATION_DLL_PROPS Dll
)
/*++
Routine Description:
pProcessSystem9x calls the MigrateSystem9x entry point.
Arguments:
Dll - Specifies the properties of the DLL to process
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
LONG rc;
if (CANCELLED()) {
SetLastError (ERROR_CANCELLED);
return FALSE;
}
Dll->LastFnName = "MigrateSystem9x";
rc = CallMigrateSystem9x (g_ParentWnd, g_MigDllAnswerFile, NULL, 0);
if (rc == ERROR_SUCCESS) {
Dll->WantsToRunOnNt = TRUE;
} else if (rc != ERROR_NOT_INSTALLED) {
SetLastError (rc);
DEBUGMSG ((DBG_MIGDLLS, "DLL failed with rc=%u", rc));
return FALSE;
}
return TRUE;
}
BOOL
pProcessMigrateInf (
IN PMIGRATION_DLL_PROPS Dll
)
/*++
Routine Description:
pProcessMigrateInf reads in all the sections of migrate.inf that a DLL might
write to and performs the actions necessary. The following sections are
supported:
[Handled] - Any file, directory or reg location will suppress
incompatibility messages associated with the handled item.
Also, any file or directory will not be touched by Setup,
and any registry key will not be copied.
[Moved] - Any file or directory marked for move will cause the rest of the
upgrade to make the correct changes, such as updating links or
replacing the old path with the new path in the registry or INI
files.
Any file marked for deletion is simply noted, and all links to the
file are also deleted.
[Incompatible Messages] - All messages are added to the report (and may be
suppressed if someone else handles the problem)
[NT Disk Space Requirements] - Any additional space needed by a migration DLL
will be added to the computations performed
by Setup
Arguments:
Dll - Specifies the properties of the DLL who owns the migrate.inf
Return Value:
TRUE if processing was successful, or FALSE if an error occurred.
--*/
{
HINF Inf;
INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
INFSTRUCT is2 = INITINFSTRUCT_POOLHANDLE;
PCSTR Object;
PCSTR ObjectType;
PCSTR Source;
PCSTR Dest;
PCSTR Size;
PCSTR Drive;
CHAR WithColonAndWack[4];
PCSTR ObjectSection;
CHAR MsgMgrContext[MAX_MBCHAR_PATH];
PSTR DisplayObjectSection;
PCSTR MigDllGroup;
BOOL HardwareSpecialCase;
PSTR p;
CHAR QuotedObjectSection[256];
PCSTR ResText;
DWORD SrcAttribs;
TREE_ENUM TreeEnum;
CHAR FixedSrc[MAX_MBCHAR_PATH];
CHAR NewDest[MAX_MBCHAR_PATH];
PCSTR OtherDevices = NULL;
PCSTR DeviceType;
PCSTR PrintDevice = NULL;
PCSTR Num;
UINT Value;
PCSTR PreDefGroup;
INT PrevChar;
//
// Open the INF
//
WritePrivateProfileString(
NULL,
NULL,
NULL,
Dll->MigrateInfPath
);
Inf = InfOpenInfFile (Dll->MigrateInfPath);
if (Inf == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR, "Cannot open %s for processing", Dll->MigrateInfPath));
return TRUE;
}
//
// Read in the [Handled] section
//
if (InfFindFirstLine (Inf, "Handled", NULL, &is)) {
do {
Object = InfGetStringField (&is, 0);
ObjectType = InfGetStringField (&is, 1);
if (Object) {
//
// Suppress all incompatibility messages associated with the object
//
DEBUGMSG ((DBG_MIGDLLS, "%s handled %s", Dll->MigrateInfPath, Object));
HandleObject (Object, ObjectType);
}
InfResetInfStruct (&is);
} while (InfFindNextLine (&is));
}
//
// Read in the [Moved] section
//
if (InfFindFirstLine (Inf, "Moved", NULL, &is)) {
do {
Source = InfGetStringField (&is, 0);
Dest = InfGetStringField (&is, 1);
if (Source) {
StackStringCopy (FixedSrc, Source);
RemoveWackAtEnd (FixedSrc);
SrcAttribs = QuietGetFileAttributes (FixedSrc);
if (SrcAttribs != INVALID_ATTRIBUTES) {
if (Source && *Source) {
if (SrcAttribs & FILE_ATTRIBUTE_DIRECTORY) {
DEBUGMSG ((DBG_MIGDLLS, "Directory %s marked as handled because %s will move it.", Source, Dll->MigrateInfPath));
HandleObject (Source, TEXT("Directory"));
}
else {
DEBUGMSG ((DBG_MIGDLLS, "File %s marked as handled because %s will move it.", Source, Dll->MigrateInfPath));
HandleObject (Source, TEXT("File"));
}
}
if (Dest && *Dest) {
if (SrcAttribs & FILE_ATTRIBUTE_DIRECTORY) {
//
// Migration DLL will move this dir
//
DEBUGMSG ((DBG_MIGDLLS, "%s moved dir %s to %s", Dll->MigrateInfPath, Source, Dest));
if (EnumFirstFileInTree (&TreeEnum, Source, NULL, TRUE)) {
StackStringCopy (NewDest, Dest);
p = AppendWack (NewDest);
do {
RemoveOperationsFromPath (TreeEnum.FullPath, ALL_CHANGE_OPERATIONS);
MYASSERT (*TreeEnum.SubPath != '\\');
StringCopy (p, TreeEnum.SubPath);
MarkFileForMoveExternal (TreeEnum.FullPath, NewDest);
} while (EnumNextFileInTree (&TreeEnum));
}
StackStringCopy (NewDest, Dest);
RemoveWackAtEnd (NewDest);
RemoveOperationsFromPath (FixedSrc, ALL_CHANGE_OPERATIONS);
MarkFileForMoveExternal (FixedSrc, NewDest);
} else {
//
// Migration DLL will move this file
//
DEBUGMSG ((DBG_MIGDLLS, "%s moved %s to %s", Dll->MigrateInfPath, Source, Dest));
RemoveOperationsFromPath (Source, ALL_CHANGE_OPERATIONS);
MarkFileForMoveExternal (Source, Dest);
}
} else {
if (SrcAttribs & FILE_ATTRIBUTE_DIRECTORY) {
DEBUGMSG ((DBG_MIGDLLS, "%s deleted dir %s", Dll->MigrateInfPath, Source));
if (EnumFirstFileInTree (&TreeEnum, Source, NULL, TRUE)) {
do {
RemoveOperationsFromPath (TreeEnum.FullPath, ALL_CHANGE_OPERATIONS);
MarkFileForExternalDelete (TreeEnum.FullPath);
} while (EnumNextFileInTree (&TreeEnum));
}
} else {
//
// Migration DLL will delete this file
//
DEBUGMSG ((DBG_MIGDLLS, "%s deleted %s", Dll->MigrateInfPath, Source));
RemoveOperationsFromPath (Source, ALL_CHANGE_OPERATIONS);
MarkFileForExternalDelete (Source);
}
}
}
ELSE_DEBUGMSG ((
DBG_WARNING,
"Ignoring non-existent soruce in [Moved]: %s",
Source
));
}
InfResetInfStruct (&is);
} while (InfFindNextLine (&is));
}
//
// Read in the [Incompatible Messages] section
//
if (InfFindFirstLine (Inf, "Incompatible Messages", NULL, &is)) {
OtherDevices = GetStringResource (MSG_UNKNOWN_DEVICE_CLASS);
PrintDevice = GetStringResource (MSG_PRINTERS_DEVICE_CLASS);
do {
//
// Add incompatible messages
//
ObjectSection = InfGetStringField (&is, 0);
if (!ObjectSection) {
DEBUGMSG ((DBG_ERROR, "Malformed migrate.inf. Some incompatibility messages may be missing."));
continue;
}
GetPrivateProfileString (
"Incompatible Messages",
ObjectSection,
"",
g_MessageBuf,
MAX_MESSAGE - 4,
Dll->MigrateInfPath
);
if (*g_MessageBuf == 0 && ByteCount (ObjectSection) < 250) {
wsprintf (QuotedObjectSection, TEXT("\"%s\""), ObjectSection);
GetPrivateProfileString (
"Incompatible Messages",
QuotedObjectSection,
"",
g_MessageBuf,
MAX_MESSAGE - 4,
Dll->MigrateInfPath
);
}
// Remove quote pairs, replace \n with actual line break character
for (p = g_MessageBuf ; *p ; p = _mbsinc (p)) {
PrevChar = _mbsnextc (p);
if (PrevChar == '\"' || PrevChar == '%') {
if (_mbsnextc (p + 1) == PrevChar) {
MoveMemory ((PSTR) p, p + 1, SizeOfStringA (p + 1));
}
} else if (_mbsnextc (p) == '\\') {
if (_mbsnextc (p + 1) == 'n') {
MoveMemory ((PSTR) p, p + 1, SizeOfStringA (p + 1));
*((PSTR) p) = '\r';
}
}
}
// Terminate anchor tag if DLL forgot it, harmless otherwise
StringCatA (g_MessageBuf, "</A>");
//
// Replace OS name variables
//
MappingSearchAndReplace (g_MsgVariableMap, g_MessageBuf, MAX_MESSAGE);
//
// Associate objects with the message
//
if (InfFindFirstLine (Inf, ObjectSection, NULL, &is2)) {
wsprintf (MsgMgrContext, "%s,%s", Dll->MigrateInfPath, ObjectSection);
//
// The ObjectSection specified by the migration DLL indicates
// what message group it goes in. There are four possibilities:
//
// 1. ObjectSection starts with a # and gives the group number,
// as in #1\Program Name. In this case we parse the number,
// and then put the message in the proper group.
//
// 2. ObjectSection starts with a well-defined, localized root
// name. In this case we use that name.
//
// 3. ObjectSection is in the form of \Hardware\<device>. In
// this case we put the device in the "Other devices"
// subgroup.
//
// 4. ObjectSection is in another format than above. In this
// case we put the object section into the migration DLL group.
//
DisplayObjectSection = NULL;
if (*ObjectSection == TEXT('#')) {
Value = 0;
Num = ObjectSection + 1;
while (*Num >= TEXT('0') && *Num <= TEXT('9')) {
Value = Value * 10 + (*Num - TEXT('0'));
Num++;
}
if (_tcsnextc (Num) == TEXT('\\')) {
Num++;
if (*Num) {
PreDefGroup = GetPreDefinedMessageGroupText (Value);
if (PreDefGroup) {
DisplayObjectSection = JoinText (PreDefGroup, Num);
DEBUGMSG ((
DBG_MIGDLLS,
"Pre-defined group created: %s -> %s",
ObjectSection,
DisplayObjectSection
));
}
}
}
}
if (!DisplayObjectSection) {
if (IsPreDefinedMessageGroup (ObjectSection)) {
DisplayObjectSection = DuplicateText (ObjectSection);
MYASSERT (DisplayObjectSection);
} else {
//
// Put message in migration DLL group
//
HardwareSpecialCase = StringIMatchCharCount (
ObjectSection,
S_HARDWARE_IN_WACKS,
S_HARDWARE_CHARS
);
if (HardwareSpecialCase) {
//
// Hack -- if this is the printer migration DLL,
// then use Printers instead of Other Devices.
//
p = (PSTR) _tcsistr (Dll->OriginalDir, TEXT("\\print"));
if (p && (*(p + ARRAYSIZE(TEXT("\\print"))) == 0)) {
DeviceType = PrintDevice;
} else {
DeviceType = OtherDevices;
}
MigDllGroup = BuildMessageGroup (
MSG_INCOMPATIBLE_HARDWARE_ROOT,
MSG_INCOMPATIBLE_HARDWARE_PNP_SUBGROUP,
DeviceType
);
ObjectSection += S_HARDWARE_CHARS;
MYASSERT (MigDllGroup);
} else {
ResText = GetStringResource (MSG_MIGDLL_ROOT);
MYASSERT (ResText);
MigDllGroup = DuplicateText (ResText);
FreeStringResource (ResText);
MYASSERT (MigDllGroup);
}
DisplayObjectSection = AllocText (SizeOfStringA (MigDllGroup) +
SizeOfStringA (ObjectSection) +
SizeOfStringA (Dll->ProductId)
);
MYASSERT (DisplayObjectSection);
if (HardwareSpecialCase) {
wsprintf (DisplayObjectSection, "%s\\%s", MigDllGroup, ObjectSection);
} else if (StringIMatch (Dll->ProductId, ObjectSection)) {
wsprintf (DisplayObjectSection, "%s\\%s", MigDllGroup, Dll->ProductId);
} else {
wsprintf (DisplayObjectSection, "%s\\%s\\%s", MigDllGroup, Dll->ProductId, ObjectSection);
}
FreeText (MigDllGroup);
}
}
MsgMgr_ContextMsg_Add (
MsgMgrContext,
DisplayObjectSection,
g_MessageBuf
);
FreeText (DisplayObjectSection);
do {
Object = InfGetStringField (&is2, 0);
if (Object) {
MsgMgr_LinkObjectWithContext (
MsgMgrContext,
Object
);
}
ELSE_DEBUGMSG ((DBG_WHOOPS, "pProcessMigrateInf: InfGetStringField failed"));
if (Object == NULL) {
LOG ((LOG_ERROR, "Failed to get string field from migration.dll migrate.inf."));
}
} while (InfFindNextLine (&is2));
InfResetInfStruct (&is2);
}
ELSE_DEBUGMSG ((DBG_ERROR, "Object section %s not found", ObjectSection));
InfResetInfStruct (&is);
} while (InfFindNextLine (&is));
if (OtherDevices) {
FreeStringResource (OtherDevices);
}
if (PrintDevice) {
FreeStringResource (PrintDevice);
}
}
//
// Read in the [NT Disk Space Requirements] section
//
if (InfFindFirstLine (Inf, "NT Disk Space Requirements", NULL, &is)) {
do {
Drive = InfGetStringField (&is, 0);
if (!Drive) {
DEBUGMSG ((DBG_ERROR, "Could not read some NT Disk Space Requirements from migrate.inf"));
continue;
}
WithColonAndWack[0] = Drive[0];
WithColonAndWack[1] = ':';
WithColonAndWack[2] = '\\';
WithColonAndWack[3] = 0;
Size = InfGetStringField (&is, 1);
UseSpace (WithColonAndWack, (LONGLONG) atoi (Size));
InfResetInfStruct (&is);
} while (InfFindNextLine (&is));
}
InfCleanUpInfStruct (&is);
InfCleanUpInfStruct (&is2);
InfCloseInfFile (Inf);
return TRUE;
}