You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
548 lines
17 KiB
548 lines
17 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation
|
|
//
|
|
// File: migrate.cpp
|
|
// xiaoyuw @ 2001/09
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <stdio.h>
|
|
#include <windows.h>
|
|
#include <setupapi.h>
|
|
#include <shlwapi.h>
|
|
|
|
// migration DLL version information
|
|
|
|
typedef struct {
|
|
CHAR CompanyName[256];
|
|
CHAR SupportNumber[256];
|
|
CHAR SupportUrl[256];
|
|
CHAR InstructionsToUser[1024];
|
|
} VENDORINFO, *PVENDORINFO;
|
|
|
|
typedef struct {
|
|
SIZE_T Size;
|
|
PCSTR StaticProductIdentifier;
|
|
UINT DllVersion;
|
|
PINT CodePageArray;
|
|
UINT SourceOs;
|
|
UINT TargetOs;
|
|
PCSTR * NeededFileList;
|
|
PVENDORINFO VendorInfo;
|
|
} MIGRATIONINFOA, *PMIGRATIONINFOA;
|
|
|
|
typedef enum {
|
|
|
|
OS_WINDOWS9X = 0,
|
|
OS_WINDOWSNT4X = 1,
|
|
OS_WINDOWS2000 = 2,
|
|
OS_WINDOWSWHISTLER = 3
|
|
|
|
} OS_TYPES, *POS_TYPES;
|
|
|
|
PMIGRATIONINFOA g_MigrationInfo;
|
|
|
|
const char g_szProductId[] = "Microsoft MSI Migration DLL v2.0";
|
|
VENDORINFO g_VendorInfo = { "Microsoft", "", "", "" };
|
|
|
|
const static CHAR pszAsmCache[] = "%windir%\\MsiAsmCache";
|
|
const static WCHAR pwszAsmCache[]= L"%windir%\\MsiAsmCache\\*.*";
|
|
|
|
typedef HRESULT (__stdcall *LPDLLGETVERSION)(DLLVERSIONINFO *);
|
|
|
|
BOOL IsThereAssembliesForMigrate()
|
|
{
|
|
TCHAR buf[MAX_PATH];
|
|
DWORD cch;
|
|
BOOL fAssemblyExist = FALSE;
|
|
|
|
cch = ExpandEnvironmentStrings(pszAsmCache, buf, MAX_PATH);
|
|
if (( cch == 0 ) || (cch > MAX_PATH))
|
|
{
|
|
#if DBG
|
|
MessageBoxA(NULL, "ExpandEnvironmentStrings return 0 or > MAX_PATH", "mig_Error", MB_OK);
|
|
#endif
|
|
goto Exit;
|
|
}
|
|
DWORD attrib = GetFileAttributes(buf);
|
|
if ((attrib == INVALID_FILE_ATTRIBUTES) || (!(attrib & FILE_ATTRIBUTE_DIRECTORY)))
|
|
{
|
|
#if DBG
|
|
MessageBoxA(NULL, "no MsiAsmCache directory ", "mig_info", MB_OK);
|
|
#endif
|
|
goto Exit;
|
|
}
|
|
|
|
fAssemblyExist = TRUE;
|
|
|
|
Exit:
|
|
return fAssemblyExist;
|
|
|
|
}
|
|
|
|
void DbgPrintMessageBox(PCSTR pszFunc)
|
|
{
|
|
#if DBG
|
|
MessageBox(NULL, pszFunc, "migrate", MB_OK);
|
|
#endif
|
|
}
|
|
|
|
void DbgPrintMessageBox(PCSTR formatStr, PWSTR para)
|
|
{
|
|
#if DBG
|
|
CHAR str[3 * MAX_PATH];
|
|
sprintf(str, formatStr, para);
|
|
MessageBox(NULL, str, "migrate", MB_OK);
|
|
#endif
|
|
}
|
|
|
|
void DbgPrintMessageBox(PCSTR formatStr, PWSTR para, DWORD dw)
|
|
{
|
|
#if DBG
|
|
CHAR str[3 * MAX_PATH];
|
|
sprintf(str, formatStr, para, dw);
|
|
MessageBox(NULL, str, "migrate", MB_OK);
|
|
#endif
|
|
}
|
|
|
|
#include "sxsapi.h"
|
|
|
|
BOOL IsDotOrDotDot(WCHAR cFileName[])
|
|
{
|
|
if ((cFileName[0] == L'.') && (cFileName[1] == L'\0'))
|
|
return TRUE;
|
|
|
|
if ((cFileName[0] == L'.') && (cFileName[1] == L'.') && (cFileName[2] == L'\0'))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const char szRunOnceMsiAsmCacheName[] = "Cleanup Msi-Installed-Fusion-Win32-Assembly Migration";
|
|
const char szRunOnceMsiAsmCacheRegKey[]= "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
|
|
const char szRunOnceValueCommandLine[]="rundll32.exe sxs.dll,SxspRunDllDeleteDirectory %S";
|
|
|
|
LONG MigrateMSIInstalledWin32Assembly()
|
|
{
|
|
LONG lResult = ERROR_SUCCESS;
|
|
HMODULE hSxs = NULL;
|
|
PSXS_INSTALL_W pfnSxsInstallW = NULL;
|
|
PSXS_BEGIN_ASSEMBLY_INSTALL pfnSxsBeginAssemblyInstall = NULL;
|
|
PSXS_END_ASSEMBLY_INSTALL pfnSxsEndAssemblyInstall = NULL;
|
|
BOOL fSuccess = FALSE;
|
|
PVOID SxsContext = NULL;
|
|
|
|
WCHAR AsmDirInAsmCache[MAX_PATH];
|
|
WCHAR Buf[MAX_PATH];
|
|
DWORD cchBuf;
|
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATAW finddata;
|
|
|
|
SXS_INSTALLW InstallData = {sizeof(InstallData)};
|
|
SXS_INSTALL_REFERENCEW InstallReference = {sizeof(InstallReference)};
|
|
|
|
cchBuf= ExpandEnvironmentStringsW(pwszAsmCache, Buf, MAX_PATH); // including trailing NULL
|
|
if ((cchBuf == 0 ) || (cchBuf > MAX_PATH))
|
|
{
|
|
DbgPrintMessageBox("::Expand windir\\MsiAsmCache\\*.* return 0 or > MAX_PATH::");
|
|
lResult = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef MSI_MIG_TEST
|
|
hSxs = ::LoadLibraryA("..\\..\\..\\..\\..\\dll\\whistler\\obj\\i386\\sxs.dll");
|
|
#else
|
|
hSxs = ::LoadLibraryA("sxs.dll");
|
|
#endif
|
|
if ((!hSxs) || (hSxs == INVALID_HANDLE_VALUE))
|
|
{
|
|
DbgPrintMessageBox("::Load Sxs.dll failed::");
|
|
lResult = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
pfnSxsInstallW = (PSXS_INSTALL_W)GetProcAddress(hSxs, SXS_INSTALL_W);
|
|
pfnSxsBeginAssemblyInstall = (PSXS_BEGIN_ASSEMBLY_INSTALL)GetProcAddress(hSxs, SXS_BEGIN_ASSEMBLY_INSTALL);
|
|
pfnSxsEndAssemblyInstall = (PSXS_END_ASSEMBLY_INSTALL)GetProcAddress(hSxs, SXS_END_ASSEMBLY_INSTALL);
|
|
|
|
if ((pfnSxsInstallW == NULL) || (pfnSxsBeginAssemblyInstall == NULL) || (pfnSxsEndAssemblyInstall == NULL))
|
|
{
|
|
DbgPrintMessageBox("::Find SxsInstall APIs failed::");
|
|
lResult = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// !!!!! below is the code copied From base\ntsetup\syssetup\copy.c
|
|
//
|
|
|
|
//
|
|
// we do not use INSTALL_RECURSIVELY because the name of directory and structures always like
|
|
// c:\WINNT\winsxs\x86_blahblah\x86_blahblah.man
|
|
//
|
|
if (!pfnSxsBeginAssemblyInstall(
|
|
SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NOT_TRANSACTIONAL
|
|
| SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_NO_VERIFY
|
|
| SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_REPLACE_EXISTING |
|
|
SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_FROM_DIRECTORY |
|
|
SXS_BEGIN_ASSEMBLY_INSTALL_FLAG_FROM_DIRECTORY_RECURSIVE, // I have to add this flag for old sxs!SxsInstallW parameter check
|
|
NULL,
|
|
NULL, // callback context
|
|
NULL, // impersonation callback
|
|
NULL, // impersonation context
|
|
&SxsContext
|
|
))
|
|
{
|
|
DbgPrintMessageBox("::pfnSxsBeginAssemblyInstall failed::");
|
|
lResult = GetLastError();
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
hFind = FindFirstFileW(Buf, &finddata);
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
DbgPrintMessageBox("::FindFirstFileW for MsiAsmCache failed::");
|
|
lResult = GetLastError();
|
|
|
|
goto Exit;
|
|
}
|
|
while (1) {
|
|
if ((wcscmp(finddata.cFileName, L"..") == 0) || (wcscmp(finddata.cFileName, L".") == 0))
|
|
goto GetNext;
|
|
|
|
if (!(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
// if it is not a directory, ignore it
|
|
goto GetNext;
|
|
}
|
|
|
|
// this must be a subdir of an asm, which is in the format of
|
|
// %windir\MsiAsmCache\x86_ms-sxstest-simple_75e377300ab7b886_1.0.10.0_en_61E9D7DC,
|
|
// otherwise, there is an error;
|
|
// the following calculation is used to decide the directory name. it must be a very long filename but
|
|
// MAX_PATH should be fine; and since Buf contains "*.*", it should cover the trailing NULL and extra back-slash.
|
|
if ((wcslen(Buf) + wcslen(finddata.cFileName)) > (sizeof(AsmDirInAsmCache) / sizeof(WCHAR)))
|
|
{
|
|
DbgPrintMessageBox("::filename %S under MsiAsmCache is too long, will be ignored::", finddata.cFileName);
|
|
goto GetNext;
|
|
}
|
|
|
|
wcscpy(AsmDirInAsmCache, Buf);
|
|
AsmDirInAsmCache[wcslen(Buf) - wcslen(L"*.*")] = L'\0'; // contain a trailing NULL
|
|
wcscat(AsmDirInAsmCache, finddata.cFileName);
|
|
|
|
//
|
|
// Set up the reference data to indicate that all of these are OS-installed
|
|
// assemblies.
|
|
//
|
|
ZeroMemory(&InstallReference, sizeof(InstallReference));
|
|
InstallReference.cbSize = sizeof(InstallReference);
|
|
InstallReference.dwFlags = 0;
|
|
#ifdef MSI_MIG_TEST
|
|
InstallReference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_SXS_INSTALL_ASSEMBLY;
|
|
#else
|
|
InstallReference.guidScheme = SXS_INSTALL_REFERENCE_SCHEME_OSINSTALL;
|
|
#endif
|
|
|
|
//
|
|
// Set up the structure to call off to the installer
|
|
//
|
|
memset(&InstallData, 0, sizeof(InstallData));
|
|
InstallData.cbSize = sizeof(InstallData);
|
|
InstallData.dwFlags =
|
|
SXS_INSTALL_FLAG_REFERENCE_VALID |
|
|
SXS_INSTALL_FLAG_REFRESH_PROMPT_VALID |
|
|
SXS_INSTALL_FLAG_INSTALL_COOKIE_VALID |
|
|
SXS_INSTALL_FLAG_CODEBASE_URL_VALID |
|
|
SXS_INSTALL_FLAG_FROM_DIRECTORY |
|
|
SXS_INSTALL_FLAG_FROM_DIRECTORY_RECURSIVE
|
|
;
|
|
|
|
InstallData.lpReference = &InstallReference;
|
|
InstallData.lpRefreshPrompt = L"migrated from downlevel, no source";
|
|
InstallData.pvInstallCookie = SxsContext;
|
|
InstallData.lpCodebaseURL = AsmDirInAsmCache;
|
|
InstallData.lpManifestPath = AsmDirInAsmCache;
|
|
|
|
fSuccess = pfnSxsInstallW(&InstallData);
|
|
if (fSuccess == FALSE)
|
|
{
|
|
DbgPrintMessageBox("::pfnSxsInstallW failed for %S, ignore the error and just continue::", AsmDirInAsmCache);
|
|
}else
|
|
{
|
|
DbgPrintMessageBox("::pfnSxsInstallW succeed for %S, Great!!!::", AsmDirInAsmCache);
|
|
}
|
|
GetNext:
|
|
if ( FALSE == FindNextFileW(hFind, &finddata))
|
|
{
|
|
if ( GetLastError() == ERROR_NO_MORE_FILES)
|
|
DbgPrintMessageBox("::FindNextFileW ends well ::");
|
|
else
|
|
DbgPrintMessageBox("::FindNextFileW failed::");
|
|
|
|
break;
|
|
}else
|
|
{
|
|
DbgPrintMessageBox("::FindNextFileW find next file::");
|
|
}
|
|
|
|
}
|
|
|
|
lResult = ERROR_SUCCESS;
|
|
|
|
{
|
|
//
|
|
// set RunOnce Key to delete MsiAsmCache Directory
|
|
//
|
|
HKEY hk = NULL;
|
|
if ( ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, szRunOnceMsiAsmCacheRegKey, 0, NULL, 0, KEY_SET_VALUE, NULL, &hk, NULL))
|
|
{
|
|
// we do not care it success or fail, we could live with it
|
|
CHAR buf[MAX_PATH * 2];
|
|
|
|
sprintf(buf, szRunOnceValueCommandLine, Buf);
|
|
|
|
if ( ERROR_SUCCESS != RegSetValueEx(hk, szRunOnceMsiAsmCacheName, 0, REG_SZ, (CONST BYTE *)buf, strlen(buf) + 1))
|
|
{
|
|
DbgPrintMessageBox("::SetRunOnceKey failed::");
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
|
|
Exit:
|
|
if ( SxsContext != NULL)
|
|
{
|
|
pfnSxsEndAssemblyInstall(
|
|
SxsContext,
|
|
fSuccess ? SXS_END_ASSEMBLY_INSTALL_FLAG_COMMIT : SXS_END_ASSEMBLY_INSTALL_FLAG_ABORT,
|
|
NULL);
|
|
}
|
|
|
|
if ( hSxs != NULL)
|
|
FreeLibrary(hSxs);
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFind);
|
|
|
|
// return the result from the actual migration call
|
|
return lResult;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// API of WIN-NT MIGRATION Dll
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
LONG CALLBACK QueryMigrationInfoA(PMIGRATIONINFOA * VersionInfo)
|
|
{
|
|
|
|
DbgPrintMessageBox("in QueryMigrationInfo");
|
|
|
|
if (IsThereAssembliesForMigrate() == FALSE)
|
|
{
|
|
return ERROR_NOT_INSTALLED; // no further migration
|
|
}
|
|
|
|
|
|
// only work for Win98 and win2k upgrade to winxp !!!
|
|
if (VersionInfo != NULL)
|
|
{
|
|
if (g_MigrationInfo == NULL)
|
|
{
|
|
g_MigrationInfo = (PMIGRATIONINFOA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MIGRATIONINFOA));
|
|
|
|
if (g_MigrationInfo == NULL)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
g_MigrationInfo->Size = sizeof(MIGRATIONINFOA);
|
|
g_MigrationInfo->StaticProductIdentifier = g_szProductId;
|
|
g_MigrationInfo->DllVersion = 200;
|
|
g_MigrationInfo->CodePageArray = NULL;
|
|
g_MigrationInfo->SourceOs = OS_WINDOWS2000;
|
|
g_MigrationInfo->TargetOs = OS_WINDOWSWHISTLER;
|
|
g_MigrationInfo->NeededFileList = NULL;
|
|
g_MigrationInfo->VendorInfo = &g_VendorInfo;
|
|
|
|
*VersionInfo = g_MigrationInfo;
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
LONG InitializeOnSource()
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////
|
|
LONG __stdcall InitializeSrcA(LPCSTR WorkingDirectory, LPCSTR SourceDirectories, LPCSTR MediaDirectory, PVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("in InitializeSrcA");
|
|
return InitializeOnSource();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
LONG CALLBACK GatherUserSettingsA(LPCSTR AnswerFile, HKEY UserRegKey, LPCSTR UserName, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("in GatherUserSettingsA");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
LONG CALLBACK GatherSystemSettingsA(LPCSTR AnswerFile, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("in GatherSystemSettingsA");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Initialization routine on WinNT. Just stores of the migration
|
|
// working directory.
|
|
LONG CALLBACK InitializeDstA(LPCSTR WorkingDirectory, LPCSTR SourceDirectories, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("in InitializeDstA");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
LONG CALLBACK ApplyUserSettingsA(
|
|
HINF AnswerFileHandle,
|
|
HKEY UserRegKey,
|
|
LPCSTR UserName,
|
|
LPCSTR UserDomain,
|
|
LPCSTR FixedUserName,
|
|
LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("in ApplyUserSettingsA");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Called once on NT
|
|
LONG CALLBACK ApplySystemSettingsA(HINF UnattendInfHandle, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("in ApplySystemSettingsA");
|
|
if (ERROR_SUCCESS != MigrateMSIInstalledWin32Assembly())
|
|
{
|
|
// just display on chk version, no bother to return to setup
|
|
DbgPrintMessageBox("the return value of MigrateMSIInstalledWin32Assembly isnot totally successful.\n");
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
HINSTANCE hinstDLL,
|
|
DWORD fdwReason,
|
|
LPVOID lpvReserved
|
|
)
|
|
{
|
|
switch (fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
g_MigrationInfo = NULL;
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
if (g_MigrationInfo != NULL)
|
|
{
|
|
if (lpvReserved != NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, g_MigrationInfo);
|
|
}
|
|
g_MigrationInfo = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// API of WIN9X MIGRATION Dll
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////
|
|
// called by setup to extract migration DLL version and support
|
|
// information.
|
|
LONG CALLBACK QueryVersion(LPCSTR *ProductID, LPUINT DllVersion, LPINT *CodePageArray, LPCSTR *ExeNamesBuf, PVENDORINFO *VendorInfo)
|
|
{
|
|
DbgPrintMessageBox("QueryVersion");
|
|
|
|
if (IsThereAssembliesForMigrate() == FALSE)
|
|
{
|
|
return ERROR_NOT_INSTALLED; // no further migration
|
|
}
|
|
|
|
|
|
// product ID information
|
|
*ProductID = g_szProductId;
|
|
*DllVersion = 200;
|
|
|
|
// DLL is language independent.
|
|
*CodePageArray = NULL;
|
|
|
|
// no EXE search is required
|
|
*ExeNamesBuf = NULL;
|
|
|
|
// vendor information
|
|
*VendorInfo = &g_VendorInfo;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
LONG __stdcall Initialize9x(LPCSTR WorkingDirectory, LPCSTR SourceDirectories, LPCSTR MediaDirectory)
|
|
{
|
|
DbgPrintMessageBox("Initialize9x");
|
|
return InitializeOnSource();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
LONG CALLBACK MigrateUser9x(HWND ParentWnd, LPCSTR AnswerFile, HKEY UserRegKey, LPCSTR UserName, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("MigrateUser9x");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
LONG CALLBACK MigrateSystem9x(HWND ParentWnd, LPCSTR AnswerFile, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("MigrateSystem9x");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
LONG CALLBACK InitializeNT(LPCWSTR WorkingDirectory, LPCWSTR SourceDirectories, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("InitializeNT");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
LONG CALLBACK MigrateUserNT(HINF AnswerFileHandle, HKEY UserRegKey, LPCWSTR UserName, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("MigrateUserNT");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
typedef HRESULT (_stdcall * PFN_MigrateFusionWin32AssemblyToXP)(PCWSTR pszInstallerDir);
|
|
///////////////////////////////////////////////////////////////////////
|
|
LONG CALLBACK MigrateSystemNT(HINF UnattendInfHandle, LPVOID Reserved)
|
|
{
|
|
DbgPrintMessageBox("MigrateSystemNT");
|
|
if (ERROR_SUCCESS != MigrateMSIInstalledWin32Assembly())
|
|
{
|
|
// just display on chk version, no bother to return to setup
|
|
DbgPrintMessageBox("the return value of MigrateMSIInstalledWin32Assembly isnot totally successful.\n");
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|