Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

624 lines
17 KiB

#include "shellprv.h"
#pragma hdrstop
#include "lnktrack.h"
#include "resolve.h"
// compute a weighted score for a given find
int ScoreFindData(RESOLVE_SEARCH_DATA *prs, LPCTSTR pszDir, const WIN32_FIND_DATA *pfd)
{
int iScore = 0;
BOOL bSameName, bSameCreateDate, bSameWriteTime, bSameExt, bHasCreateDate;
bSameName = lstrcmpi(prs->pfd->cFileName, pfd->cFileName) == 0;
bSameExt = lstrcmpi(PathFindExtension(prs->pfd->cFileName), PathFindExtension(pfd->cFileName)) == 0;
bHasCreateDate = !IsNullTime(&pfd->ftCreationTime);
bSameCreateDate = bHasCreateDate &&
(CompareFileTime(&pfd->ftCreationTime, &prs->pfd->ftCreationTime) == 0);
bSameWriteTime = !IsNullTime(&pfd->ftLastWriteTime) &&
(CompareFileTime(&pfd->ftLastWriteTime, &prs->pfd->ftLastWriteTime) == 0);
if (bSameName || bSameCreateDate)
{
if (bSameName)
iScore += bHasCreateDate ? 16 : 32;
if (bSameCreateDate)
{
iScore += 32;
if (bSameExt)
iScore += 8;
}
if (bSameWriteTime)
iScore += 8;
if (pfd->nFileSizeLow == prs->pfd->nFileSizeLow)
iScore += 4;
// if it is in the same folder as the original give it a slight bonus
if (lstrcmpi(pszDir, prs->pszSearchOrigin) == 0)
iScore += 2;
}
else
{
// doesn't have create date, apply different rules
if (bSameExt)
iScore += 8;
if (bSameWriteTime)
iScore += 8;
if (pfd->nFileSizeLow == prs->pfd->nFileSizeLow)
iScore += 4;
}
return iScore;
}
BOOL BeenThereDoneThat(LPCTSTR pszOriginal, LPCTSTR pszPath)
{
return PathCommonPrefix(pszOriginal, pszPath, NULL) == lstrlen(pszPath);
}
typedef struct _PATH_NODE {
struct _PATH_NODE *pNext;
int cDepth; // levels from top of search
// I choose to store this rather than use two lists for
// simplicity; even though using two lists would result
// in slightly less memory.
TCHAR szPath[1];
} PATH_NODE;
PATH_NODE *AllocPathNode(LPCTSTR pszStr)
{
PATH_NODE *p = LocalAlloc(LPTR, lstrlen(pszStr)*SIZEOF(TCHAR) + sizeof(PATH_NODE));
if (p)
lstrcpy(p->szPath, pszStr);
return p;
}
// iterative link search code (breadth first search)
//
// in:
// prs resolve search state
//
// pszFolder root of the search, we start here then resurse on
// all sub folders
//
// cLevels -1 means do all subdirs
// 0 means do this directory only
// 1 means do this directory and immediate children
BOOL SearchInFolder(RESOLVE_SEARCH_DATA *prs,
LPCTSTR pszFolder,
int cLevels,
int iStopScore,
const BOOL *pfTerminate)
{
PATH_NODE *pFree, *pFirst, *pLast; // list in FIFO order
// initial list of the one folder we want to look in
pLast = pFirst = AllocPathNode(pszFolder);
while (pFirst && prs->bContinue)
{
TCHAR szPath[MAX_PATH];
HANDLE hfind;
DebugMsg(DM_TRACE, TEXT("SearchInFolder: %s"), pFirst->szPath);
PathCombine(szPath, pFirst->szPath, c_szStarDotStar);
hfind = FindFirstFile(szPath, &prs->fd);
if (hfind != INVALID_HANDLE_VALUE)
{
do {
if (prs->fd.cFileName[0] != TEXT('.'))
{
DWORD dwFind = prs->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
if (!(dwFind ^ prs->dwMatch) && !PathIsLink(prs->fd.cFileName))
{
// both are files or folders, see how it scores
int iScore = ScoreFindData(prs, pFirst->szPath, &prs->fd);
if (iScore > prs->iScore)
{
prs->fdFound = prs->fd;
DebugMsg(DM_TRACE, TEXT("Better match found %s, %d"), prs->fd.cFileName, iScore);
// store the score and fully qualified path
prs->iScore = iScore;
PathCombine(prs->fdFound.cFileName, pFirst->szPath, prs->fd.cFileName);
}
}
if (prs->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
PathCombine(szPath, pFirst->szPath, prs->fd.cFileName);
if (!BeenThereDoneThat(prs->pszSearchOrigin, szPath) && !IsFileInBitBucket(szPath))
{
if (cLevels == -1 || pFirst->cDepth + 1 <= cLevels)
{
PATH_NODE *p = AllocPathNode(szPath);
if (p)
{
p->cDepth = pFirst->cDepth + 1;
pLast->pNext = p;
pLast = p;
}
}
}
}
}
#ifdef ENABLE_TRACK
if (g_fNewTrack)
{
if( FAILED( TimeoutExpired( prs->dwTimeLimit ))
||
*pfTerminate
)
{
prs->bContinue = FALSE;
}
}
else
{
#endif
if (GetTickCount() >= prs->dwTimeLimit)
prs->bContinue = FALSE;
#ifdef ENABLE_TRACK
}
#endif
} while (prs->bContinue && FindNextFile(hfind, &prs->fd));
FindClose(hfind);
}
// remove the element we just searched from the list
// leaving other elements we may have added
Assert(pFirst && pLast);
pFree = pFirst;
pFirst = pFirst->pNext;
Assert(pFirst || pFree == pLast);
LocalFree(pFree);
if (prs->iScore >= iStopScore)
{
prs->bContinue = FALSE;
DebugMsg(DM_TRACE, TEXT("search terminated match found %d"), prs->iScore);
}
}
// if we were canceled make sure we clean up
while (pFirst)
{
pFree = pFirst;
pFirst = pFirst->pNext;
LocalFree(pFree);
}
return prs->bContinue;
}
// result in prs->fdFound.cFileName
VOID
DoDownLevelSearch(RESOLVE_SEARCH_DATA *prs,
TCHAR szFolderPath[],
int iStopScore,
const BOOL *pfTerminate)
{
int cUp = LNKTRACK_HINTED_UPLEVELS;
LPITEMIDLIST pidl;
//
// search up from old location
//
lstrcpy(szFolderPath, prs->pszSearchOriginFirst);
prs->pszSearchOrigin = prs->pszSearchOriginFirst;
while (cUp-- != 0 && SearchInFolder(prs, szFolderPath, LNKTRACK_HINTED_DOWNLEVELS, iStopScore, pfTerminate))
{
if (PathIsRoot(szFolderPath) || !PathRemoveFileSpec(szFolderPath))
break;
}
if (prs->bContinue)
{
//
// search down from desktop
//
HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
if (hr == NOERROR)
{
if (SHGetPathFromIDList(pidl, szFolderPath))
{
prs->pszSearchOrigin = szFolderPath;
SearchInFolder(prs, szFolderPath, LNKTRACK_DESKTOP_DOWNLEVELS, iStopScore, pfTerminate);
}
ILFree(pidl);
}
}
if (prs->bContinue)
{
//
// search down from root of fixed drives
//
TCHAR atch[4];
lstrcpy(atch, TEXT("C:\\"));
prs->pszSearchOrigin = atch;
for (; prs->bContinue && atch[0] <= TEXT('Z'); atch[0]++)
{
lstrcpy(szFolderPath, atch);
if (GetDriveType(atch) == DRIVE_FIXED)
{
SearchInFolder(prs, szFolderPath, LNKTRACK_ROOT_DOWNLEVELS, iStopScore, pfTerminate);
}
}
}
if (prs->bContinue)
{
//
// resume search of last volume (should do an exclude list)
//
lstrcpy(szFolderPath, prs->pszSearchOriginFirst);
prs->pszSearchOrigin = prs->pszSearchOriginFirst;
while (SearchInFolder(prs, szFolderPath, -1, iStopScore, pfTerminate))
{
if (PathIsRoot(szFolderPath) || !PathRemoveFileSpec(szFolderPath))
break;
}
}
}
DWORD CALLBACK LinkFindThreadProc(LPVOID pv)
{
RESOLVE_SEARCH_DATA *prs = pv;
TCHAR szPath[MAX_PATH];
HRESULT hr;
BOOL fTerminate = FALSE;
DoDownLevelSearch(prs, szPath, MIN_NO_UI_SCORE, &fTerminate);
if (prs->hDlg)
PostMessage(prs->hDlg, WM_COMMAND, IDOK, 0);
return prs->iScore;
}
void LinkFindInit(HWND hDlg, RESOLVE_SEARCH_DATA *prs)
{
DWORD idThread;
TCHAR szFmt[128];
TCHAR szTemp[MAX_PATH + ARRAYSIZE(szFmt)];
GetDlgItemText(hDlg, IDD_NAME, szFmt, ARRAYSIZE(szFmt));
wsprintf(szTemp, szFmt, prs->pfd->cFileName);
SetDlgItemText(hDlg, IDD_NAME, szTemp);
prs->hDlg = hDlg;
prs->hThread = CreateThread(NULL, 0, LinkFindThreadProc, prs, 0, &idThread);
if (!prs->hThread)
{
DebugMsg(DM_TRACE, TEXT("Failed to create search thread"));
EndDialog(hDlg, IDCANCEL);
}
else
{
HWND hwndAni = GetDlgItem(hDlg, IDD_STATUS);
Animate_Open(hwndAni, MAKEINTRESOURCE(IDA_SEARCH)); // open the resource
Animate_Play(hwndAni, 0, -1, -1); // play from start to finish and repeat
}
}
int CALLBACK LinkFindDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
RESOLVE_SEARCH_DATA *prs = (RESOLVE_SEARCH_DATA *)GetWindowLong(hDlg, DWL_USER);
switch (wMsg) {
case WM_INITDIALOG:
SetWindowLong(hDlg, DWL_USER, lParam);
LinkFindInit(hDlg, (RESOLVE_SEARCH_DATA *)lParam);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDD_BROWSE:
prs->hDlg = NULL; // don't let the thread close us
prs->bContinue = FALSE; // cancel thread
Animate_Stop(GetDlgItem(hDlg, IDD_STATUS));
if (GetFileNameFromBrowse(hDlg, prs->fd.cFileName, ARRAYSIZE(prs->fd.cFileName), prs->pszSearchOriginFirst, prs->pfd->cFileName, NULL, NULL))
{
HANDLE hfind = FindFirstFile(prs->fd.cFileName, &prs->fdFound);
Assert(hfind != INVALID_HANDLE_VALUE);
FindClose(hfind);
lstrcpy(prs->fdFound.cFileName, prs->fd.cFileName);
prs->iScore = MIN_NO_UI_SCORE;
wParam = IDOK;
}
else
{
wParam = IDCANCEL;
}
// Fall through...
case IDCANCEL:
prs->bContinue = FALSE; // tell searching thread to stop
// Fall through...
case IDOK:
// thread posts this to us
Assert(prs->hThread);
// We will attempt to wait up to 5 seconds for the thread to terminate
if (WaitForSingleObject(prs->hThread, 5000) == WAIT_TIMEOUT)
{
// BUGBUG: if this timed out we potentially leaked the list
// of paths that we are searching (PATH_NODE list)
TerminateThread(prs->hThread, (DWORD)-1); // Blow it away!
}
CloseHandle(prs->hThread);
EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
DWORD
GetTimeOut(DWORD uFlags)
{
DWORD dwTimeOut = HIWORD(uFlags);
if (dwTimeOut == 0)
dwTimeOut = NOUI_SEARCH_TIMEOUT;
else
if (dwTimeOut == 0xFFFF)
{
TCHAR tszTimeOut[10];
LONG cbTimeOut = SIZEOF(tszTimeOut);
tszTimeOut[0] = 0;
if (ERROR_SUCCESS == RegQueryValue(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Tracking\\TimeOut"),
tszTimeOut,
&cbTimeOut))
dwTimeOut = _fatoi(tszTimeOut);
else
dwTimeOut = NOUI_SEARCH_TIMEOUT;
}
return(dwTimeOut);
}
// in:
// hwnd NULL implies NOUI
// uFlags IShellLink::Resolve flags parameter
// pszFolder place to look
//
//
// in/out:
// pfd in: thing we are looking for on input (cFileName unqualified path)
// out: if return is TRUE filled in with the new find info
// returns:
// IDOK found something
// IDNO didn't find it
// IDCANCEL user canceled the operation
//
int FindInFolder(HWND hwnd, UINT uFlags, LPCTSTR pszPath, WIN32_FIND_DATA *pfd)
{
RESOLVE_SEARCH_DATA rs;
TCHAR szSearchStart[MAX_PATH];
lstrcpy(szSearchStart, pszPath);
PathRemoveFileSpec(szSearchStart);
while (!PathIsDirectory(szSearchStart))
{
if (PathIsRoot(szSearchStart) || !PathRemoveFileSpec(szSearchStart))
{
DebugMsg(DM_TRACE, TEXT("root path does not exists %s"), szSearchStart);
return IDNO;
}
}
rs.iScore = 0; // nothing found yet
rs.bContinue = TRUE;
rs.pszSearchOriginFirst = szSearchStart;
rs.pszSearchOrigin = szSearchStart;
rs.dwTimeLimit = GetTickCount() + UI_SEARCH_TIMEOUT;
rs.dwMatch = pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; // must match bits
rs.pfd = pfd;
rs.hDlg = NULL;
if (uFlags & SLR_NO_UI)
{
rs.dwTimeLimit = GetTickCount() + GetTimeOut(uFlags);
LinkFindThreadProc(&rs);
}
else
{
if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_LINK_SEARCH), hwnd, LinkFindDlgProc, (LPARAM)&rs) != IDOK)
{
return IDCANCEL; // cancel stuff below
}
}
if (rs.iScore < MIN_NO_UI_SCORE)
{
if (rs.iScore == 0 || (uFlags & SLR_NO_UI))
return IDNO;
// BUGBUG: strip extensions
if (ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_LINKCHANGED), MAKEINTRESOURCE(IDS_LINKERROR),
MB_YESNO | MB_ICONEXCLAMATION, PathFindFileName(pszPath), rs.fdFound.cFileName) != IDYES)
return IDCANCEL;
}
*pfd = rs.fdFound;
return IDOK;
}
//+-------------------------------------------------------------------
//
// Member: TimeoutExpired
//
// Synopsis: Return MK_E_EXCEEDEDDEADLINE if the deadline has
// passed. Note that the deadline is an absolute tick
// count, as specified by GetTickCount().
// A deadline of 0, however, indicates infinity, and
// can never be expired.
//
// History: 17-Nov-95 MikeHill Created
//
//--------------------------------------------------------------------
#ifdef ENABLE_TRACK
HRESULT
TimeoutExpired( DWORD dwTickCountDeadline )
{
// If there is time left on the deadline, then we haven't expired.
return( DeltaTickCount( dwTickCountDeadline ) ? S_OK : MK_E_EXCEEDEDDEADLINE );
} // TimeoutExpired
#endif
//+--------------------------------------------------------------------
//
// Function: DeltaTickCount
//
// Purpose: Calculate the number of ticks from the current time
// to a given deadline. If the given deadline is 0,
// (indicating infinity), this routine will return 0xFFFFFFFF.
//
// Inputs: [DWORD] -- End tick count
//
// Output: [DWORD] -- Number of ticks until the end tick count,
// or zero if current time is past the end.
//
// Notes: This routine handles wraps in
// GetTickCount() by assuming that if the deadline is
// more than 2**31 ticks below the current time, then
// it actually represents a time in the future (after
// GetTickCount() wraps). This definition/use of
// dwTickCountDeadline is described in the OLE2.0
// design spec, section 7.2 "IMoniker interface and
// OLE IMoniker Implementations".
//
// History: 5-Nov-95 MikeHill Modified from CTimeout::Expired().
// 17-Nov-95 MikeHill Changed to match OLE 2.0 design spec.
//
//+--------------------------------------------------------------------
#ifdef ENABLE_TRACK
#define TICK_COUNT_WRAP_MARGIN 0x80000000 // 2**31
DWORD DeltaTickCount( DWORD dwTickCountDeadline )
{
DWORD dwNow;
// Is there a deadline?
if( dwTickCountDeadline == 0 )
{
return INFINITE; // There is no deadline.
}
// Are we approaching or at the deadline?
dwNow = GetTickCount();
if( dwNow <= dwTickCountDeadline )
{
return( dwTickCountDeadline - dwNow );
}
// Otherwise, the current tick count is greater than the deadline tick count.
else
{
// But if the deadline is more than 2**31 below
// the current time, then it actually represents a time
// in the future (after GetTickCount() wraps).
if( ( dwNow > TICK_COUNT_WRAP_MARGIN )
&&
( dwTickCountDeadline < (dwNow - TICK_COUNT_WRAP_MARGIN) )
)
{
// The delta tick count is the time from now to the wrap,
// plus the time after the wrap.
return ( (INFINITE - dwNow) + 1 + dwTickCountDeadline );
}
// Otherwise, the current time is greater than the deadline,
// and there's no way a wrap can explain it.
else
{
return( 0 ); // Timeout is Expired
}
}
} // DeltaTickCount()
#endif