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.
 
 
 
 
 
 

1423 lines
37 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: tracker.cxx
//
// Contents: Implementation of Cairo link tracking search.
//
// Classes: CTracker
//
// Functions: Tracker_InitFromPath
// Tracker_GetSize
// Tracker_Save
// Tracker_Load
// Tracker_IsDirty
// Tracker_Search
//
// History: 1-Mar-95 BillMo Created.
// 21-Sep-95 MikeHill Added Tracker_SetCreationFlags()
// and Tracker_GetCreationFlags()
// 21-Nov-95 MikeHill Moved dwTickCountDeadline into the
// ResolveSearchData and used new
// TimeoutExpired & DeltaTickCount routines.
//
// Notes: CTracker currently assumes no allocated data: i.e.
// all state is within the shell link and initialized by
// the shell link to all zeros.
//
// Codework:
//
//--------------------------------------------------------------------------
#define NO_INCLUDE_UNION 1
#include "shellprv.h"
#pragma hdrstop
#ifdef ENABLE_TRACK
#include <iofs.h>
#include <olecairo.h>
#include "tracker.h"
#include "lnktrack.h"
#include "thdcmds.hxx"
extern "C"
{
#include "loaddll.h"
#include "ids.h"
}
extern "C" DWORD GetTimeOut(DWORD uFlags);
//
// Thunk routines to go from C to C++
//
SETOBJECTIDPROC g_pfnSetObjectId = NULL;
QUERYOBJECTIDPROC g_pfnQueryObjectId = NULL;
SEARCHVOLUMEPROC g_pfnSearchVolume = NULL;
UUIDCREATEPROC g_pfnUuidCreate = NULL;
extern "C" WORD wDebugMask;
extern "C"
VOID Tracker_InitCode()
{
static BOOL _fLoadAttempted;
static HINSTANCE _hNtDllDll;
static HINSTANCE _hRpcrt4Dll;
TCHAR tszDebug[10];
LONG cbDebug = SIZEOF(tszDebug);
tszDebug[0] = 0;
RegQueryValue(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Tracking\\EnableTrack"),
tszDebug,
&cbDebug);
g_fNewTrack = (tszDebug[0] == TEXT('y') || tszDebug[0] == TEXT('Y'));
if (g_fNewTrack && !_fLoadAttempted)
{
wDebugMask = 0x10e;
_fLoadAttempted = TRUE;
_hNtDllDll = LoadLibrary(TEXT("NTDLL.DLL"));
if (_hNtDllDll != NULL)
{
g_pfnSetObjectId = (SETOBJECTIDPROC) GetProcAddress(_hNtDllDll,
"RtlSetObjectId");
g_pfnQueryObjectId = (QUERYOBJECTIDPROC) GetProcAddress(_hNtDllDll,
"RtlQueryObjectId");
g_pfnSearchVolume = (SEARCHVOLUMEPROC) GetProcAddress(_hNtDllDll,
"RtlSearchVolume");
}
_hRpcrt4Dll = LoadLibrary(TEXT("RPCRT4.DLL"));
if (_hRpcrt4Dll != NULL)
{
g_pfnUuidCreate = (UUIDCREATEPROC) GetProcAddress(_hRpcrt4Dll,
"UuidCreate");
}
if (g_pfnSetObjectId == NULL)
{
g_fNewTrack = FALSE;
MessageBox(GetDesktopWindow(),
TEXT("Error"),
TEXT("To enable tracking you need debug NTDLL.DLL"),
MB_OK);
}
}
}
extern "C"
HRESULT Tracker_InitFromPath(CTracker *pThis, const TCHAR *ptszPath)
{
return pThis->InitFromPath(ptszPath);
}
extern "C"
ULONG Tracker_GetSize(struct CTracker *pThis)
{
return(pThis->uGetSize());
}
extern "C"
VOID Tracker_Save(struct CTracker *pThis, BYTE *pb)
{
pThis->Save(pb);
}
extern "C"
HRESULT Tracker_Load(struct CTracker *pThis, BYTE *pb, ULONG cb)
{
return pThis->Load(pb, cb);
}
extern "C"
BOOL Tracker_IsDirty( struct CTracker *pThis )
{
return(pThis->IsDirty());
}
extern "C"
VOID Tracker_SetCreationFlags(struct CTracker *pThis,
DWORD dwCreationFlags )
{
pThis->SetCreationFlags( dwCreationFlags );
}
extern "C"
VOID Tracker_GetCreationFlags(struct CTracker *pThis,
DWORD *pdwCreationFlags )
{
pThis->GetCreationFlags( pdwCreationFlags );
}
OBJECTID g_oidZero;
HRESULT g_hr;
CGlobalThreadSet *g_pts = NULL;
//+-------------------------------------------------------------------
//
// Function: ConvertToNtPath, public
//
// Synopsis: Convert the path in the buffer to an Nt style path, in the
// other buffer.
//
// Arguments: [pwszVolumePath] -- In. Buffer containing Dos style path.
// [pwszNtPath] -- Out. Buffer for new path.
// [cwcBuf] -- In. Size of buffer in wide chars.
//
// Returns: Return value is length in characters (not including nul)
// of converted path. Zero if not completely converted.
//
//--------------------------------------------------------------------
unsigned
ConvertToNtPath(const TCHAR *ptszVolumePath, TCHAR *ptszNtPath, ULONG cwcBuf)
{
unsigned i=12; // for \DosDevices\ .
TCHAR *ptszWrite = ptszNtPath;
ULONG cwcLeft = cwcBuf;
BOOL fDone = FALSE;
unsigned ret;
if (ptszVolumePath[0] == TEXT('\\') && ptszVolumePath[1] == TEXT('\\'))
{
i+=3;
ptszVolumePath++;
// i = 15
// ptszVolumePath -----\ .
// |
// v
// \\billmo2\rootd
//
}
if (cwcLeft > i)
{
memcpy(ptszWrite, TEXT("\\DosDevices\\UNC"), i*SIZEOF(TCHAR));
ptszWrite += i;
cwcLeft -= i;
while (cwcLeft)
{
*ptszWrite = *ptszVolumePath;
cwcLeft --;
if (*ptszVolumePath == 0)
{
// we just copied a null
fDone = TRUE;
break;
}
else
{
ptszVolumePath++;
ptszWrite++;
}
}
}
ret = (fDone ? ptszWrite - ptszNtPath : 0);
return(ret);
}
//+-------------------------------------------------------------------
//
// Function: OpenObject, public
//
// Synopsis: Open a file or directory.
//
// Effects: Convert the passed path to an Nt path and call
// NtOpenFile. This is because we need to be able to
// open directories.
//
// Arguments: [ptszDosPath] -- The Dos path to the object.
// [AccessMask] -- Optional access mask.
// [ShareAccess] -- Optional share access.
//
// Returns: NT status code.
//
// Signals: Nothing.
//
// Modifies: Nothing.
//
// History: 27-May-93 BillMo Created.
//
// Notes:
//
//--------------------------------------------------------------------
NTSTATUS
OpenObject(const TCHAR * ptszDosPath,
ACCESS_MASK AccessMask,
ULONG ShareAccess,
HANDLE * ph)
{
#if defined(WINNT) && defined(UNICODE)
unsigned cwcNtPath;
TCHAR tszNtPath[MAX_PATH+1];
NTSTATUS Status;
if (cwcNtPath = ConvertToNtPath(ptszDosPath, tszNtPath, SIZEOF(tszNtPath)/SIZEOF(tszNtPath[0])))
{
OBJECT_ATTRIBUTES ObjectAttr;
IO_STATUS_BLOCK IoStatus;
UNICODE_STRING uVolume = { cwcNtPath * SIZEOF( TCHAR ), // Length
cwcNtPath * SIZEOF( TCHAR ), // Max length
tszNtPath }; // String
InitializeObjectAttributes( &ObjectAttr, // Structure
&uVolume, // Name
OBJ_CASE_INSENSITIVE, // Attributes
0, // Root
0 ); // Security
//
// We have to use NtOpenFile because CreateFile won't open directories.
//
Status = NtOpenFile( ph, // Handle
AccessMask, // Access
&ObjectAttr, // Object Attributes
&IoStatus, // I/O Status block
ShareAccess, // Sharing
FILE_SYNCHRONOUS_IO_NONALERT ); // Flags
}
else
{
Status = STATUS_NAME_TOO_LONG;
}
DebugMsg(DM_TRACK, TEXT("OpenObject: tszNtPath=%s, Status=%08X"), tszNtPath, Status);
return(Status);
#else
return(STATUS_NOT_IMPLEMENTED);
#endif
}
//+---------------------------------------------------------------------------
//
// Function: SearchVolume
//
// Synopsis: Search the given volume for an object with matching OBJECTID
//
// Arguments: [ptszAncestor] -- path to object/root to open to get
// handle to send FSCTL on.
//
// [poid] -- OBJECTID
// [pfd] -- returned fully qualified found path
//
// Returns: S_OK if object found,
// S_FALSE if couldn't search, or not found
// HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW)
//
// History: 7-Jan-93 BillMo Created.
//
// Notes: This function is only available on WINNT
//
//----------------------------------------------------------------------------
HRESULT
SearchVolume(const TCHAR * ptszAncestor,
OBJECTID * poid,
WIN32_FIND_DATA * pfd)
{
HANDLE h;
HRESULT hr = S_FALSE;
#if defined(WINNT) && defined(UNICODE)
NTSTATUS Status;
Assert(ptszAncestor[lstrlen(ptszAncestor)-1] == TEXT('\\'));
Status = OpenObject(ptszAncestor,
SYNCHRONIZE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&h);
if (Status == STATUS_SUCCESS)
{
FINDOBJECTOUT foo;
Status = RtlSearchVolume( h, // Handle
poid,
0,
FALSE,
SIZEOF(FINDOBJECTOUT),
&foo );
if (Status == STATUS_SUCCESS)
{
if (foo.cwcExact != 0)
{
TCHAR tszBuf[MAX_PATH+1];
int cwcAncestor = lstrlen(ptszAncestor);
int cwcTotal = cwcAncestor + foo.cwcExact;
if (cwcTotal <= sizeof(tszBuf)/sizeof(tszBuf[0]))
{
HANDLE hFind;
Assert(foo.wszPaths[0] == TEXT('\\'));
memcpy(tszBuf, ptszAncestor, cwcAncestor*SIZEOF(TCHAR));
memcpy(tszBuf+cwcAncestor, foo.wszPaths + 1, (foo.cwcExact-1)*SIZEOF(TCHAR));
tszBuf[cwcAncestor + foo.cwcExact - 1] = TEXT('\0');
if (!IsFileInBitBucket(tszBuf))
{
hFind = FindFirstFile(tszBuf, pfd);
if (hFind != INVALID_HANDLE_VALUE)
{
lstrcpy(pfd->cFileName, tszBuf);
FindClose(hFind);
hr = S_OK;
}
}
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
}
}
}
NtClose( h );
}
DebugMsg(DM_TRACK,
TEXT("SearchVolume(thread=%d): ptszAncestor=%s, OID=%08X:%04X:%04X, Status=%08X, hr=%08X, ptszFound=%s"),
GetCurrentThreadId(),
ptszAncestor,
poid->Lineage.Data1,
poid->Lineage.Data2,
poid->Lineage.Data3,
Status,
hr,
hr == S_OK ? pfd->cFileName : TEXT("") );
#endif
return( hr );
}
//+-------------------------------------------------------------------
//
// Function: GetObjectInfo
//
// Synopsis: Get OBJECTID and, later, other info from an object
// to allow tracking.
//
// Effects:
//
// Arguments: [ptszPath] -- the path to the file/directory.
// [poid] -- buffer for returned OBJECTID.
//
//
// Returns: STATUS_SUCCESS if no error occurs.
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 6-Aug-93 BillMo Created.
//
// Notes:
//
//--------------------------------------------------------------------
NTSTATUS
GetObjectInfo(const TCHAR *ptszPath, OBJECTID *poid)
{
NTSTATUS Status;
HANDLE h;
OBJECTID oid;
BOOL fRetry = FALSE;
BOOL fReadOnly = FALSE;
Status = OpenObject(ptszPath,
SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&h);
if (Status == STATUS_ACCESS_DENIED)
{
Status = OpenObject(ptszPath,
SYNCHRONIZE | FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&h);
fReadOnly = TRUE;
}
if (Status == STATUS_SUCCESS)
{
while (TRUE)
{
Status = RtlQueryObjectId(h, &oid);
DebugMsg(DM_TRACK,
TEXT("GetObjectInfo: ptszPath=%s, querying OBJECTID, Status=%08X"),
ptszPath, Status);
if (NT_SUCCESS(Status))
{
// first time - simple success - id exists
// retry - read the id just set by another process
*poid = oid;
break;
}
else
if (!fReadOnly && !fRetry && Status == STATUS_NOT_FOUND)
{
int cGen = 0;
do
{
(*g_pfnUuidCreate)(&oid.Lineage);
oid.Uniquifier = 0;
Status = RtlSetObjectId(h, &oid);
} while ( ++cGen < 10 &&
Status == STATUS_DUPLICATE_OBJECTID &&
(Sleep(5), TRUE) );
// at this point either we wrote it, or it exists
// (both forms of success) or it failed miserably
if (Status == STATUS_SUCCESS)
{
*poid = oid;
break;
}
else
if (Status == STATUS_OBJECTID_EXISTS)
{
// someone else did the set a moment ago
// we better read it
fRetry = TRUE;
continue;
}
else
{
break;
}
}
else
{
break;
}
}
NtClose(h);
}
DebugMsg(DM_TRACK,
TEXT("GetObjectInfo: ptszPath=%s, oid=%08X:%04X:%04X, Status=%08X"),
ptszPath,
poid->Lineage.Data1,
poid->Lineage.Data2,
poid->Lineage.Data3,
Status);
return(Status);
}
//+-------------------------------------------------------------------
//
// Function: IsLocal
//
// Synopsis: Returns TRUE if the passed root path refers to a
// local volume.
//
// Effects:
//
// Arguments: [pwszPath] -- path to volume
//
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 6-Aug-93 BillMo Created.
//
// Notes:
//
//--------------------------------------------------------------------
BOOL
IsLocal(const WCHAR *pwszPath)
{
UINT drivetype = 0;
drivetype = GetDriveType(pwszPath);
// BUGBUG: UNC path to local machine
return(drivetype == DRIVE_FIXED ||
drivetype == DRIVE_REMOVABLE ||
drivetype == DRIVE_CDROM ||
drivetype == DRIVE_RAMDISK);
}
//+-------------------------------------------------------------------------
//
// Function: GetRootLength, public
//
// Synopsis: Get the count of characters of root portion of
// name, including the final backslash.
//
// Arguments: [ptszPath] -- Pointer to the path. May be NULL.
//
// Returns: 0 if not valid UNC or valid drive letter root.
//
// Signals: Nothing.
//
// Modifies: Nothing.
//
// History: 17-Feb-93 BillMo Created.
//
// Notes: BUGBUG need proper character set for GetRootLength
//
//--------------------------------------------------------------------------
unsigned
GetRootLength(const TCHAR *ptszPath)
{
ULONG cwcRoot = 0;
if (ptszPath == 0)
ptszPath = TEXT("");
if (*ptszPath == TEXT('\\'))
{
// If the first character is a path separator (backslash), this
// must be a UNC drive designator which must be of the form:
// <path-separator><path-separator>(<alnum>+)
// <path-separator>(<alnum>+)<path-separator>
//
// This covers drives like these: \\worf\scratch\ and
// \\savik\win4dev\.
//
ptszPath++;
cwcRoot++;
BOOL fMachine = FALSE;
BOOL fShare = FALSE;
if (*ptszPath == TEXT('\\'))
{
cwcRoot++;
ptszPath++;
while (*ptszPath != '\0' && *ptszPath != TEXT('\\'))
{
cwcRoot++;
ptszPath++;
fMachine = TRUE;
}
if (*ptszPath == TEXT('\\'))
{
cwcRoot++;
ptszPath++;
while (*ptszPath != TEXT('\0') && *ptszPath != TEXT('\\'))
{
cwcRoot++;
ptszPath++;
fShare = TRUE;
}
// If there weren't any characters in the machine or
// share portions of the UNC name, then the drive
// designator is bogus.
//
if (!fMachine || !fShare)
{
cwcRoot = 0;
}
}
else
{
cwcRoot = 0;
}
}
else
{
cwcRoot = 0;
}
}
else
if (IsCharAlpha(*ptszPath))
{
// If the first character is an alphanumeric, we must have
// a drive designator of this form:
// (<alnum>)+<drive-separator><path-separator>
//
// This covers drives like these: a:\, c:\, etc
//
ptszPath++;
cwcRoot++;
if (*ptszPath == TEXT(':'))
{
cwcRoot++;
ptszPath++;
}
else
{
cwcRoot = 0;
}
}
// If we have counted one or more characters in the root and these
// are followed by a component separator, we need to add the separator
// to the root length. Otherwise this is not a valid root and we need
// to return a length of zero.
//
if ((cwcRoot > 0) && (*ptszPath == TEXT('\\')))
{
cwcRoot++;
}
else
{
cwcRoot = 0;
}
return (cwcRoot);
}
//+-------------------------------------------------------------------
//
// Function: MakeRoot
//
// Synopsis: Truncate the path to be just a volume entry path, e.g.
// A:\ or \\foo\bar\ .
//
// Effects:
//
// Arguments: [ptszPath] -- the path to truncate.
//
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 6-Aug-93 BillMo Created.
//
// Notes:
//
//--------------------------------------------------------------------
void MakeRoot(TCHAR *ptszPath)
{
unsigned rootlength = GetRootLength(ptszPath);
if (rootlength)
{
ptszPath[rootlength] = TEXT('\0');
}
}
//+-------------------------------------------------------------------
//
// Member: CTracker::InitFromPath
//
// Synopsis: Get tracking state from the given path.
//
// Arguments: [ptszPath] -- path of file to track
//
// Returns: S_OK
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes:
//
//--------------------------------------------------------------------
HRESULT
CTracker::InitFromPath( const TCHAR *ptszPath )
{
OBJECTID oidOld = _oid;
GetObjectInfo(ptszPath, &_oid);
_fDirty = memcmp(&oidOld, &_oid, SIZEOF(_oid)) != 0;
return(S_OK);
}
//+-------------------------------------------------------------------
//
// Member: CTracker::IsDirty
//
// Synopsis: Return TRUE if the tracker has been changed.
//
// Effects:
//
// Arguments:
//
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes:
//
//--------------------------------------------------------------------
BOOL
CTracker::IsDirty( VOID )
{
return(_fDirty);
}
//+-------------------------------------------------------------------
//
// Member: CTracker::InitFromPath
//
// Synopsis: Initialization for monikers to use.
//
// Effects:
//
// Arguments:
//
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes: Not used yet: may change when monikers implemented.
//
//--------------------------------------------------------------------
HRESULT
CTracker::InitFromPath( const TCHAR *ptszPath, DWORD dwCreationFlags )
{
if (dwCreationFlags & ~(TRACK_LOCALONLY | TRACK_INDEXEDONLY | TRACK_LASTONLY | TRACK_EXACTONLY))
{
return(E_INVALIDARG);
}
_dwCreationFlags = dwCreationFlags;
GetObjectInfo(ptszPath, &_oid);
return(S_OK);
}
//+-------------------------------------------------------------------
//
// Member: CTracker::Load
//
// Synopsis: Load the tracker from the memory buffer.
//
// Effects:
//
// Arguments: [pb] -- buffer to load from
// [cb] -- size from GetSize used when saving
//
//
// Returns: S_OK
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes:
//
//--------------------------------------------------------------------
HRESULT
CTracker::Load( BYTE *pb, ULONG cb )
{
_oid = *(OBJECTID *)pb; pb += SIZEOF(_oid);
_dwCreationFlags = *(DWORD*)pb; pb += SIZEOF(_dwCreationFlags);
_fDirty = FALSE;
return(S_OK);
}
//+-------------------------------------------------------------------
//
// Member: CTracker::GetSize
//
// Synopsis: Get size in bytes required to save tracker.
//
// Effects:
//
// Arguments:
//
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes:
//
//--------------------------------------------------------------------
ULONG
CTracker::uGetSize( VOID )
{
return( SIZEOF(_oid) + SIZEOF(_dwCreationFlags) );
}
//+-------------------------------------------------------------------
//
// Member: CTracker::Save
//
// Synopsis: Save tracker to the given buffer.
//
// Effects:
//
// Arguments: [pb] -- buffer for tracker. Must be at least as large
// as size specified by calling GetSize.
//
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes:
//
//--------------------------------------------------------------------
VOID
CTracker::Save( BYTE *pb )
{
*(OBJECTID*)pb = _oid; pb += SIZEOF(_oid);
*(DWORD*)pb = _dwCreationFlags; pb += SIZEOF(_dwCreationFlags);
}
//+-------------------------------------------------------------------
//
// Member: CTracker::Search
//
// Synopsis: Search for the object referred to by the tracker.
//
// Arguments: [dwRestriction] -- TRACK_* flags combined into the
// saved flags.
// [dwTickCountDeadline] -- absolute tick count for deadline
// [pcw] -- interface to call for cancel window
// [ptszHintedIn] -- old path to object
// [pfdIn] -- may be NULL
// must contain valid data if specified
// [pfdOut] -- may not be NULL
// will contain updated data on success
// [piScore] -- pointer to variable to receive score
// [pcw] -- ICancelWindow *
// If NULL, no UI thread is created
// This object will be deleted by the
// Search function or threads, do not use
// after success OR failure returns
// In other words, this pointer is ALWAYS
// deleted by this function.
//
// Returns: S_OK -- if found (pfd contains new info)
// S_FALSE -- searched for but not found
// E_OUTOFMEMORY -- if out of memory or buffer is too small
// MK_E_EXCEEDEDDEADLINE -- timeout
// E_ABORT -- user cancelled
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes:
//
//--------------------------------------------------------------------
HRESULT
CTracker::Search( DWORD dwRestriction,
DWORD dwTickCountDeadline,
const TCHAR *ptszHintedIn,
const WIN32_FIND_DATA *pfdIn,
WIN32_FIND_DATA *pfdOut,
int * piScore,
ICancelWindow *pcw)
{
DebugMsg(DM_TRACK, TEXT("In CTracker::Search(): ptszHintedIn=%s, _oid=%08X:%04X:%04X")
TEXT("pfdIn=%08X, pfdOut=%08X, pcw=%08X\n"),
ptszHintedIn,
_oid.Lineage.Data1,
_oid.Lineage.Data2,
_oid.Lineage.Data3,
pfdIn,
pfdOut,
pcw);
BOOL fHasObjectId = memcmp(&g_oidZero, &_oid, SIZEOF(_oid));
DWORD dwTrackFlags = dwRestriction | _dwCreationFlags;
HRESULT hr;
CTrackPool * pTrackPool = NULL;
CTrackCommand * pTrackCommand = NULL;
CCancelWindowCommand * pcwc = NULL;
*piScore = 0;
// CStringList volumelist; // later add a list so we don't search a volume twice
// will add check for customized tracking here
//
// this is a thread safe initialization of the global thread set ...
// it works by optimistically creating a CGlobalThreadSet if the g_pts
// pointer is NULL. If it turns out that the pointer was NULL but has
// now been assigned by another thread, then the critical section will
// pick up that the g_pts pointer is non-NULL and will delete the "extra"
// one created.
//
CGlobalThreadSet * pts = NULL;
hr = S_OK;
if (g_pts == NULL)
{
pts = new CGlobalThreadSet(&hr);
if (pts == NULL || hr != S_OK)
{
delete pts;
hr = E_OUTOFMEMORY;
goto Ret;
}
}
// the shell guys tell me I shouldn't do anything but assignments in
// critical sections implemented by ENTERCRITICAL so that is why
// I've done it this way
ENTERCRITICAL
if (g_pts == NULL)
{
g_pts = pts;
pts = NULL;
}
LEAVECRITICAL
delete pts;
//
// ... end thread safe initialization
//
hr = E_OUTOFMEMORY;
pTrackPool = new CTrackPool(MAX_TRACK_THREADS, *g_pts, dwTickCountDeadline, &_oid, dwTrackFlags, pfdIn, &hr);
if (hr != S_OK)
goto Ret;
Assert(hr == S_OK);
//
// Create a thread to handle the cancel dialog
//
if (pcw != NULL)
{
pTrackCommand = new CCancelWindowCommand(pTrackPool, ptszHintedIn, &pcw, &hr);
if (pTrackCommand == NULL || hr != S_OK)
goto Ret;
hr = pTrackPool->AssignCommandToThread(pTrackCommand, -1);
if (hr != S_OK)
goto Ret;
}
if (fHasObjectId && !(dwTrackFlags & TRACK_LASTONLY))
{
// step 8 -- for each entry in the searchlist of volumes, search by object id
pTrackCommand = new CRegistryListCommand(pTrackPool, TEXT("Manual"), &hr);
if (pTrackCommand == NULL || hr != S_OK)
goto Ret;
hr = pTrackPool->AssignCommandToThread(pTrackCommand, 1);
if (hr != S_OK)
goto Ret;
if (!(dwTrackFlags & TRACK_LOCALONLY))
{
pTrackCommand = new CMappedDrivesCommand(pTrackPool, NULL, &hr);
if (pTrackCommand == NULL || hr != S_OK)
goto Ret;
hr = pTrackPool->AssignCommandToThread(pTrackCommand, 1);
if (hr != S_OK)
goto Ret;
}
pTrackCommand = new COUListCommand(pTrackPool, ptszHintedIn, &hr);
if (pTrackCommand == NULL || hr != S_OK)
goto Ret;
hr = pTrackPool->AssignCommandToThread(pTrackCommand, 1);
if (hr != S_OK)
goto Ret;
}
if (pfdIn != NULL &&
!(dwTrackFlags & TRACK_INDEXEDONLY))
{
pTrackCommand = new CDownLevelCommand(pTrackPool, ptszHintedIn, &hr);
if (pTrackCommand == NULL || hr != S_OK)
goto Ret;
hr = pTrackPool->AssignCommandToThread(pTrackCommand, 1);
if (hr != S_OK)
goto Ret;
}
pTrackCommand = NULL;
pTrackPool->EnableSecondTier();
pTrackPool->WaitForSuccessOrCompletion();
pTrackPool->GetFindData(pfdOut, piScore);
Ret:
delete pTrackCommand;
delete pcw;
if (pTrackPool != NULL)
pTrackPool->Release();
DebugMsg(DM_TRACK, TEXT("Out CTracker::Search(): hr=%08X, ptszHintedIn=%s, _oid=%08X:%04X:%04X, pfdOut->cFileName=%s"),
hr,
ptszHintedIn,
_oid.Lineage.Data1,
_oid.Lineage.Data2,
_oid.Lineage.Data3,
pfdOut->cFileName);
return(hr);
}
//+-------------------------------------------------------------------
//
// Member: CTracker::SearchLocalVolumesById
//
// Synopsis: Search for the object referred to by the tracker on
// local indexed volumes.
//
// Effects:
//
// Arguments: [dwRestriction] -- TRACK_* flags combined into the
// saved flags.
// [pfd] -- buffer for updated WIN32_FIND_DATA
//
// Returns: S_OK -- if found and ptszFound contains path.
// S_FALSE -- searched for but not found OR
// the step was disabled by flags or available data.
// E_OUTOFMEMORY -- if out of memory or buffer is too small
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// Notes:
//
//--------------------------------------------------------------------
HRESULT
CTracker::SearchLocalVolumesById( DWORD dwRestriction, WIN32_FIND_DATA *pfd)
{
HRESULT hr = S_FALSE;
BOOL fHasObjectId = memcmp(&g_oidZero, &_oid, SIZEOF(_oid));
DWORD dwTrackFlags = dwRestriction | _dwCreationFlags;
TCHAR atcLocalDrives[4];
// step 4
if (fHasObjectId && !(dwTrackFlags & TRACK_LASTONLY))
{
for (lstrcpy(atcLocalDrives, TEXT("C:\\"));
atcLocalDrives[0] <= TEXT('Z');
atcLocalDrives[0] ++)
{
if (IsLocal(atcLocalDrives))
{
hr = SearchVolume(atcLocalDrives, &_oid, pfd);
if (hr == S_OK)
break;
}
}
}
DebugMsg(DM_TRACK,
TEXT("Out SearchLocalVolumesById( returns %08X\n"),
hr);
return(hr);
}
//+-------------------------------------------------------------------
//
// Member: CTracker::SearchHintedVolumeById
//
// Synopsis: Search for the object referred to by the tracker on
// the last known volume
//
// Effects:
//
// Arguments: [dwRestriction] -- TRACK_* flags combined into the
// saved flags.
// [ptszHintedIn] -- hinted path of object
// [pfd] -- buffer for updated WIN32_FIND_DATA
//
// Returns: S_OK -- if found and ptszFound contains path.
// S_FALSE -- searched for but not found OR
// the step was disabled by flags or available data.
// SearchVolume errors
//
// Notes:
//
//--------------------------------------------------------------------
HRESULT
CTracker::SearchHintedVolumeById( DWORD dwRestriction,
const TCHAR *ptszHintedIn,
WIN32_FIND_DATA *pfd)
{
HRESULT hr = S_FALSE;
BOOL fHasObjectId = memcmp(&g_oidZero, &_oid, SIZEOF(_oid));
DWORD dwTrackFlags = dwRestriction | _dwCreationFlags;
Assert(lstrlen(ptszHintedIn) <= MAX_PATH);
// step 4
if (fHasObjectId)
{
TCHAR tszRoot[MAX_PATH+1];
lstrcpy(tszRoot, ptszHintedIn);
MakeRoot(tszRoot);
if (!(dwTrackFlags & TRACK_LOCALONLY) || IsLocal(tszRoot))
hr = SearchVolume(tszRoot, &_oid, pfd);
}
DebugMsg(DM_TRACK,
TEXT("Out SearchHintedVolumeById( returns %08X\n"),
hr);
return(hr);
}
//+-------------------------------------------------------------------
//
// Function: FindInFolder2
//
// Synopsis: Glue logic to use CTracker, handles glueing UI feedback
// object to tracker object.
//
// Arguments: [hwnd] -- window to be owner of any dialogs
// [uFlags] -- SLR_* flags, e.g. SLR_NO_UI
// [pszPath] -- where to put the resultant path
// [pfd] -- on input, the WIN32_FIND_DATA for the create-time
// search
// on output, the WIN32_FIND_DATA for the new found
// file
// [dwRestriction] -- TRACK_* flags
// [dwTickCountDeadline] -- deadline as per OLE spec
// [pTracker] -- the CTracker object that should do the
// tracking
//
// Returns: IDOK if object found
// IDNO if no object found
// IDCANCEL user cancelled
//
// Notes:
//
//--------------------------------------------------------------------
extern "C"
int FindInFolder2(HWND hwnd, UINT uFlags, LPCTSTR pszPath, WIN32_FIND_DATA *pfd,
DWORD dwRestriction, DWORD dwTickCountDeadline,
struct CTracker *pTracker )
{
int ret=IDOK;
int iScore;
DebugMsg(DM_TRACK, TEXT("In FindInFolder: hwnd=%08X, ")
TEXT("uFlags=%08X, pszPath=%s, pfd=%08X, dwRestriction=%08X, ")
TEXT("dwTickCountDeadline=%08X, pTracker=%08X\n"),
hwnd, uFlags, pszPath, pfd, dwRestriction, dwTickCountDeadline,
pTracker);
if (lstrlen(pszPath) > MAX_PATH)
{
ret=IDNO;
goto Ret;
}
if (pTracker->SearchHintedVolumeById(dwRestriction, pszPath, pfd) == S_FALSE)
{
if (pTracker->SearchLocalVolumesById(dwRestriction, pfd) == S_FALSE)
{
ICancelWindow *pcw;
HRESULT hr;
if (uFlags & SLR_NO_UI)
{
hr = S_OK;
pcw = NULL;
}
else
{
hr = E_OUTOFMEMORY;
pcw = new CCancelWindow (hwnd, &hr);
}
if (hr == S_OK)
{
if (dwTickCountDeadline == 0)
{
dwTickCountDeadline = GetTickCount() + GetTimeOut(uFlags);
}
// else if specified in bind context, then use it
pTracker->Search(dwRestriction,
dwTickCountDeadline,
pszPath,
pfd,
pfd,
&iScore,
pcw);
if (iScore < MIN_NO_UI_SCORE)
{
if (iScore == 0 || uFlags & SLR_NO_UI)
{
ret=IDNO;
}
else
{
if (ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_LINKCHANGED), MAKEINTRESOURCE(IDS_LINKERROR),
MB_YESNO | MB_ICONEXCLAMATION, PathFindFileName(pszPath), pfd->cFileName) != IDYES)
ret=IDCANCEL;
}
}
else
if (iScore == SCORE_USER_CANCELLED)
{
ret=IDCANCEL;
}
}
else
{
delete pcw;
ret=IDCANCEL;
}
}
}
Ret:
TCHAR *ptszFile;
TCHAR *ptszDebug;
#if DBG
switch (ret)
{
case IDOK:
ptszFile = pfd->cFileName;
ptszDebug = TEXT("IDOK");
break;
case IDCANCEL:
ptszFile = TEXT("");
ptszDebug = TEXT("IDCANCEL");
break;
case IDNO:
ptszFile = TEXT("");
ptszDebug = TEXT("IDNO");
break;
}
#endif
DebugMsg(DM_TRACK,
TEXT("Out FindInFolder: iScore=%d, ret=%s, cFileName=%s\n"),
iScore,
ptszDebug,
ptszFile);
return ret;
}
#endif