GWCLEAN.CPP

// --gwclean.cpp-------------------------------------------------------------- 
//
// Implements the GWCLEAN gateway utility which
// moves all messages in a gateway's folder before a particular
// date and time to a PST (personal information store).
//
// Syntax:
//
// GWCLEAN GatewayProfileName [flags]
//
// where the flags are:
//
// -DAYS=<#DaysPriorToToday> -- # of days prior to today
// defaults to yesterday
// -FILE=<PSTFileName> -- PST file name to move messages to
// defaults to GWCLEAN.PST in current
// directory
// -FOLDER=<GatewayFolderName> -- folder to move gateway message from
// defaults to MTS-OUT
//
// Copyright (C) Microsoft Corporation 1986-1996. All Rights Reserved
//
// ---------------------------------------------------------------------------

#include "edk.h"
#include "mspst.h"

#include "gwstring.h"
#include "gwobjcls.h"
#include "gwevents.h"// compiled from gwevents.mc
#include "gwclean.chk"

// Globals
CGWClean * lpCGWClean = NULL; // global GWCLEAN class object pointer

// external function declarations
extern HRESULT HrParseCommandLine(
IN INT argc, // argument count
IN CHAR * argv[], // list of arguments
OUT LPTSTR lpProfile, // GW profile name
OUT LPTSTR lpPSTName, // PST name
OUT LPTSTR lpFolder, // GW folder name
OUT LPFILETIME lpBeforeTime); // date/time before structure pointer

extern VOID DisplayUserMsg( // RETURNS: VOID
IN UINT uResID, // resource identifier
...); // variable arguments

extern HRESULT HrComputeDate( // RETURNS: HRESULT
IN UINT, // number of days before today
OUT LPFILETIME); // file time structure pointer

extern HRESULT HrCheckFileName( // RETURNS: HRESULT
IN LPTSTR lpFileName); // file name to check

// useful constants
LPTSTRpszAppName=TEXT("GWCLEAN");

//$--INTERNAL_ERROR-----------------------------------------------------------
// Helper function for EventLogMsg
// ---------------------------------------------------------------------------
extern inline VOID INTERNAL_ERROR(
IN LPCTSTR str,
IN const HRESULT hr)
{
TCHAR szErrorCode[16] = {0};

EventLogMsg(
GWCLEAN_INTERNALERR,
2, str, _itot( hr, szErrorCode, 16),
0);
}

//$--main---------------------------------------------------------------------
//
// DESCRIPTION: Implement GWCLEAN gateway utility described above.
//
// INPUT: INT -- number of command line arguments
// TCHAR[] * -- command line arguments
//
// RETURNS: int -- 0 if successful,
// non-zero otherwise:
// (See edkcode.h for more specifics)
//
// ----------------------------------------------------------------------------

INT main( // RETURNS: INT
INT argc, // number of command line arguments
TCHAR * argv[]) // command line arguments
{
HRESULT hr = NOERROR;
HRESULThrT=NOERROR;// temporary return code
FILETIME sBeforeTime = {0}; // before date/time structure
TCHAR lpProfile[MAX_PATH+1] = ""; // GW profile name
TCHAR lpPSTName[MAX_PATH+1] = ""; // PST file name
TCHAR lpFolder[MAX_PATH+1] = ""; // GW folder name
HANDLEhEvent=NULL;// event logging handle
EDKEVENTCOUNTsEventCount={0};// event log count structure
BOOLfEventLogOpened=FALSE;// TRUE if event log is open
TCHAR szErrorCode[16] = {0};

// Special base class which guarantees that MAPI is
// unitialized when it goes out of scope.
BOOL fInitialized = FALSE; // TRUE if MAPI is initialized

DEBUGPUBLIC("main()\n");

// check command line arguments
hr = CHK_main(argc, argv);

if ( FAILED(hr) )
{
// This should never happen
MODULE_ERROR("Arguments to main() invalid");
return _nEcFromHr(hr);
}

// Open the NT event log for this application.
hr = HrEventOpenLog(
pszAppName,// application name
NULL,// executable name (computed)
NULL,// event message file (computed)
NULL,// parameter message file
NULL,// category message file
&hEvent);// event logging handle

if ( FAILED(hr) )
{
DisplayUserMsg(
IDS_EVENT_LOG,
hr);

goto cleanup;
}

ASSERTERROR((hEvent != NULL), "Bad hEvent");

fEventLogOpened = TRUE;

// Instantiate a GWCLEAN object.
lpCGWClean = new CGWClean();

if ( !lpCGWClean )
{
EventLogMsg(
GWCLEAN_OUTOFMEMORY,
0,// # of string replacements
0);// # of Win32 error arguments

hr = HR_LOG(E_OUTOFMEMORY);

goto cleanup;
}

// Parse command line arguments. The syntax is:
//
// GWCLEAN <GWProfileName> -FILE=<PSTName> -DAYS=<#days> -FOLDER=<folder>
//
// where PSTName is a fully-pathed file name, #days
// is a positive integer representing the number of days before
// today for which to move all gateway <folder> messages which were
// sent on before that date, and GWProfileName is the name of an
// gateway profile.
//
hr = HrParseCommandLine(argc, argv,
lpProfile, // GW profile name
lpPSTName, // PST file name
lpFolder, // GW folder name
&sBeforeTime); // before time pointer

if ( FAILED(hr) )
{
// User errors are displayed in HrParseCommandLine()
goto cleanup;
}

// Initialize use of extended MAPI.
hr = MAPIInitialize(NULL);

if ( FAILED(hr) )
{
EventLogMsg(
GWCLEAN_MAPIINIT,
1, _itot( hr, szErrorCode, 16),
0);

hr = HR_LOG(E_FAIL);
goto cleanup;
}

fInitialized = TRUE; // MAPI is initialized

// Initialize our CGWClean class instance. This
// function takes care of creating the new PST, logging
// on to the gateway and temporary profiles, etc...
hr = lpCGWClean->HrInitialize(
lpProfile,
lpPSTName,
lpFolder,
&sBeforeTime);

if ( FAILED(hr) )
{
// User messages are taken care of in HrInitialize().
goto cleanup;
}

// Move messages in GW MTS-OUT folder before date/time to
// new PST folder.
hr = lpCGWClean->HrMoveMsgs();

if ( FAILED(hr) )
{
// user messages displayed in HrMoveMsgs
goto cleanup;
}

cleanup:

// If doing event logging
if ( fEventLogOpened == TRUE )
{
// Retrieve number of errors and warnings written to the event log.
ZeroMemory(&sEventCount, sizeof(sEventCount));
hrT = HrEventGetCounts(
&sEventCount);// structure to hold event logging statistics

if ( FAILED(hrT) )
{
INTERNAL_ERROR( TEXT("HrEventGetCounts()"), hrT);
sEventCount.cError = 1;// have at least one error
}

if ( sEventCount.cError == 1 )
{
// print one error message
DisplayUserMsg(
IDS_1ERROR);
}

else if ( sEventCount.cError > 1 )
{
// print multiple error message
DisplayUserMsg(
IDS_ERRORS);
}

if ( sEventCount.cWarning == 1 )
{
// print out one warning message
DisplayUserMsg(
IDS_1WARNING);
}

else if ( sEventCount.cWarning > 1 )
{
// print out multiple warnings message
DisplayUserMsg(
IDS_WARNINGS);
}

// close event log
(VOID)HrEventCloseLog();
hEvent = NULL;
fEventLogOpened = FALSE;

}// end if doing event logging

if ( lpCGWClean )
{
// delete gwclean object.
// A side effect of this is that all MAPI objects are freed
// and all sessions are logged off.
delete lpCGWClean;

} // end if lpCGWClean

if ( fInitialized )
{
// Unitialize MAPI
(VOID)MAPIUninitialize();
}

// We are done.
// return the appropriate exit code
return _nEcFromHr(hr);

} // end main()

//$--HrParseCommandLine---------------------------------------
//
// DESCRIPTION: Parse GWCLEAN command line
//
// INPUT: argc -- argument count
// argv -- list of arguments
//
// OUTPUT: lpProfile -- GW profile name pointer (MAX_PATH + 1 character buffer)
// lpPSTName -- PST file name (MAX_PATH + 1 character buffer)
// lpFolder -- GW folder name (MAX_PATH + 1 character buffer)
// pnDays -- #days before pointer
//
// RETURNS: HRESULT -- NOERROR if successful,
// E_INVALIDARG if bad input
// E_FAIL otherwise
//
// ------------------------------------------------------------
HRESULT HrParseCommandLine( // RETURNS: HRESULT
IN INT argc, // argument count
IN TCHAR * argv[], // list of arguments
OUT LPTSTR lpProfile, // GW profile name
OUT LPTSTR lpPSTName, // PST name
OUT LPTSTR lpFolder, // GW folder name
OUT LPFILETIME lpBeforeTime)// before time pointer
{
HRESULT hr = NOERROR;
LPTSTR lpData = NULL; // pointer to flag data
LPTSTR lpFlag = NULL; // pointer to flag name
ULONG iFlag = 0; // flag index
INT iArg = 0; // argument index
UINT nDays = 0; // number of days before today
BOOL fDaysFound = FALSE; // TRUE if user specifies DAYS flag
LPTSTR lpTemp = 0; // temporary string pointer
UINT cchExeName = 0; // # characters in executable name
DWORD dwRetVal = 0; // Win32 API return code

// fully-pathed module file name
TCHAR lpszExeName[MAX_PATH + 1] = TEXT("");

// default values
TCHAR lpszPath[MAX_PATH + 1] = TEXT(""); // default path
TCHAR lpszPSTName[MAX_PATH + 1] = TEXT(""); // default PST name

const LPTSTR lpszFolder = TEXT("MTS-OUT"); // gw folder
const UINT nDefDays = 1; // #days

const TCHAR chBackSlash = TEXT('\\'); // back slash
const LPTSTR lpszPSTExt = TEXT("PST"); // PST extension
const LPTSTR lpszEXEExt = TEXT("EXE"); // EXE extension

// argument indices into the argument flag array
const UINT iHelp1 = 0; // HELP index
const UINT iHelp2 = 1; // HELP index
const UINT iDays = 2; // DAYS index
const UINT iFile = 3; // FILE index
const UINT iFolder = 4; // FOLDER index

// # of command line flags
const ULONG nFlags = 5;

// Array of known flags for this program.
const LPCTSTR rgpszFlags[nFlags] =
{
TEXT("HELP"), // help flag
TEXT("?"), // help flag
TEXT("DAYS"), // # days before today flag
TEXT("FILE"), // PST file name flag
TEXT("FOLDER") // gateway folder name flag
};

DEBUGPRIVATE("HrParseCommandLine()\n");

hr = CHK_HrParseCommandLine(argc, argv,
lpProfile, lpPSTName, lpFolder, lpBeforeTime);

if ( FAILED(hr) )
{
RETURN(hr);
}

// If no arguments are passed in, print the syntax
// message
if ( argc < 2 )
{
DisplayUserMsg(
IDS_USAGE);
DisplayUserMsg(
IDS_BLANK);
DisplayUserMsg(
IDS_USAGEPROF);
DisplayUserMsg(
IDS_USAGEDAYS);
DisplayUserMsg(
IDS_USAGEPST);
DisplayUserMsg(
IDS_USAGEFOLDER);
DisplayUserMsg(
IDS_USAGEHELP);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

// Build the default default PST file
// name from the fully-pathed executable name.
dwRetVal = GetModuleFileName(
NULL, // module handle (default is current application)
lpszExeName, // string buffer
MAX_PATH); // maximum length

if ( dwRetVal == 0 )
{
INTERNAL_ERROR( TEXT("GetModuleFileName()"), dwRetVal);

hr = HR_LOG(E_FAIL);
goto cleanup;
}

cchExeName = lstrlen(lpszExeName); // # characters in executable name
lstrcpy(lpszPSTName, lpszExeName);

// Replace the EXE extension with the PST extension.
lpTemp = lpszPSTName + (cchExeName - lstrlen(lpszEXEExt));
lstrcpy(lpTemp,
lpszPSTExt);

// Retrieve the default path name
lstrcpy(lpszPath, lpszExeName);

// Strip off the executable name.
lpTemp = lpszPath + (cchExeName - 1);
while ( *lpTemp != chBackSlash )
{
if ( lpTemp == lpszPath )
{
// back at the beginning. This is an error.
INTERNAL_ERROR( TEXT("HrParseCommandLine()"), E_FAIL);

hr = HR_LOG(E_FAIL);
goto cleanup;

} // if can't find back slash

lpTemp--;

} // end while

// When we get to here, lpTemp is pointing to the back slash.
*(lpTemp + 1) = 0; // null terminate path

// loop throught the command line arguments
// Start with the first user-supplied argument
for ( iArg = 1; iArg < argc; iArg++ )
{
hr = _HrExpandCommandLineArgument(
argv[iArg], // argument user entered
(LPTSTR *) rgpszFlags, // array of recognized switches
nFlags, // number of known flags
&iFlag, // pointer to flag index into array
&lpFlag, // pointer to flag name
&lpData); // pointer to flag data

if ( FAILED(hr) )
{
if ( hr == EDK_E_NOT_FOUND )
{
// Bad command line argument.
// Display error message.
DisplayUserMsg(
IDS_PARAMERR);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

else
{
INTERNAL_ERROR( TEXT("_HrExpandCommandLineArgument()"), hr);

hr = HR_LOG(E_FAIL);
goto cleanup;
}
}

if ( (lpFlag == NULL) && (*lpData != 0) )
{
// This should be the gateway profile name
// (The profile name doesn't have a flag.)
lstrcpyn(lpProfile, lpData, MAX_PATH);

continue; // go on to next argument
}

// We have a valid flag. Copy the data
// to the appropriate parameter and test it.
switch ( iFlag )
{
case iHelp1:
case iHelp2:
// user wants help message.
// Print it out.
DisplayUserMsg(
IDS_HELPDESCR);
DisplayUserMsg(
IDS_BLANK);
DisplayUserMsg(
IDS_USAGE);
DisplayUserMsg(
IDS_BLANK);
DisplayUserMsg(
IDS_USAGEPROF);
DisplayUserMsg(
IDS_HELPDAYS1);
DisplayUserMsg(
IDS_HELPDAYS2);
DisplayUserMsg(
IDS_HELPDAYS3);
DisplayUserMsg(
IDS_HELPDAYS4);
DisplayUserMsg(
IDS_HELPDAYS5);
DisplayUserMsg(
IDS_USAGEPST);
DisplayUserMsg(
IDS_HELPPST);
DisplayUserMsg(
IDS_USAGEFOLDER);
DisplayUserMsg(
IDS_HELPFOLDER);
DisplayUserMsg(
IDS_HELPHELP);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;

case iDays:
// Check number of days variable
if ( (lpData == NULL) || (*lpData == 0) )
{
// Bad command line argument.
// Display error message.
DisplayUserMsg(
IDS_BADDAYS);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

// Make sure number of days is a valid decimal
// number less than or equal to 9999.
if ( lstrlen(lpData) > 4 )
{
// number of days is greater than 9999.
DisplayUserMsg(
IDS_BADDAYS);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

// check individual characters of days value
lpTemp = lpData;
while ( *lpTemp )
{
if ( ! isdigit(*lpTemp) )
{
// number of days is not a non-negative
// integer!
DisplayUserMsg(
IDS_BADDAYS);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

lpTemp++;

} // end while

// Try to convert #days to an integer.
nDays = atoi(lpData);

fDaysFound = TRUE; // user specified DAYS value

break;

case iFile:
// check PST output file name
if ( (lpData == NULL) || (*lpData == 0) )
{
// invalid file name.
DisplayUserMsg(
IDS_NOFILE);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

// Save PST file name
if ( strchr(lpData, chBackSlash) == NULL )
{
lstrcpyn(lpPSTName, lpszPath, MAX_PATH);
strncat(lpPSTName, lpData, MAX_PATH);
}

else
{
// already have full path
lstrcpyn(lpPSTName, lpData, MAX_PATH);
}

break;

case iFolder:
// check gateway folder name
if ( (lpData == NULL) || (*lpData == 0) )
{
// invalid folder name.
DisplayUserMsg(
IDS_NOFOLDER);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

// save folder name
lstrcpyn(lpFolder, lpData, MAX_PATH);

// let open folder call determine if its a valid
// folder name.

break;

default:
// shouldn't happen!
INTERNAL_ERROR( TEXT("HrParseCommandLine()"), E_FAIL);

hr = HR_LOG(E_FAIL);
goto cleanup;

} // end switch
} // end for

// Check required parameters
if ( *lpProfile == 0 )
{
DisplayUserMsg(
IDS_NOPROFILE);

hr = HR_LOG(E_INVALIDARG);

goto cleanup;
}

// Handle default values.
if ( !fDaysFound )
{
// use default days value (yesterday)
nDays = nDefDays;
}

// Convert # days to a data and time.
hr = HrComputeDate(nDays, lpBeforeTime);

if ( FAILED(hr) )
{
INTERNAL_ERROR( TEXT("HrComputeDate()"), hr);

goto cleanup;
}

if ( *lpFolder == 0 )
{
// use default folder.
lstrcpyn(lpFolder, lpszFolder, MAX_PATH);
}

if ( *lpPSTName == 0 )
{
// use default PST name
lstrcpyn(lpPSTName, lpszPSTName, MAX_PATH);
}

// Check to see if this file is valid
// and can be created or overwritten
hr = HrCheckFileName(lpPSTName);

if ( FAILED(hr) )
{
// HrCheckFileName prints out error
// messages.
goto cleanup;
}

// We are done.

cleanup:

RETURN(hr);

}

//$--HrCheckFileName----------------------------------------------
//
// DESCRIPTION: Checks to see if file exists. If it does
// prompts user as to whether or not to overwrite
// it. If file is valid file name and can be over-written,
// returns NOERROR. Otherwise, returns E_FAIL.
//
// INPUT: lpFileName -- file name to test for existence
//
// RETURNS: HRESULT -- NOERROR if successful,
// E_INVALIDARG if bad input,
// E_FAIL otherwise.
//
// ---------------------------------------------------------------
HRESULT HrCheckFileName( // RETURNS: HRESULT
IN LPTSTR lpFileName) // file name to check
{
HRESULT hr = NOERROR;
HANDLE hFile = NULL; // file handle
DWORD dwLastError = 0; // last Win32 error
TCHAR szLine[MAX_PATH] = {0}; // large input buffer

DEBUGPRIVATE("HrCheckFileName()\n");

// check input parameters
hr = CHK_HrCheckFileName(lpFileName);

if ( FAILED(hr) )
{
RETURN(hr);
}

hFile = CreateFile(lpFileName,
GENERIC_READ | GENERIC_WRITE,
0,
0,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
0);

if ( hFile == INVALID_HANDLE_VALUE )
{
dwLastError = GetLastError(); // Get windows error code
if ( dwLastError == ERROR_FILE_EXISTS )
{
// File already exists. Ask user is he/she wants to
// overwrite it.
while ( hFile == INVALID_HANDLE_VALUE )
{
DisplayUserMsg(
IDS_OVERWRITEFILE,
lpFileName);

if ( gets(szLine) == NULL )
{
INTERNAL_ERROR( TEXT("gets()"), GetLastError());

hr = HR_LOG(E_FAIL);
goto cleanup;
}

switch ( szLine[0] )
{
case TEXT('Y'):
case TEXT('y'):
// overwrite the file.
hFile = CreateFile(lpFileName,
GENERIC_READ | GENERIC_WRITE,
0,
0,
TRUNCATE_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);

if ( hFile == INVALID_HANDLE_VALUE )
{
DisplayUserMsg(
IDS_OUTPUTERR,
lpFileName);

hr = HR_LOG(E_FAIL);

goto cleanup;
}

break;

case TEXT('n'):
case TEXT('N'):
// Give up!
DisplayUserMsg(
IDS_USERCANCEL);

// This is not really an error. The user
// just wishes to terminate the program early.
hr = HR_LOG(E_FAIL);

goto cleanup;

default:
// ask again!
continue;

} // end switch
} // end while
} // end if file already exits.

else
{
// some other file path or permission-type error
DisplayUserMsg(
IDS_OUTPUTERR,
lpFileName);

hr = HR_LOG(E_FAIL);

goto cleanup;
}

} // end if can't create new file

// O.K. to overwrite file.
// Delete the file just created.
if ( hFile != INVALID_HANDLE_VALUE )
{
CLOSEHANDLE(hFile);

(VOID)DeleteFile(lpFileName);
}

cleanup:

// close the opened file.
CLOSEHANDLE(hFile);

RETURN(hr);

}

//$--DisplayUserMsg----------------------------------------------------------
//
// DESCRIPTION: Utility to display message to command line user.
//
// INPUT: UINT -- Resource string identifier
//
// NOTES: This function can handle up to nine variable string arguments.
//
// RETURNS: nothing
//
// ----------------------------------------------------------------------------

VOID DisplayUserMsg( // RETURNS: VOID
IN UINT nResID, // resource string identifier
...) // additional arguments
{
INT nRetCode= 0; // return value
va_list vArgList= {0}; // variable argument list
static HMODULEhInst=NULL;// application handle
staticBOOLfInit=FALSE;// TRUE if hInst is initialized

const INT cbMsgLength= 256; // maximum length of printed messageso

// LoadString message buffer
TCHAR szMessage[cbMsgLength]= TEXT("");

DEBUGPRIVATE("DisplayUserMsg()\n");

// No CHK_DisplayUserMsg() call.
// We check arguments as we go, as we are already in an error state.

// Check the instance handle passed in.
if ( fInit == FALSE )
{
// Retrieve this applications module handle.
hInst = GetModuleHandle(NULL);

fInit = TRUE;

if ( hInst == NULL )
{
// Print out default error string.
fprintf(stderr, "ERROR: Can't dipslay resource strings.\n");

HR_LOG(E_FAIL);

goto cleanup;
}
}

if ( hInst == NULL )
{
// nothing to do
goto cleanup;
}

nRetCode = LoadString(hInst, nResID, szMessage, cbMsgLength);

if ( nRetCode == 0 )
{
// Print out default error string.
fprintf(stderr, "ERROR: Can't load resource string %d.\n", nRetCode);

HR_LOG(E_FAIL);

goto cleanup;
}

// Get an optional argument list pointer
va_start(vArgList, nResID);

// Print the message to standard out
_vtprintf(szMessage, // format string
vArgList); // variable argument list

va_end(vArgList);

cleanup:

// Return
return;

} // end DisplayUserMsg()

//$--HrComputeDate-----------------------------------------------------------
//
// DESCRIPTION: Computes the date of today minus the number of days passed
// in and fills in the file time structure buffer passed in.
//
// OUTPUT: lpSysFileTime -- pointer to file time buffer
//
// RETURNS: HRESULT -- NOERROR if O.K.,
// E_INVALIDARG if bad input.
//
// ---------------------------------------------------------------------------

static HRESULT HrComputeDate( // RETURNS: HRESULT
IN UINT uDaysBack, // number of days before today
OUT LPFILETIME lpSysFileTime) // pointer to system file time
{
HRESULT hr = NOERROR; // return code
INT iCounter = 0; // loop counter
SYSTEMTIME sLocalSysTime = {0}; // Local system time structure

FILETIME    sLocalFileTime  =   {0};        // Local file time structure     
DWORDLONG dwlFileTime = 0; // file time converted to DWORDLONG

const DWORDLONG dwlNum100nsPerDay = 864000000000; // number of 100 nanoseconds in one day!

DEBUGPRIVATE("HrComputeDate\n");

hr = CHK_HrComputeDate(uDaysBack, lpSysFileTime);

if ( FAILED(hr) )
{
RETURN(hr);
}

// Compute desired system time of the before date/time variable
// before which all gateway MTS-OUT messages are to be moved.
// Algorithm:
// 1) Today's date and time.
// 2) Modify
// from # days before today parameter passed in by user.
// First, get the current system time and adjust it to the end
// of today and convert that to a FILETIME value. Then, convert the
// FILETIME value to a DWORDLONG (64 bit) value.
GetLocalTime(&sLocalSysTime); // Get local time
sLocalSysTime.wHour = 23;
sLocalSysTime.wMinute = 59;
sLocalSysTime.wSecond = 59;
sLocalSysTime.wMilliseconds = 999;
if ( sLocalSysTime.wDayOfWeek )
{
sLocalSysTime.wDayOfWeek--;
}
else
{
sLocalSysTime.wDayOfWeek = 6; // Sunday to Saturday
}
SystemTimeToFileTime(&sLocalSysTime, &sLocalFileTime); // local time to local file time
LocalFileTimeToFileTime(&sLocalFileTime, lpSysFileTime); // local file time to system file time
dwlFileTime = ((DWORDLONG)((DWORDLONG) lpSysFileTime->dwLowDateTime) |
(DWORDLONG)(((DWORDLONG)(((DWORDLONG)lpSysFileTime->dwHighDateTime) << 32)) & 0xFFFFFFFF00000000));

// Subtract off the desired number of 100 nanoseconds from this mornings
// time.
// (The FILETIME structure measures time in 100 nanoseconds intervals.)
// Then, convert this value back to a FILETIME value.
dwlFileTime -= (DWORDLONG)(((DWORDLONG) uDaysBack) * dwlNum100nsPerDay);
lpSysFileTime->dwLowDateTime = (DWORD) dwlFileTime;
lpSysFileTime->dwHighDateTime =
(DWORD) (((DWORDLONG) (dwlFileTime >> 32)) & 0xFFFFFFFF);

RETURN(hr);

} // end HrComputeDate()

/*
* EOF
*/