//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation // // File: migrate.cpp // xiaoyuw @ 2001/09 // //-------------------------------------------------------------------------- #include #include #include #include // 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; }