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

716 lines
20 KiB
C

/*++
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 <winbasep.h>
#include <shellapi.h>
//
// 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;
}