1255 lines
35 KiB
C++
1255 lines
35 KiB
C++
|
|
// Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// File: common.cxx
|
|
//
|
|
// Contents: Code common to Tracking (Workstation) Service and
|
|
// Tracking (Server) Service.
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
//
|
|
//
|
|
// History: 18-Nov-96 BillMo Created.
|
|
//
|
|
// Notes:
|
|
//
|
|
// Codework:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include "trklib.hxx"
|
|
#include "ntlsa.h" // LsaGetUserName
|
|
|
|
#if MAX_COMPUTERNAME_LENGTH != 15
|
|
#error MAX_COMPUTERNAME_LENGTH assumed to be 15
|
|
#endif
|
|
|
|
//DWORD g_ftIndex;
|
|
|
|
|
|
const TCHAR s_HexGuidString[] = TEXT("%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X");
|
|
#define CCH_HEXGUID_STRING (8+4+4+2*8)
|
|
|
|
EXTERN_C const CLSID CLSID_TrackFile = {0x8790c947, 0xa30b, 0x11d0, {0x8c, 0xab, 0x00, 0xc0, 0x4f, 0xd9, 0x0f, 0x85} };
|
|
|
|
|
|
|
|
#if DBG
|
|
#define TRK_E_ERROR_MAP(tr,hr) { tr, hr, TEXT(#tr) }
|
|
#else
|
|
#define TRK_E_ERROR_MAP(tr,hr) { tr, hr }
|
|
#endif
|
|
|
|
// Map of TRK_E_ type error codes to HRESULTs, and in the debug build to strings.
|
|
|
|
const TrkEMap g_TrkEMap[] =
|
|
{
|
|
TRK_E_ERROR_MAP(TRK_S_OUT_OF_SYNC, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ),
|
|
TRK_E_ERROR_MAP(TRK_E_CORRUPT_LOG, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ),
|
|
TRK_E_ERROR_MAP(TRK_E_TIMER_REGISTRY_CORRUPT, HRESULT_FROM_WIN32(ERROR_REGISTRY_CORRUPT) ),
|
|
TRK_E_ERROR_MAP(TRK_E_REGISTRY_REFRESH_CORRUPT, HRESULT_FROM_WIN32(ERROR_REGISTRY_CORRUPT) ),
|
|
TRK_E_ERROR_MAP(TRK_E_CORRUPT_IDT, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ),
|
|
TRK_E_ERROR_MAP(TRK_E_DB_CONNECT_ERROR, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ),
|
|
TRK_E_ERROR_MAP(TRK_E_DN_TOO_LONG, CO_E_PATHTOOLONG ),
|
|
TRK_E_ERROR_MAP(TRK_E_DOMAIN_COMPUTER_NAMES_TOO_LONG, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ),
|
|
TRK_E_ERROR_MAP(TRK_E_BAD_USERNAME_NO_SLASH, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ),
|
|
TRK_E_ERROR_MAP(TRK_E_UNKNOWN_SID, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ),
|
|
TRK_E_ERROR_MAP(TRK_E_IMPERSONATED_COMPUTERNAME_TOO_LONG, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ),
|
|
TRK_E_ERROR_MAP(TRK_E_UNKNOWN_SVR_MESSAGE_TYPE, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ),
|
|
TRK_E_ERROR_MAP(TRK_E_FAIL_TEST, E_FAIL ),
|
|
TRK_E_ERROR_MAP(TRK_E_DENIAL_OF_SERVICE_ATTACK, HRESULT_FROM_WIN32(ERROR_RETRY) ),
|
|
TRK_E_ERROR_MAP(TRK_E_SERVICE_NOT_RUNNING, HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) ),
|
|
TRK_E_ERROR_MAP(TRK_E_TOO_MANY_UNSHORTENED_NOTIFICATIONS, HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) ),
|
|
TRK_E_ERROR_MAP(TRK_E_CORRUPT_CLNTSYNC, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ),
|
|
TRK_E_ERROR_MAP(TRK_E_COMPUTER_NAME_TOO_LONG, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ),
|
|
TRK_E_ERROR_MAP(TRK_E_SERVICE_STOPPING, HRESULT_FROM_WIN32(ERROR_NO_TRACKING_SERVICE) ),
|
|
TRK_E_ERROR_MAP(TRK_E_BIRTHIDS_DONT_MATCH, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ),
|
|
TRK_E_ERROR_MAP(TRK_E_CORRUPT_VOLTAB, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ),
|
|
TRK_E_ERROR_MAP(TRK_E_INTERNAL_ERROR, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ),
|
|
TRK_E_ERROR_MAP(TRK_E_PATH_TOO_LONG, CO_E_PATHTOOLONG ),
|
|
TRK_E_ERROR_MAP(TRK_E_GET_MACHINE_NAME_FAIL, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ),
|
|
TRK_E_ERROR_MAP(TRK_E_SET_VOLUME_STATE_FAIL, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ),
|
|
TRK_E_ERROR_MAP(TRK_E_VOLUME_ACCESS_DENIED, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ),
|
|
TRK_E_ERROR_MAP(TRK_S_VOLUME_NOT_FOUND, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) ),
|
|
TRK_E_ERROR_MAP(TRK_S_VOLUME_NOT_OWNED, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ),
|
|
TRK_E_ERROR_MAP(TRK_E_REFERRAL, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ),
|
|
TRK_E_ERROR_MAP(TRK_E_NOT_FOUND, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ),
|
|
TRK_E_ERROR_MAP(TRK_E_UNAVAILABLE, HRESULT_FROM_WIN32(ERROR_CONNECTION_UNAVAIL) ),
|
|
TRK_E_ERROR_MAP(TRK_E_TIMEOUT, HRESULT_FROM_WIN32(ERROR_SERVICE_REQUEST_TIMEOUT) ),
|
|
TRK_E_ERROR_MAP(TRK_E_VOLUME_QUOTA_EXCEEDED, HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_QUOTA) ),
|
|
TRK_E_ERROR_MAP(TRK_S_NOTIFICATION_QUOTA_EXCEEDED, HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_QUOTA) ),
|
|
TRK_E_ERROR_MAP(TRK_E_SERVER_TOO_BUSY, HRESULT_FROM_WIN32(RPC_S_SERVER_TOO_BUSY) ),
|
|
TRK_E_ERROR_MAP(TRK_E_INVALID_VOLUME_ID, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ),
|
|
TRK_E_ERROR_MAP(TRK_E_POTENTIAL_FILE_FOUND, HRESULT_FROM_WIN32(ERROR_POTENTIAL_FILE_FOUND) ),
|
|
TRK_E_ERROR_MAP(TRK_E_NULL_COMPUTERNAME, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ),
|
|
TRK_E_ERROR_MAP(TRK_E_NOT_FOUND_AND_LAST_VOLUME_NOT_FOUND, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ),
|
|
TRK_E_ERROR_MAP(TRK_E_NOT_FOUND_BUT_LAST_VOLUME_FOUND, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) )
|
|
};
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: GetErrorString
|
|
//
|
|
// Synopsis: Map an TRK_E_ error to a descriptive string.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
#if DBG
|
|
const TCHAR * GetErrorString(HRESULT hr)
|
|
{
|
|
ULONG iError;
|
|
|
|
if( S_OK == hr )
|
|
return( TEXT("S_OK") );
|
|
|
|
for( iError = 0; iError < sizeof(g_TrkEMap)/sizeof(*g_TrkEMap); iError++ )
|
|
{
|
|
if( g_TrkEMap[iError].tr == hr )
|
|
return( g_TrkEMap[iError].ptszDescription );
|
|
}
|
|
|
|
return( TEXT("Not a TRK_E_ error") );
|
|
}
|
|
#endif
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: MapTR2HR
|
|
//
|
|
// Synopsis: Map a TRK_E_ type error code to an HRESULT. If the input is
|
|
// already a non-trk HRESULT, the input will be returned unchanged.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT MapTR2HR( HRESULT tr )
|
|
{
|
|
ULONG iError;
|
|
HRESULT hr = tr;
|
|
|
|
if( S_OK == hr )
|
|
return( hr );
|
|
|
|
// Convert TRK_E_ error codes into an HRESULT
|
|
|
|
for( iError = 0; iError < sizeof(g_TrkEMap)/sizeof(*g_TrkEMap); iError++ )
|
|
{
|
|
if( g_TrkEMap[iError].tr == hr )
|
|
{
|
|
hr = g_TrkEMap[iError].hr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If this HRESULT is actually an NTSTATUS, then convert it to a Win32
|
|
// error, then back to an HRESULT.
|
|
|
|
if( FACILITY_NT_BIT & hr )
|
|
{
|
|
if( STATUS_VOLUME_NOT_UPGRADED == hr )
|
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
|
else
|
|
hr = HRESULT_FROM_WIN32( RtlNtStatusToDosError(hr & ~FACILITY_NT_BIT) );
|
|
}
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// HexStringizeGuid
|
|
//
|
|
// Optimized conversion from a GUID to a string.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
inline void
|
|
HexStringizeByte( BYTE b, TCHAR* &rptsz )
|
|
{
|
|
static const TCHAR _tszLookup[] = { TEXT("0123456789ABCDEF") };
|
|
|
|
*rptsz++ = _tszLookup[ b >> 4 ];
|
|
*rptsz++ = _tszLookup[ b & 0xF ];
|
|
}
|
|
|
|
|
|
void
|
|
HexStringizeGuid(const GUID &g, TCHAR * & rptsz)
|
|
{
|
|
|
|
HexStringizeByte( HIGH_BYTE(HIGH_WORD(g.Data1)), rptsz );
|
|
HexStringizeByte( LO_BYTE(HIGH_WORD(g.Data1)), rptsz );
|
|
HexStringizeByte( HIGH_BYTE(LO_WORD(g.Data1)), rptsz );
|
|
HexStringizeByte( LO_BYTE(LO_WORD(g.Data1)), rptsz );
|
|
|
|
HexStringizeByte( HIGH_BYTE(g.Data2), rptsz );
|
|
HexStringizeByte( LO_BYTE(g.Data2), rptsz );
|
|
|
|
HexStringizeByte( HIGH_BYTE(g.Data3), rptsz );
|
|
HexStringizeByte( LO_BYTE(g.Data3), rptsz );
|
|
|
|
for( int i = 0; i < sizeof(g.Data4); i++ )
|
|
HexStringizeByte( g.Data4[i], rptsz );
|
|
|
|
*rptsz = TEXT('\0');
|
|
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// HexUnstringizeGuid
|
|
//
|
|
// Convert a string to a GUID. This is not used as often as HexStringizeGuid,
|
|
// so it uses the CRT.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
HexUnstringizeGuid(const TCHAR * &ptsz, GUID * pg)
|
|
{
|
|
DWORD Data1;
|
|
DWORD Data2;
|
|
DWORD Data3;
|
|
DWORD Data40;
|
|
DWORD Data41;
|
|
DWORD Data42;
|
|
DWORD Data43;
|
|
DWORD Data44;
|
|
DWORD Data45;
|
|
DWORD Data46;
|
|
DWORD Data47;
|
|
|
|
if( 11 != _stscanf( ptsz,
|
|
s_HexGuidString,
|
|
&Data1,
|
|
&Data2,
|
|
&Data3,
|
|
&Data40,
|
|
&Data41,
|
|
&Data42,
|
|
&Data43,
|
|
&Data44,
|
|
&Data45,
|
|
&Data46,
|
|
&Data47))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pg->Data1 = Data1;
|
|
pg->Data2 = (WORD)Data2;
|
|
pg->Data3 = (WORD)Data3;
|
|
pg->Data4[0] = (BYTE)Data40;
|
|
pg->Data4[1] = (BYTE)Data41;
|
|
pg->Data4[2] = (BYTE)Data42;
|
|
pg->Data4[3] = (BYTE)Data43;
|
|
pg->Data4[4] = (BYTE)Data44;
|
|
pg->Data4[5] = (BYTE)Data45;
|
|
pg->Data4[6] = (BYTE)Data46;
|
|
pg->Data4[7] = (BYTE)Data47;
|
|
|
|
ptsz += CCH_HEXGUID_STRING;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
TCHAR *
|
|
wcstotcs(TCHAR *ptszBuf, const WCHAR *pwsz)
|
|
{
|
|
#ifdef UNICODE
|
|
wcscpy(ptszBuf, pwsz);
|
|
#else
|
|
wcstombs(ptszBuf, pwsz, (wcslen(pwsz)+1)*sizeof(WCHAR));
|
|
#endif
|
|
return(ptszBuf);
|
|
}
|
|
|
|
CHAR *
|
|
tcstombs(CHAR *pszBuf, const TCHAR *ptsz)
|
|
{
|
|
#ifdef UNICODE
|
|
wcstombs(pszBuf, ptsz, (_tcslen(ptsz)+1)*sizeof(CHAR));
|
|
#else
|
|
strcpy(pszBuf, ptsz);
|
|
#endif
|
|
return(pszBuf);
|
|
}
|
|
|
|
WCHAR *
|
|
tcstowcs(WCHAR *pwszBuf, const TCHAR *ptsz)
|
|
{
|
|
#ifdef UNICODE
|
|
wcscpy(pwszBuf, ptsz);
|
|
#else
|
|
mbstowcs(pwszBuf, ptsz, (_tcslen(ptsz)+1)*sizeof(WCHAR));
|
|
#endif
|
|
return(pwszBuf);
|
|
}
|
|
|
|
TCHAR *
|
|
mbstotcs(TCHAR *ptszBuf, const CHAR *psz)
|
|
{
|
|
#ifdef UNICODE
|
|
mbstowcs(ptszBuf, psz, (strlen(psz)+1)*sizeof(WCHAR));
|
|
#else
|
|
_tcscpy(ptszBuf, psz);
|
|
#endif
|
|
return(ptszBuf);
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
TrkTimeUnits(const SYSTEMTIME &st)
|
|
{
|
|
CFILETIME cft( st );
|
|
|
|
// 2**32 * 100e-9 = 429.4967296 seconds
|
|
//
|
|
// 32bit int can last 1844674407371 seconds = 58494.24173551 years
|
|
|
|
return( cft.HighDateTime() );
|
|
}
|
|
|
|
DWORD
|
|
TrkTimeUnits(const CFILETIME &cft)
|
|
{
|
|
// 2**32 * 100e-9 = 429.4967296 seconds
|
|
//
|
|
// 32bit int can last 1844674407371 seconds = 58494.24173551 years
|
|
|
|
return( cft.HighDateTime() );
|
|
}
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
void
|
|
CMachineId::AssertValid()
|
|
{
|
|
}
|
|
|
|
#endif // #if DBG
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CMachineId::CMachineId( ptsz )
|
|
//
|
|
// Instantiate a mcid from a computer name, e.g. "mymachine".
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
CMachineId::CMachineId(const TCHAR * ptszPath)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
int nReturn;
|
|
|
|
// Zero everything out first
|
|
new(this) CMachineId;
|
|
|
|
// Ensure that this isn't a real path, it should
|
|
// have been pre-processed already.
|
|
|
|
TrkAssert( _tcslen(ptszPath) < 2
|
|
||
|
|
( TEXT('\\') != ptszPath[0]
|
|
&&
|
|
TEXT(':') != ptszPath[1]
|
|
)
|
|
);
|
|
|
|
// Ensure that it's not too long.
|
|
if (_tcslen(ptszPath) > MAX_COMPUTERNAME_LENGTH )
|
|
TrkRaiseException( TRK_E_COMPUTER_NAME_TOO_LONG );
|
|
|
|
#ifndef _UNICODE
|
|
#error Ansi build not supported.
|
|
#endif
|
|
|
|
// Convert the Unicode machine name into Ansi, using
|
|
// the OEM code page (the netbios/computer name is always
|
|
// OEM code page, not the Windows Ansi code page).
|
|
|
|
nReturn = WideCharToMultiByte( CP_OEMCP, 0,
|
|
ptszPath, -1,
|
|
_szMachine, sizeof(_szMachine),
|
|
NULL, NULL );
|
|
|
|
if( 0 == nReturn )
|
|
TrkRaiseLastError();
|
|
|
|
|
|
TrkLog(( TRKDBG_WARNING, TEXT("Machine name is: %hs (from %s)"),
|
|
_szMachine, ptszPath ));
|
|
|
|
Normalize(); // Guarantee a terminator
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CMachineId::CMachineId( type )
|
|
//
|
|
// Initialize an mcid. The type indicates if the mcid should be for the
|
|
// local computer, a DC (possibly doing a rediscovery), or invalid.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
CMachineId::CMachineId(MCID_CREATE_TYPE type)
|
|
{
|
|
DWORD dwSize = MAX_COMPUTERNAME_LENGTH+1;
|
|
CHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1];
|
|
int nReturn;
|
|
|
|
TrkAssert(type == MCID_LOCAL ||
|
|
type == MCID_INVALID ||
|
|
type == MCID_DOMAIN ||
|
|
type == MCID_DOMAIN_REDISCOVERY ||
|
|
type == MCID_PDC_REQUIRED);
|
|
|
|
// Basic initialization
|
|
new(this) CMachineId;
|
|
|
|
if (type == MCID_INVALID)
|
|
goto Exit;
|
|
|
|
switch (type)
|
|
{
|
|
case MCID_LOCAL:
|
|
{
|
|
WCHAR wszComputerName[ MAX_COMPUTERNAME_LENGTH + 1 ];
|
|
|
|
// Create an MCID of the local machine
|
|
// We can't call GetComputerNameA, because it uses
|
|
// RtlUnicodeStringToAnsiString and consequently returns
|
|
// a Windows/Ansi string, when it should be returning an
|
|
// OEM string.
|
|
|
|
if (!GetComputerNameW(wszComputerName, &dwSize))
|
|
TrkRaiseException(HRESULT_FROM_WIN32(GetLastError()));
|
|
|
|
nReturn = WideCharToMultiByte( CP_OEMCP, 0,
|
|
wszComputerName, -1,
|
|
szComputerName, sizeof(szComputerName),
|
|
NULL, NULL );
|
|
if( 0 == nReturn )
|
|
TrkRaiseLastError();
|
|
}
|
|
|
|
break;
|
|
|
|
case MCID_DOMAIN:
|
|
case MCID_DOMAIN_REDISCOVERY:
|
|
case MCID_PDC_REQUIRED:
|
|
|
|
// Create an MCID for a DC
|
|
|
|
DWORD dwErr;
|
|
PDOMAIN_CONTROLLER_INFOW pdci;
|
|
|
|
// DsGetDcName gets us the appropriate DC computer name
|
|
// (of the form \\machine). We call the W version
|
|
// because the A version returns an Ansi string,
|
|
// rather than an OEM string.
|
|
|
|
dwErr = DsGetDcNameW(NULL, NULL, NULL, NULL,
|
|
DS_RETURN_FLAT_NAME |
|
|
DS_BACKGROUND_ONLY |
|
|
DS_DIRECTORY_SERVICE_REQUIRED |
|
|
DS_WRITABLE_REQUIRED |
|
|
(type == MCID_DOMAIN_REDISCOVERY ?
|
|
DS_FORCE_REDISCOVERY : 0 ) |
|
|
(type == MCID_PDC_REQUIRED ?
|
|
DS_PDC_REQUIRED : 0 ),
|
|
&pdci);
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
TrkRaiseWin32Error(dwErr);
|
|
}
|
|
|
|
// Validate the returned name.
|
|
|
|
TrkAssert(pdci->DomainControllerName &&
|
|
pdci->DomainControllerName[0] == L'\\' &&
|
|
pdci->DomainControllerName[1] == L'\\');
|
|
|
|
dwSize = wcslen(pdci->DomainControllerName + 2);
|
|
if ( dwSize + 1 > sizeof(_szMachine))
|
|
{
|
|
NetApiBufferFree(pdci);
|
|
TrkRaiseException(HRESULT_FROM_WIN32(ERROR_INVALID_NAME));
|
|
}
|
|
|
|
// Keep the returned name.
|
|
|
|
nReturn = WideCharToMultiByte( CP_OEMCP, 0,
|
|
&pdci->DomainControllerName[2], -1,
|
|
_szMachine, sizeof(_szMachine),
|
|
NULL, NULL );
|
|
if( 0 == nReturn )
|
|
TrkRaiseLastError();
|
|
|
|
NetApiBufferFree(pdci);
|
|
|
|
goto Exit;
|
|
} // switch
|
|
|
|
if (dwSize + 1 <= sizeof(_szMachine))
|
|
{
|
|
strcpy(_szMachine, szComputerName);
|
|
Normalize();
|
|
}
|
|
else
|
|
{
|
|
TrkRaiseException(HRESULT_FROM_WIN32(ERROR_INVALID_NAME));
|
|
}
|
|
|
|
Exit:
|
|
|
|
Normalize(); // Guarantee a terminator
|
|
return;
|
|
}
|
|
|
|
|
|
#ifndef UNICODE
|
|
extern "C"
|
|
NET_API_STATUS
|
|
NetpGetDomainName (
|
|
IN LPWSTR *ComputerNamePtr);
|
|
#endif
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CMachineId::GetLocalAuthName
|
|
//
|
|
// Returns the authentication name for use in secure RPC (to be used in
|
|
// the RpcBindingSetAuthInfo on the server). The name
|
|
// is of the form DOMAIN\MACHINE$, where DOMAIN is the local domain
|
|
// and MACHINE is the contents of this CMachineId
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CMachineId::GetLocalAuthName(RPC_TCHAR * ptszAuthName, DWORD cchBuf) const
|
|
{
|
|
// To get domain name: if you link to netlib.lib, you can call NetpGetDomainName,
|
|
// which does all the work for you. Or you could copy the code
|
|
// from \nt\private\net\netlib\domname.c
|
|
|
|
NET_API_STATUS Status;
|
|
WCHAR * pwszDomain;
|
|
DWORD dwErr;
|
|
PDOMAIN_CONTROLLER_INFOA pdci = NULL;
|
|
|
|
__try
|
|
{
|
|
// Get the domain name ...
|
|
|
|
Status = NetpGetDomainName(&pwszDomain);
|
|
if (Status != NO_ERROR)
|
|
{
|
|
pwszDomain = NULL;
|
|
TrkRaiseWin32Error(Status);
|
|
}
|
|
|
|
// and validate it.
|
|
|
|
if (cchBuf < wcslen(pwszDomain) + 1 + strlen(_szMachine) + 1 + 1)
|
|
{
|
|
TrkRaiseException(TRK_E_DOMAIN_COMPUTER_NAMES_TOO_LONG);
|
|
}
|
|
|
|
// Copy the domain name then the machine name into the return buffer.
|
|
|
|
wcstotcs((TCHAR*)ptszAuthName, pwszDomain);
|
|
_tcscat((TCHAR*)ptszAuthName, TEXT("\\"));
|
|
mbstotcs(_tcschr((TCHAR*)ptszAuthName, 0), _szMachine);
|
|
_tcscat((TCHAR*)ptszAuthName, TEXT("$"));
|
|
|
|
}
|
|
__finally
|
|
{
|
|
if (pwszDomain != NULL)
|
|
{
|
|
NetApiBufferFree(pwszDomain);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// GetFileTimeNow
|
|
//
|
|
// Get the current FILETIME (UTC).
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
FILETIME GetFileTimeNow()
|
|
{
|
|
SYSTEMTIME st;
|
|
FILETIME ft;
|
|
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, &ft);
|
|
|
|
return(ft);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CDomainRelativeObjId::Stringize
|
|
//
|
|
// Stringize a droid.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
TCHAR *
|
|
CDomainRelativeObjId::Stringize( TCHAR * ptszOutBuf, DWORD cchBuf ) const
|
|
{
|
|
TCHAR *ptszBuf = ptszOutBuf;
|
|
_volume.Stringize(ptszBuf); /*in, out, c++ reference*/
|
|
_object.Stringize(ptszBuf); /*in, out, c++ reference*/
|
|
|
|
TrkAssert(_tcslen(ptszOutBuf) + 1 < cchBuf);
|
|
|
|
return(ptszOutBuf);
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CTrkRegistryKey::Delete
|
|
//
|
|
// Common code to delete a registry key, relative to _hkey.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
LONG
|
|
CTrkRegistryKey::Delete( const TCHAR *ptszName )
|
|
{
|
|
LONG lRet = 0;
|
|
_cs.Enter();
|
|
__try
|
|
{
|
|
// Open _hkey if it's not already.
|
|
|
|
lRet = Open();
|
|
|
|
// And delete the value
|
|
|
|
if( ERROR_SUCCESS == lRet )
|
|
{
|
|
RegDeleteValue( _hkey, ptszName );
|
|
Close();
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
_cs.Leave();
|
|
}
|
|
|
|
return( lRet );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CTrkRegistryKey::SetDword
|
|
//
|
|
// Set a REG_DWORD value under _hkey.
|
|
//
|
|
//+---------------------------------------------------------------------------0
|
|
|
|
LONG
|
|
CTrkRegistryKey::SetDword( const TCHAR *ptszName, DWORD dw )
|
|
{
|
|
LONG lRet = 0;
|
|
|
|
_cs.Enter();
|
|
__try
|
|
{
|
|
// Open _hkey if it's not already
|
|
|
|
lRet = Open();
|
|
|
|
// And set the value
|
|
|
|
if ( ERROR_SUCCESS == lRet )
|
|
{
|
|
lRet = RegSetValueEx( _hkey,
|
|
ptszName,
|
|
0,
|
|
REG_DWORD,
|
|
reinterpret_cast<CONST BYTE *>(&dw),
|
|
sizeof(dw) );
|
|
if( ERROR_SUCCESS != lRet )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't set registry value for %s (%lu)"),
|
|
ptszName, lRet ));
|
|
RegDeleteKey( _hkey, ptszName );
|
|
}
|
|
|
|
Close();
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
_cs.Leave();
|
|
}
|
|
|
|
return( lRet );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CTrkRegistryKey::GetDword
|
|
//
|
|
// Get a REG_DWORD value from _hkey.
|
|
//
|
|
//+---------------------------------------------------------------------------0
|
|
|
|
LONG
|
|
CTrkRegistryKey::GetDword( const TCHAR *ptszName, DWORD *pdwRead, DWORD dwDefault )
|
|
{
|
|
LONG lRet;
|
|
DWORD dwRead;
|
|
DWORD cbData = sizeof(*pdwRead);
|
|
DWORD dwType;
|
|
|
|
*pdwRead = dwDefault;
|
|
|
|
_cs.Enter();
|
|
__try
|
|
{
|
|
// Open _hkey if it's not already.
|
|
|
|
lRet = Open();
|
|
if( ERROR_SUCCESS != lRet )
|
|
__leave;
|
|
|
|
|
|
// Get the DWORD
|
|
|
|
lRet = RegQueryValueEx( _hkey, ptszName, 0, &dwType,
|
|
reinterpret_cast<BYTE*>(&dwRead), &cbData );
|
|
|
|
if( ERROR_SUCCESS == lRet )
|
|
{
|
|
// Validate the type
|
|
|
|
if( REG_DWORD != dwType || sizeof(dwRead) != cbData )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Wrong type/size (%d/%d) for registry value %s"),
|
|
dwType, cbData, ptszName ));
|
|
RegDeleteKey( _hkey, ptszName );
|
|
}
|
|
else
|
|
{
|
|
*pdwRead = dwRead;
|
|
}
|
|
}
|
|
else if( ERROR_FILE_NOT_FOUND == lRet
|
|
||
|
|
ERROR_PATH_NOT_FOUND == lRet )
|
|
{
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
TrkLog(( TRKDBG_ERROR,
|
|
TEXT("Couldn't read %s from registry (%lu)"), ptszName, lRet ));
|
|
RegDeleteKey( _hkey, ptszName );
|
|
__leave;
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
Close();
|
|
_cs.Leave();
|
|
}
|
|
|
|
|
|
return( lRet );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// ThreadPoolCallbackFunction
|
|
//
|
|
// This function is passed as the callback function to
|
|
// RegisterWaitForSingleObjectEx. The context is a PWorkItem pointer.
|
|
//
|
|
// Arguments:
|
|
// [pvWorkItem]
|
|
// The Context parameter from RegisterWaitForSingleObjectEx.
|
|
// Is a PWorkItem*
|
|
// [fTimeout]
|
|
// We always register INFINITE as the timeout, so this value
|
|
// should always be FALSE.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
VOID NTAPI
|
|
ThreadPoolCallbackFunction( PVOID pvWorkItem, BOOLEAN fTimeout )
|
|
{
|
|
SThreadFromPoolState state;
|
|
PWorkItem *pWorkItem = reinterpret_cast<PWorkItem*>(pvWorkItem);
|
|
|
|
TrkLog(( TRKDBG_WORKMAN, TEXT("Enter ThreadPoolCallbackFunction for %s (%p/%p)"),
|
|
pWorkItem->_tszWorkItemSig, pWorkItem, *reinterpret_cast<UINT_PTR*>(pWorkItem) ));
|
|
|
|
TrkAssert( FALSE == fTimeout );
|
|
|
|
// Make sure we never raise back into the thread pool.
|
|
|
|
__try
|
|
{
|
|
// Update thread-count stats. Note that this isn't
|
|
// thread-safe, but for private statistics it's not worth
|
|
// creating a critsec.
|
|
|
|
InterlockedIncrement( reinterpret_cast<LONG*>(&g_cThreadPoolThreads) );
|
|
if( g_cThreadPoolThreads > g_cThreadPoolMaxThreads )
|
|
g_cThreadPoolMaxThreads = g_cThreadPoolThreads;
|
|
|
|
// Set our necessary thread-specific settings
|
|
state = InitializeThreadFromPool();
|
|
|
|
// Process the signal
|
|
pWorkItem->DoWork();
|
|
}
|
|
__except( BREAK_THEN_RETURN( EXCEPTION_EXECUTE_HANDLER ))
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Unexpected exception on thread pool callback (%08x)"),
|
|
GetExceptionCode() ));
|
|
}
|
|
|
|
TrkLog(( TRKDBG_WORKMAN, TEXT("Exit ThreadPoolCallbackFunction for %s (%p)"),
|
|
pWorkItem->_tszWorkItemSig, pWorkItem ));
|
|
|
|
InterlockedDecrement( reinterpret_cast<LONG*>(&g_cThreadPoolThreads) );
|
|
|
|
//IFDBG( TrkRtlCheckForOrphanedCriticalSections( GetCurrentThread() ));
|
|
|
|
// Restore the original thread-specific settings
|
|
UnInitializeThreadFromPool( state );
|
|
}
|
|
|
|
// The work item callback function (used for RtlQueueWorkItem)
|
|
// just calls to the function above.
|
|
|
|
VOID NTAPI
|
|
ThreadPoolWorkItemFunction( PVOID pvWorkItem )
|
|
{
|
|
ThreadPoolCallbackFunction( pvWorkItem, FALSE );
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// RunningAsAdministratorHack
|
|
//
|
|
// This routine is only used by test/debug hooks. It checks to see if
|
|
// the thread is running as an administrative user by seeing if we can
|
|
// get write access to the service's parameters key in the registry.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
RunningAsAdministratorHack()
|
|
{
|
|
LONG lResult = 0;
|
|
HKEY hkey = NULL;
|
|
BOOL fReturn = FALSE;
|
|
|
|
lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
s_tszKeyNameLinkTrack,
|
|
0, // Options, reserved must be zero
|
|
KEY_ALL_ACCESS,
|
|
&hkey );
|
|
if( ERROR_SUCCESS == lResult )
|
|
{
|
|
fReturn = TRUE;
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
return( fReturn );
|
|
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// EnablePrivilege
|
|
//
|
|
// Enable the specified privielge in the current access token if
|
|
// it is available.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
EnablePrivilege( const TCHAR *ptszPrivilegeName )
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
HANDLE hToken = INVALID_HANDLE_VALUE;
|
|
LUID luid;
|
|
TOKEN_PRIVILEGES token_privileges;
|
|
|
|
// Get the process token.
|
|
|
|
if( !OpenProcessToken( GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&hToken))
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Failed OpenProcessToken (%lu)"), GetLastError() ));
|
|
goto Exit;
|
|
}
|
|
|
|
// Look up the name of this privilege.
|
|
|
|
if( !LookupPrivilegeValue( (LPTSTR) NULL, ptszPrivilegeName, &luid ))
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Failed LookupPrivilegeValue (%lu)"), GetLastError() ));
|
|
goto Exit;
|
|
}
|
|
|
|
// Enable the privilege.
|
|
|
|
token_privileges.PrivilegeCount = 1;
|
|
token_privileges.Privileges[0].Luid = luid;
|
|
token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
AdjustTokenPrivileges( hToken, FALSE, &token_privileges, sizeof(TOKEN_PRIVILEGES),
|
|
(PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL);
|
|
|
|
// The return value doesn't tell us anything useful. We have to check GetLastError
|
|
// for ERROR_SUCCESS or ERROR_NOT_ALL_ASSIGNED.
|
|
|
|
if( ERROR_SUCCESS != GetLastError() )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't adjust process token privileges (%lu)"), GetLastError() ));
|
|
goto Exit;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
|
|
if( INVALID_HANDLE_VALUE != hToken )
|
|
CloseHandle( hToken );
|
|
|
|
return( fSuccess );
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
CDebugString::CDebugString(const TRKSVR_MESSAGE_TYPE MsgType)
|
|
{
|
|
switch(MsgType)
|
|
{
|
|
case old_SEARCH:
|
|
_tcscpy( _tsz, TEXT("old_SEARCH") ); break;
|
|
case SEARCH:
|
|
_tcscpy( _tsz, TEXT("SEARCH") ); break;
|
|
case MOVE_NOTIFICATION:
|
|
_tcscpy( _tsz, TEXT("MOVE_NOTIFICATION") ); break;
|
|
case REFRESH:
|
|
_tcscpy( _tsz, TEXT("REFRESH") ); break;
|
|
case SYNC_VOLUMES:
|
|
_tcscpy( _tsz, TEXT("SYNC_VOLUMES") ); break;
|
|
case DELETE_NOTIFY:
|
|
_tcscpy( _tsz, TEXT("DELETE_NOTIFY") ); break;
|
|
case STATISTICS:
|
|
_tcscpy( _tsz, TEXT("STATISTICS") ); break;
|
|
default:
|
|
_tcscpy( _tsz, TEXT("UNKNOWN") ); break;
|
|
}
|
|
}
|
|
|
|
#endif // #if DBG
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
CDebugString::CDebugString(LONG VolIndex, const PFILE_NOTIFY_INFORMATION pNotifyInfo )
|
|
{
|
|
TCHAR *ptsz = _tsz;
|
|
_tsz[0] = TEXT('\0');
|
|
|
|
switch( pNotifyInfo->Action )
|
|
{
|
|
case FILE_ACTION_ADDED:
|
|
_tcscat( _tsz, TEXT("Added ")); break;
|
|
case FILE_ACTION_REMOVED:
|
|
_tcscat( _tsz, TEXT("Removed ")); break;
|
|
case FILE_ACTION_MODIFIED:
|
|
_tcscat( _tsz, TEXT("Modified ")); break;
|
|
case FILE_ACTION_RENAMED_OLD_NAME:
|
|
_tcscat( _tsz, TEXT("Rename old name ")); break;
|
|
case FILE_ACTION_RENAMED_NEW_NAME:
|
|
_tcscat( _tsz, TEXT("Rename new name ")); break;
|
|
case FILE_ACTION_ADDED_STREAM:
|
|
_tcscat( _tsz, TEXT("Added stream ")); break;
|
|
case FILE_ACTION_REMOVED_STREAM:
|
|
_tcscat( _tsz, TEXT("Removed stream ")); break;
|
|
case FILE_ACTION_MODIFIED_STREAM:
|
|
_tcscat( _tsz, TEXT("Modified stream ")); break;
|
|
case FILE_ACTION_REMOVED_BY_DELETE:
|
|
_tcscat( _tsz, TEXT("Removed by delete ")); break;
|
|
case FILE_ACTION_ID_NOT_TUNNELLED:
|
|
_tcscat( _tsz, TEXT("OID not tunnelled ")); break;
|
|
case FILE_ACTION_TUNNELLED_ID_COLLISION:
|
|
_tcscat( _tsz, TEXT("OID tunnel collision ")); break;
|
|
default:
|
|
_stprintf( _tsz, TEXT("Unknown action (0x%x)"), pNotifyInfo->Action ); break;
|
|
}
|
|
|
|
ptsz = _tsz + _tcslen(_tsz);
|
|
|
|
// The name length for an object ID is always 72
|
|
if( pNotifyInfo->FileNameLength != 72 )
|
|
_stprintf( ptsz, TEXT(" name length=%d"), pNotifyInfo->FileNameLength );
|
|
else
|
|
{
|
|
// Stringize the path of this object ID
|
|
CDomainRelativeObjId droidBirth;
|
|
CObjId objid( FOI_OBJECTID, *(FILE_OBJECTID_INFORMATION*)pNotifyInfo->FileName );
|
|
|
|
ptsz[0] = VolChar(VolIndex);
|
|
ptsz[1] = TEXT(':');
|
|
ptsz[2] = TEXT('\0');
|
|
FindLocalPath( VolIndex, objid, &droidBirth, &ptsz[2] );
|
|
ptsz += _tcslen(ptsz);
|
|
ptsz += _stprintf( ptsz, TEXT(" - %c:"), VolChar(VolIndex) );
|
|
_stprintf( ptsz, TEXT("%s"), CDebugString(objid)._tsz );
|
|
ptsz += _tcslen(ptsz);
|
|
}
|
|
|
|
}
|
|
|
|
#endif // #if DBG
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CActiveThreadList::AddCurrent
|
|
//
|
|
// Add the current thread to the list of threads maintained by this class.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CActiveThreadList::AddCurrent( )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HANDLE hThread = NULL;
|
|
|
|
if( !_cs.IsInitialized() )
|
|
{
|
|
TrkLog(( TRKDBG_WARNING, TEXT("Active thread list critsec not initialized!!!") ));
|
|
return S_OK;
|
|
}
|
|
|
|
_cs.Enter();
|
|
|
|
if( _cActiveThreads < _cMaxThreads )
|
|
{
|
|
// Add the thread ID at the end of the list.
|
|
_prgdwThreadIDs[ _cActiveThreads++ ] = GetCurrentThreadId();
|
|
}
|
|
else
|
|
{
|
|
// Alloc a larger buffer for the list, then add the thread ID.
|
|
|
|
hr = Grow();
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
_prgdwThreadIDs[ _cActiveThreads++ ] = GetCurrentThreadId();
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if( SUCCEEDED(hr) )
|
|
TrkLog(( TRKDBG_WORKMAN, TEXT("Added thread 0x%x to the active thread list (%d)"),
|
|
GetCurrentThreadId(), _cActiveThreads ));
|
|
#endif
|
|
|
|
_cs.Leave();
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CActiveThreadList::RemoveCurrent
|
|
//
|
|
// Remove the current thread ID from the list which is maintained by this
|
|
// class.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CActiveThreadList::RemoveCurrent( )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fFound = FALSE;
|
|
DWORD dwThreadID = GetCurrentThreadId();
|
|
|
|
if( !_cs.IsInitialized() )
|
|
return S_OK;
|
|
|
|
_cs.Enter();
|
|
|
|
// Search for the current thread's ID in the list.
|
|
|
|
for( ULONG i = 0; i < _cActiveThreads; i++ )
|
|
{
|
|
if( _prgdwThreadIDs[ i ] == dwThreadID )
|
|
{
|
|
// We found this thread. Remove it from the list by copying down
|
|
// all the IDs behind it.
|
|
|
|
memcpy( &_prgdwThreadIDs[i], &_prgdwThreadIDs[i+1],
|
|
(--_cActiveThreads - i) * sizeof(_prgdwThreadIDs[0]) );
|
|
_prgdwThreadIDs[ _cActiveThreads ] = 0;
|
|
|
|
TrkLog(( TRKDBG_WORKMAN, TEXT("Removed thread 0x%x from the active thread list (%d)"),
|
|
dwThreadID, _cActiveThreads ));
|
|
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !fFound )
|
|
{
|
|
hr = E_FAIL;
|
|
TrkLog(( TRKDBG_WORKMAN, TEXT("CActiveThreadList couldn't remove thread 0x%x, not found"),
|
|
dwThreadID ));
|
|
}
|
|
|
|
_cs.Leave();
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CActiveThreadList::CancelAllRpc
|
|
//
|
|
// Call RpcCancelThread on each of the threads in the list.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CActiveThreadList::CancelAllRpc()
|
|
{
|
|
if( !_cs.IsInitialized() )
|
|
return;
|
|
|
|
_cs.Enter();
|
|
|
|
TrkLog(( TRKDBG_WKS|TRKDBG_SVR, TEXT("Canceling all out-going RPCs") ));
|
|
|
|
// Loop through the list of threads.
|
|
|
|
for( ULONG i = 0; i < _cActiveThreads; i++ )
|
|
{
|
|
TrkAssert( 0 != _prgdwThreadIDs[i] );
|
|
|
|
// Get a thread handle for this thread ID.
|
|
|
|
HANDLE hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, _prgdwThreadIDs[i] );
|
|
if( NULL == hThread )
|
|
{
|
|
// Nothing we can do about it. Move on to the next thread.
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open thread 0x%x to cancel RPC (%lu)"),
|
|
_prgdwThreadIDs[i], GetLastError() ));
|
|
continue;
|
|
}
|
|
|
|
// Cancel any out-going RPC on this thread.
|
|
|
|
RPC_STATUS rpcstatus = RpcCancelThread( hThread );
|
|
if( RPC_S_OK != rpcstatus )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Failed RpcCancelThread on %p/0x%x (%lu)"),
|
|
hThread, _prgdwThreadIDs[i], rpcstatus ));
|
|
}
|
|
else
|
|
{
|
|
TrkLog(( TRKDBG_WORKMAN, TEXT("Canceled RPC on %p/0x%x"),
|
|
hThread, _prgdwThreadIDs[i] ));
|
|
}
|
|
|
|
// Close the thread handle and move on.
|
|
|
|
CloseHandle( hThread );
|
|
}
|
|
|
|
_cs.Leave();
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CActiveThreadList::Grow
|
|
//
|
|
// Private member function to grow the buffer used to hold the thread IDs.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
|
|
HRESULT
|
|
CActiveThreadList::Grow()
|
|
{
|
|
// This is a private method, the critsec has already been entered.
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD *prgNew = NULL;
|
|
ULONG cMaxThreads = _cMaxThreads + INCREMENT_ACTIVE_THREAD_LIST;
|
|
|
|
prgNew = new DWORD[ cMaxThreads ];
|
|
if( NULL == prgNew )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't grow active thread list") ));
|
|
return( E_OUTOFMEMORY );
|
|
}
|
|
|
|
TrkLog(( TRKDBG_WORKMAN, TEXT("Growing active thread list from %d to %d"),
|
|
_cMaxThreads, cMaxThreads ));
|
|
|
|
memcpy( prgNew, _prgdwThreadIDs, _cMaxThreads*sizeof(_prgdwThreadIDs[0]) );
|
|
_cMaxThreads = cMaxThreads;
|
|
delete [] _prgdwThreadIDs;
|
|
_prgdwThreadIDs = prgNew;
|
|
|
|
return( hr );
|
|
}
|