/*++ Copyright (c) 1998 Microsoft Corporation Module Name: lnkstub.c Abstract: Implements a simple application that replaces incompatible applications detected dynamically during Win9x upgrade to Windows 2000. Author: Calin Negreanu (calinn) 25-Feb-1999 Revision History: --*/ #include "pch.h" #include "resource.h" #include "msg.h" #include #include // // Globals // HINSTANCE g_hInst; HANDLE g_hHeap; BOOL g_RemoveLnk = FALSE; BOOL g_RestoreLnk = FALSE; BOOL g_RunOrgApp = FALSE; HICON g_hIcon = NULL; BOOL g_ReportAvailable = FALSE; BOOL g_StartAppAvailable = FALSE; BOOL g_RemoveLnkAvailable = FALSE; BOOL g_RestoreLnkAvailable = FALSE; PCTSTR g_ReportPath = NULL; DWORD g_Announcement = ACT_INC_NOBADAPPS; DWORD g_Availability = 1; PCTSTR g_ActualLnkName = NULL; // // Library prototypes // BOOL WINAPI MigUtil_Entry ( HINSTANCE hInstance, DWORD dwReason, PVOID lpReserved ); // // Local prototypes // BOOL CALLBACK DialogProc ( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); // // Implementation // VOID pSetProgramIcon ( PCTSTR OrigIconPath, INT OrigIconNr ) { HINSTANCE iconFile; iconFile = LoadLibraryEx (OrigIconPath, NULL, LOAD_LIBRARY_AS_DATAFILE); if (iconFile) { g_hIcon = LoadIcon (iconFile, MAKEINTRESOURCE(OrigIconNr)); FreeLibrary (iconFile); } if (g_hIcon == NULL) { g_hIcon = LoadIcon (NULL, IDI_EXCLAMATION); } } BOOL pIsFileAccessible ( IN PCTSTR FileName, IN DWORD DesiredAccess ) { HANDLE fileHandle; fileHandle = CreateFile ( FileName, DesiredAccess, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING, NULL ); if (fileHandle == INVALID_HANDLE_VALUE) { return FALSE; } CloseHandle (fileHandle); return TRUE; } BOOL pRestoreLnk ( IN PCTSTR LnkName, IN PCTSTR LnkTarget, IN PCTSTR LnkArgs, IN PCTSTR LnkWorkDir, IN PCTSTR LnkIconPath, IN INT LnkIconNr, IN INT ShowMode ) { IShellLink *psl = NULL; IPersistFile *ppf = NULL; BOOL result = FALSE; HRESULT comResult; comResult = CoInitialize (NULL); if (FAILED (comResult)) { return FALSE; } __try { if (!DoesFileExist (LnkName)) { __leave; } if (((LnkTarget == NULL) || (LnkTarget [0] == 0)) && ((LnkWorkDir == NULL) || (LnkWorkDir [0] == 0)) && ((LnkIconPath == NULL) || (LnkIconPath [0] == 0)) && (LnkIconNr == 0) ) { __leave; } comResult = CoCreateInstance ( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (void **) &psl); if (comResult != S_OK) { __leave; } comResult = psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile, (void **) &ppf); if (comResult != S_OK) { __leave; } // // We only load if the file was really a LNK // comResult = ppf->lpVtbl->Load(ppf, LnkName, STGM_READ); if (comResult != S_OK) { __leave; } if (LnkTarget != NULL) { comResult = psl->lpVtbl->SetPath (psl, LnkTarget); if (comResult != S_OK) { __leave; } } if (LnkArgs != NULL) { comResult = psl->lpVtbl->SetArguments (psl, LnkArgs); if (comResult != S_OK) { __leave; } } if (LnkWorkDir != NULL) { comResult = psl->lpVtbl->SetWorkingDirectory (psl, LnkWorkDir); if (comResult != S_OK) { __leave; } } if (LnkIconPath != NULL) { comResult = psl->lpVtbl->SetIconLocation (psl, LnkIconPath, LnkIconNr); if (comResult != S_OK) { __leave; } } comResult = psl->lpVtbl->SetShowCmd (psl, ShowMode); if (comResult != S_OK) { __leave; } comResult = ppf->lpVtbl->Save (ppf, LnkName, FALSE); if (comResult != S_OK) { __leave; } comResult = ppf->lpVtbl->SaveCompleted (ppf, LnkName); if (comResult != S_OK) { __leave; } result = TRUE; } __finally { if (ppf != NULL) { ppf->lpVtbl->Release (ppf); ppf = NULL; } if (psl != NULL) { psl->lpVtbl->Release (psl); psl = NULL; } CoUninitialize (); } return result; } INT WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR AnsiCmdLine, INT CmdShow ) /*++ Routine Description: The entry point to lnkstub.exe. All the work is done in a dialog box, so no message loop is necessary. Arguments: hInstance - The instance handle of this EXE hPrevInstance - The previous instance handle of this EXE if it is running, or NULL if no other instances exist. AnsiCmdLine - The command line (ANSI version) CmdShow - The ShowWindow command passed by the shell Return Value: Returns -1 if an error occurred, or 0 if the exe completed. --*/ { UINT Result; TCHAR winDir [MAX_PATH]; STARTUPINFO startInfo; PCTSTR OrigLnkName = NULL; PCTSTR OrigTarget = NULL; PCTSTR OrigArgs = NULL; PCTSTR OrigWorkDir = NULL; PCTSTR OrigIconPath = NULL; INT OrigIconNr = 0; INT OrigShowMode = SW_NORMAL; PCTSTR LnkStubDatFile = NULL; PBYTE LnkStubDatPtr = NULL; PBYTE LnkStubDatPtrTmp = NULL; HANDLE StubMapHandle = NULL; HANDLE StubFileHandle = NULL; INT ofsHeader; PDWORD offset; FILETIME fileTime; FILETIME reqFileTime; WIN32_FIND_DATA findData; BOOL shouldRestoreLnk = FALSE; PCTSTR reqFilePath = NULL; PCTSTR reqFileFullPath = NULL; PCTSTR oldFileSpec = NULL; PTSTR oldFilePtr = NULL; INITCOMMONCONTROLSEX init = {sizeof (INITCOMMONCONTROLSEX), 0}; InitCommonControlsEx (&init); g_hInst = hInstance; g_hHeap = GetProcessHeap(); MigUtil_Entry (hInstance, DLL_PROCESS_ATTACH, NULL); if (GetWindowsDirectory (winDir, MAX_PATH)) { g_ReportPath = JoinPaths (winDir, S_UPGRADEHTM); g_ReportAvailable = DoesFileExist (g_ReportPath) && pIsFileAccessible (g_ReportPath, GENERIC_READ); LnkStubDatFile = JoinPaths (winDir, S_LNKSTUB_DAT); } // let's see if we can get the LNK that launched us. GetStartupInfo (&startInfo); if (startInfo.dwFlags & STARTF_TITLEISLINKNAME) { g_ActualLnkName = DuplicatePathString (startInfo.lpTitle, 0); g_RemoveLnkAvailable = DoesFileExist (g_ActualLnkName) && pIsFileAccessible (g_ActualLnkName, GENERIC_READ|GENERIC_WRITE); } // now let's see if we can find data about our original LNK if (LnkStubDatFile) { __try { LnkStubDatPtr = MapFileIntoMemoryEx (LnkStubDatFile, &StubMapHandle, &StubFileHandle, TRUE); if (LnkStubDatPtr) { ofsHeader = atoi (AnsiCmdLine) - 1; if (ofsHeader >= 0) { // // Read details about original LNK. See w95upgnt\filemig.c // for format of lnkstub.dat // offset = (PDWORD) (LnkStubDatPtr + ofsHeader * sizeof (DWORD)); OrigLnkName = (PCTSTR) (LnkStubDatPtr + *offset); OrigTarget = GetEndOfString (OrigLnkName) + 1; OrigArgs = GetEndOfString (OrigTarget) + 1; OrigWorkDir = GetEndOfString (OrigArgs) + 1; OrigIconPath = GetEndOfString (OrigWorkDir) + 1; LnkStubDatPtrTmp = (PBYTE) (GetEndOfString (OrigIconPath) + 1); OrigIconNr = *((PINT) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (INT); OrigShowMode = *((PINT) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (INT); g_Announcement = *((PDWORD) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (DWORD); g_Availability = *((PDWORD) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (DWORD); fileTime.dwLowDateTime = *((PDWORD) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (DWORD); fileTime.dwHighDateTime = *((PDWORD) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (DWORD); reqFilePath = (PTSTR)LnkStubDatPtrTmp; // // Continue reading [in a loop] the list of required // files. This is how lnkstub detects changes from a // reinstall or uninstall, and auto-removes itself. // shouldRestoreLnk = FALSE; while (reqFilePath [0]) { if (!shouldRestoreLnk) { oldFileSpec = DuplicatePathString (OrigTarget, 0); oldFilePtr = (PTSTR)GetFileNameFromPath (oldFileSpec); if (oldFilePtr) { *oldFilePtr = 0; } reqFileFullPath = JoinPaths (oldFileSpec, reqFilePath); if (!DoesFileExistEx (reqFileFullPath, &findData)) { shouldRestoreLnk = FALSE; } FreePathString (reqFileFullPath); FreePathString (oldFileSpec); } LnkStubDatPtrTmp = (PBYTE) (GetEndOfString (reqFilePath) + 1); reqFileTime.dwLowDateTime = *((PDWORD) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (DWORD); reqFileTime.dwHighDateTime = *((PDWORD) LnkStubDatPtrTmp); LnkStubDatPtrTmp += sizeof (DWORD); reqFilePath = (PTSTR)LnkStubDatPtrTmp; if (!shouldRestoreLnk) { if ((findData.ftLastWriteTime.dwLowDateTime != reqFileTime.dwLowDateTime) || (findData.ftLastWriteTime.dwHighDateTime != reqFileTime.dwHighDateTime) ) { shouldRestoreLnk = TRUE; } } } // // Save data from memory mapped lnkstub.dat into path pool // OrigLnkName = DuplicatePathString (OrigLnkName, 0); OrigTarget = DuplicatePathString (OrigTarget, 0); OrigArgs = DuplicatePathString (OrigArgs, 0); OrigWorkDir = DuplicatePathString (OrigWorkDir, 0); OrigIconPath = DuplicatePathString (OrigIconPath, 0); g_StartAppAvailable = DoesFileExistEx (OrigTarget, &findData); g_RestoreLnkAvailable = g_RemoveLnkAvailable && g_StartAppAvailable; if (!shouldRestoreLnk) { if ((findData.ftLastWriteTime.dwLowDateTime != fileTime.dwLowDateTime) || (findData.ftLastWriteTime.dwHighDateTime != fileTime.dwHighDateTime) ) { shouldRestoreLnk = TRUE; } } shouldRestoreLnk = shouldRestoreLnk && g_StartAppAvailable; } } } __except (1) { UnmapFile (LnkStubDatPtr, StubMapHandle, StubFileHandle); LnkStubDatPtr = NULL; OrigLnkName = NULL; OrigTarget = NULL; OrigArgs = NULL; OrigWorkDir = NULL; OrigIconPath = NULL; g_StartAppAvailable = FALSE; g_RestoreLnkAvailable = FALSE; } if (LnkStubDatPtr) { UnmapFile (LnkStubDatPtr, StubMapHandle, StubFileHandle); LnkStubDatPtr = NULL; } } if (OrigIconPath && *OrigIconPath) { pSetProgramIcon (OrigIconPath, OrigIconNr); } else { pSetProgramIcon (OrigTarget, OrigIconNr); } if (shouldRestoreLnk) { g_RestoreLnk = TRUE; g_RunOrgApp = TRUE; } else { switch (g_Announcement) { case ACT_REINSTALL: case ACT_REINSTALL_BLOCK: Result = DialogBox ( hInstance, MAKEINTRESOURCE(IDD_REINST_DLG), NULL, DialogProc ); break; case ACT_INC_PREINSTUTIL: Result = DialogBox ( hInstance, MAKEINTRESOURCE(IDD_PREINSTUTIL_DLG), NULL, DialogProc ); break; case ACT_INC_SIMILAROSFUNC: Result = DialogBox ( hInstance, MAKEINTRESOURCE(IDD_SIMILAROSFUNCT_DLG), NULL, DialogProc ); break; case ACT_INC_IHVUTIL: Result = DialogBox ( hInstance, MAKEINTRESOURCE(IDD_IHVUTIL_DLG), NULL, DialogProc ); break; default: Result = DialogBox ( hInstance, MAKEINTRESOURCE(IDD_INCOMP_DLG), NULL, DialogProc ); break; } } if (g_RestoreLnk) { MYASSERT (!g_RemoveLnk); if (!pRestoreLnk ( g_ActualLnkName, OrigTarget, OrigArgs, OrigWorkDir, OrigIconPath, OrigIconNr-1, OrigShowMode )) { DEBUGMSG ((DBG_ERROR, "Cannot restore %s", g_ActualLnkName)); } } if (g_RunOrgApp) { MYASSERT (!g_RemoveLnk); if (ShellExecute (NULL, NULL, OrigTarget, OrigArgs, OrigWorkDir, SW_SHOWDEFAULT) <= (HINSTANCE)32) { DEBUGMSG ((DBG_ERROR, "Cannot start %s", OrigTarget)); } } if (g_RemoveLnk) { if (!DeleteFile (g_ActualLnkName)) { DEBUGMSG ((DBG_ERROR, "Cannot remove %s", g_ActualLnkName)); } } if (OrigIconPath) { FreePathString (OrigIconPath); } if (OrigWorkDir) { FreePathString (OrigWorkDir); } if (OrigArgs) { FreePathString (OrigArgs); } if (OrigTarget) { FreePathString (OrigTarget); } if (OrigLnkName) { FreePathString (OrigLnkName); } if (g_ActualLnkName) { FreePathString (g_ActualLnkName); g_ActualLnkName = NULL; } if (LnkStubDatFile) { FreePathString (LnkStubDatFile); } if (g_ReportPath) { FreePathString (g_ReportPath); } MigUtil_Entry (hInstance, DLL_PROCESS_DETACH, NULL); return 0; } BOOL CALLBACK DialogProc ( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) /*++ Routine Description: DialogProc is the dialog procedure for the main dialog. Arguments: hdlg - Dialog window handle uMsg - Message to process wParam - Message-specific lParam - Message-specific Return Value: TRUE if the message was processed, or FALSE if the message should be processed by the OS. --*/ { static RECT LargeWndRect; static RECT SmallWndRect; static TCHAR LargeCaption[128]; static TCHAR SmallCaption[128]; static BOOL LargeWnd; RECT ButtonRect; PTSTR lnkName = NULL; PTSTR extPtr = NULL; BOOL showReport = TRUE; HINSTANCE result; switch (uMsg) { case WM_INITDIALOG: if (g_ActualLnkName) { lnkName = DuplicatePathString (GetFileNameFromPath (g_ActualLnkName), 0); if (lnkName) { extPtr = (PTSTR)GetFileExtensionFromPath (lnkName); if (extPtr) { extPtr = _tcsdec (lnkName, extPtr); if (extPtr) { *extPtr = 0; SetWindowText (hdlg, lnkName); } } FreePathString (lnkName); } } showReport = g_Availability && g_ReportAvailable; GetWindowText (GetDlgItem (hdlg, IDC_OPTIONS), LargeCaption, 125); GetWindowText (GetDlgItem (hdlg, IDC_OPTIONS), SmallCaption, 125); _tcscat (LargeCaption, TEXT(" <<")); _tcscat (SmallCaption, TEXT(" >>")); SetDlgItemText (hdlg, IDC_OPTIONS, SmallCaption); GetWindowRect (hdlg, &LargeWndRect); GetWindowRect (GetDlgItem (hdlg, IDC_DLG_SIZE_SEPARATOR), &ButtonRect); CopyMemory (&SmallWndRect, &LargeWndRect, sizeof (RECT)); SmallWndRect.bottom = ButtonRect.bottom; SetWindowPos ( hdlg, NULL, 0, 0, SmallWndRect.right-SmallWndRect.left, SmallWndRect.bottom-SmallWndRect.top, SWP_NOMOVE|SWP_NOZORDER ); EnableWindow (GetDlgItem (hdlg, IDC_START), FALSE); EnableWindow (GetDlgItem (hdlg, IDC_REMOVE), FALSE); EnableWindow (GetDlgItem (hdlg, IDC_RESTORE), FALSE); LargeWnd = FALSE; if (!showReport) { EnableWindow (GetDlgItem (hdlg, IDC_REPORTBUTTON), FALSE); ShowWindow (GetDlgItem (hdlg, IDC_REPORTTEXT), SW_HIDE); } else { EnableWindow (GetDlgItem (hdlg, IDC_REPORTBUTTON), TRUE); ShowWindow (GetDlgItem (hdlg, IDC_REPORTTEXT), SW_SHOW); } SendDlgItemMessage (hdlg, IDC_PROGICON, STM_SETICON, (LPARAM)g_hIcon, 0); return FALSE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: EndDialog (hdlg, LOWORD (wParam)); break; case IDC_REPORTBUTTON: if (HIWORD (wParam) == BN_CLICKED) { result = ShellExecute ( hdlg, NULL, g_ReportPath, NULL, NULL, SW_SHOW ); } break; case IDC_OPTIONS: if (HIWORD (wParam) == BN_CLICKED) { LargeWnd = !LargeWnd; SetWindowPos ( hdlg, NULL, 0, 0, LargeWnd?LargeWndRect.right-LargeWndRect.left:SmallWndRect.right-SmallWndRect.left, LargeWnd?LargeWndRect.bottom-LargeWndRect.top:SmallWndRect.bottom-SmallWndRect.top, SWP_NOMOVE|SWP_NOZORDER ); SetDlgItemText (hdlg, IDC_OPTIONS, LargeWnd?LargeCaption:SmallCaption); EnableWindow (GetDlgItem (hdlg, IDC_START), LargeWnd & g_StartAppAvailable); EnableWindow (GetDlgItem (hdlg, IDC_REMOVE), LargeWnd & g_RemoveLnkAvailable); EnableWindow (GetDlgItem (hdlg, IDC_RESTORE), LargeWnd & g_RestoreLnkAvailable); } break; case IDC_START: if (HIWORD (wParam) == BN_CLICKED) { g_RunOrgApp = TRUE; EndDialog (hdlg, LOWORD (wParam)); } break; case IDC_REMOVE: if (HIWORD (wParam) == BN_CLICKED) { g_RemoveLnk = TRUE; EndDialog (hdlg, LOWORD (wParam)); } break; case IDC_RESTORE: if (HIWORD (wParam) == BN_CLICKED) { g_RestoreLnk = TRUE; EndDialog (hdlg, LOWORD (wParam)); } break; } break; case WM_DESTROY: break; } return FALSE; }