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.
1359 lines
40 KiB
1359 lines
40 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// Job Object Handler
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996.
|
|
//
|
|
// File: persist.cxx
|
|
//
|
|
// Contents: persistent storage methods
|
|
//
|
|
// Classes: CJob (continued)
|
|
//
|
|
// Interfaces: IPersist, IPersistFile
|
|
//
|
|
// History: 24-May-95 EricB created
|
|
// 19-Jul-97 AnirudhS Rewrote SaveP and LoadP to minimize calls
|
|
// to ReadFile, WriteFile and LocalAlloc
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "..\pch\headers.hxx"
|
|
#pragma hdrstop
|
|
#include <align.h> // needed only for use in some schasserts
|
|
#include "job.hxx"
|
|
#include "defines.hxx"
|
|
#include "SASecRPC.h" // Get/SetAccountInformation RPC definition.
|
|
#include <strsafe.h>
|
|
|
|
// defined in security.cxx
|
|
HRESULT
|
|
SetTaskFileSecurity(LPCWSTR pwszTaskPath, BOOL fIsATTask);
|
|
|
|
#define JOB_SIGNATURE_VERSION 1 // data version we write
|
|
#define JOB_SIGNATURE_CLIENT_VERSION 1 // software version we are
|
|
#define JOB_SIGNATURE_MIN_CLIENT_VERSION 1 // min s/w version that can
|
|
// read data we write
|
|
|
|
struct JOB_SIGNATURE_HEADER
|
|
{
|
|
WORD wSignatureVersion;
|
|
WORD wMinClientVersion;
|
|
};
|
|
|
|
void GenerateUniqueID(GUID * pUuid);
|
|
BOOL ReadString(CInputBuffer * pBuf, LPWSTR *ppwsz);
|
|
|
|
//
|
|
// This array of members is used to iterate through the string fields
|
|
// of a CJob that are initially held in m_MainBlock.
|
|
//
|
|
|
|
LPWSTR CJob::* const CJob::s_StringField[] =
|
|
{
|
|
&CJob::m_pwszApplicationName,
|
|
&CJob::m_pwszParameters,
|
|
&CJob::m_pwszWorkingDirectory,
|
|
&CJob::m_pwszCreator,
|
|
&CJob::m_pwszComment
|
|
};
|
|
|
|
// IPersist method
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::IPersist::GetClassID
|
|
//
|
|
// Synopsis: supplies VBScript class object CLSID
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetClassID(CLSID * pClsID)
|
|
{
|
|
TRACE(CJob, GetClassID);
|
|
*pClsID = CLSID_CTask;
|
|
return S_OK;
|
|
}
|
|
|
|
// IPersistFile methods
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::IPersistFile::IsDirty
|
|
//
|
|
// Synopsis: checks for changes since it was last saved
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::IsDirty(void)
|
|
{
|
|
TRACE3(CJob,IsDirty);
|
|
return (IsFlagSet(JOB_I_FLAG_PROPERTIES_DIRTY) ||
|
|
IsFlagSet(JOB_I_FLAG_TRIGGERS_DIRTY) ||
|
|
IsFlagSet(JOB_I_FLAG_SET_ACCOUNT_INFO)) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::IPersistFile::Load
|
|
//
|
|
// Synopsis: loads the job object indicated by the given filename
|
|
//
|
|
// Arguments: [pwszFileName] - name of the job object file
|
|
// [dwMode] - open mode, currently ignored.
|
|
//
|
|
// Notes: All OLE32 strings are UNICODE, including the filename passed
|
|
// in the IPersistFile methods.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::Load(LPCOLESTR pwszFileName, DWORD dwMode)
|
|
{
|
|
return LoadP(pwszFileName, dwMode, TRUE, TRUE);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::LoadP, private
|
|
//
|
|
// Synopsis: private load method that takes a TCHAR filename
|
|
//
|
|
// Arguments: [ptszFileName] - name of the job object file
|
|
// [dwMode] - open mode, currently ignored.
|
|
// [fRemember] - save the file name?
|
|
// [fAllData] - if TRUE, load the entire job. If false, load
|
|
// only the fixed length data at the head of the
|
|
// job file + the job command.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT
|
|
CJob::LoadP(
|
|
LPCTSTR ptszFileName,
|
|
DWORD dwMode,
|
|
BOOL fRemember,
|
|
BOOL fAllData
|
|
)
|
|
{
|
|
TRACE3(CJob, LoadP);
|
|
HRESULT hr = S_OK;
|
|
BYTE * HeapBlock = NULL;
|
|
//WCHAR tszFileName[MAX_PATH + 1] = L"";
|
|
//
|
|
// check the file name
|
|
//
|
|
//if (!CheckFileName((LPOLESTR)ptszFileName, tszFileName, NULL))
|
|
//{
|
|
// return E_INVALIDARG;
|
|
//}
|
|
|
|
//
|
|
// Save the file name?
|
|
//
|
|
if (fRemember)
|
|
{
|
|
LPTSTR ptsz = new TCHAR[lstrlen(ptszFileName) + 1];
|
|
if (!ptsz)
|
|
{
|
|
ERR_OUT("CJob::LoadP", E_OUTOFMEMORY);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if (m_ptszFileName)
|
|
{
|
|
delete m_ptszFileName;
|
|
}
|
|
m_ptszFileName = ptsz;
|
|
StringCchCopy(m_ptszFileName, lstrlen(ptszFileName) + 1, ptszFileName);
|
|
}
|
|
|
|
//
|
|
// Open the file.
|
|
//
|
|
HANDLE hFile = NULL;
|
|
hr = OpenFileWithRetry(ptszFileName, GENERIC_READ, FILE_SHARE_READ, &hFile);
|
|
if (FAILED(hr))
|
|
{
|
|
ERR_OUT("CJob::LoadP, file open", hr);
|
|
return hr;
|
|
}
|
|
|
|
if (FILE_TYPE_DISK != GetFileType(hFile))
|
|
{
|
|
CloseHandle(hFile);
|
|
ERR_OUT("CJob::Load, Not a disk file", E_ACCESSDENIED);
|
|
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
|
|
if (fRemember)
|
|
{
|
|
m_fFileCreated = TRUE;
|
|
}
|
|
|
|
//
|
|
// Get the file size.
|
|
//
|
|
DWORD dwFileSize;
|
|
{
|
|
DWORD dwFileSizeHigh;
|
|
DWORD dwError;
|
|
dwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
|
|
if (dwFileSize == 0xFFFFFFFF &&
|
|
(dwError = GetLastError()) != NO_ERROR)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwError);
|
|
ERR_OUT("CJob::Load, GetFileSize", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (dwFileSizeHigh > 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
schDebugOut((DEB_ERROR, "CJob::Load: file too big, SizeHigh = %u\n",
|
|
dwFileSizeHigh));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (dwFileSize < sizeof(FIXDLEN_DATA))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
schDebugOut((DEB_ERROR, "CJob::Load: file too small, size = %u\n",
|
|
dwFileSize));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Read the entire file into memory.
|
|
//
|
|
HeapBlock = new BYTE[dwFileSize];
|
|
if (HeapBlock == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
ERR_OUT("CJob::Load, buffer alloc", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
DWORD dwBytesRead;
|
|
if (!ReadFile(hFile, HeapBlock, dwFileSize, &dwBytesRead, NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ERR_OUT("CJob::Load, read file", hr);
|
|
delete [] HeapBlock;
|
|
goto Cleanup;
|
|
}
|
|
if (dwBytesRead != dwFileSize)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
ERR_OUT("CJob::Load bytes read", hr);
|
|
delete [] HeapBlock;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Close the file.
|
|
//
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Free old values of properties that we are about to read, and switch
|
|
// to the new heap block.
|
|
//
|
|
FreeProperties();
|
|
m_MainBlock.Set(HeapBlock, dwFileSize);
|
|
|
|
//
|
|
// Get the fixed length Job data.
|
|
// (We already verified that the file is at least as big as FIXDLEN_DATA.)
|
|
//
|
|
{ // scope flData and CInputBuffer to avoid "initialization skipped" error
|
|
const FIXDLEN_DATA& flData = *(FIXDLEN_DATA *)&HeapBlock[0];
|
|
CInputBuffer Buf(&HeapBlock[sizeof FIXDLEN_DATA],
|
|
&HeapBlock[dwFileSize]);
|
|
|
|
//
|
|
// Check the version.
|
|
//
|
|
// Here is where, in future versions, we would look at the version
|
|
// properties to determine how to read the job properties.
|
|
// For now, though, we just reject old versions as being invalid.
|
|
//
|
|
#ifdef IN_THE_FUTURE
|
|
|
|
if (m_wVersion != flData.wVersion)
|
|
{
|
|
fNewVersion = TRUE;
|
|
//
|
|
// Add version specific processing here.
|
|
//
|
|
}
|
|
|
|
#else // !IN_THE_FUTURE
|
|
|
|
if (m_wFileObjVer != flData.wFileObjVer)
|
|
{
|
|
hr = SCHED_E_UNKNOWN_OBJECT_VERSION;
|
|
ERR_OUT("CJob::Load invalid object version", 0);
|
|
goto Cleanup;
|
|
}
|
|
|
|
#endif // !IN_THE_FUTURE
|
|
|
|
//schDebugOut((DEB_TRACE, "Load: job object version: %d.%d, build %d\n",
|
|
// HIBYTE(flData.wVersion), LOBYTE(flData.wVersion),
|
|
// flData.wFileObjVer));
|
|
|
|
m_wVersion = flData.wVersion;
|
|
m_wFileObjVer = flData.wFileObjVer;
|
|
m_uuidJob = flData.uuidJob;
|
|
m_wTriggerOffset = flData.wTriggerOffset;
|
|
m_wErrorRetryCount = flData.wErrorRetryCount;
|
|
m_wErrorRetryInterval = flData.wErrorRetryInterval;
|
|
m_wIdleWait = flData.wIdleWait;
|
|
m_wIdleDeadline = flData.wIdleDeadline;
|
|
m_dwPriority = flData.dwPriority;
|
|
m_dwMaxRunTime = flData.dwMaxRunTime;
|
|
m_ExitCode = flData.ExitCode;
|
|
m_hrStatus = flData.hrStatus;
|
|
m_rgFlags = flData.rgFlags;
|
|
m_stMostRecentRunTime = flData.stMostRecentRunTime;
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
|
|
if (!Buf.Read(&m_cRunningInstances, sizeof m_cRunningInstances))
|
|
{
|
|
ERR_OUT("CJob::Load, read Running Instance Count", 0);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the variable length Job properties.
|
|
//
|
|
// In all cases, retrieve the job application name.
|
|
// (Notice that flData.wAppNameLenOffset is not honored. We have to
|
|
// leave it this way now for backward compatibility.)
|
|
//
|
|
if (!ReadString(&Buf, &m_pwszApplicationName))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If a full load, retrieve the rest of the variable length data,
|
|
// including the triggers.
|
|
//
|
|
if (fAllData)
|
|
{
|
|
// Strings
|
|
if (!(ReadString(&Buf, &m_pwszParameters) &&
|
|
ReadString(&Buf, &m_pwszWorkingDirectory) &&
|
|
ReadString(&Buf, &m_pwszCreator) &&
|
|
ReadString(&Buf, &m_pwszComment)
|
|
))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// User Data size
|
|
if (!Buf.Read(&m_cbTaskData, sizeof m_cbTaskData))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// User Data
|
|
if (m_cbTaskData == 0)
|
|
{
|
|
m_pbTaskData = NULL;
|
|
}
|
|
else
|
|
{
|
|
m_pbTaskData = Buf.CurrentPosition();
|
|
if (!Buf.Advance(m_cbTaskData))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
// Size of reserved data
|
|
if (!Buf.Read(&m_cReserved, sizeof m_cReserved))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Reserved data
|
|
//
|
|
// If there is reserved data, it must begin with a structure
|
|
// that we recognize. In this version we recognize the
|
|
// TASKRESERVED1 structure.
|
|
//
|
|
m_pbReserved = NULL;
|
|
if (m_cReserved == 0)
|
|
{
|
|
//
|
|
// There is no reserved data. Initialize the members that
|
|
// we would have read from the reserved data to defaults.
|
|
//
|
|
m_hrStartError = SCHED_S_TASK_HAS_NOT_RUN;
|
|
m_rgTaskFlags = 0;
|
|
}
|
|
else if (m_cReserved < sizeof(TASKRESERVED1))
|
|
{
|
|
ERR_OUT("CJob::Load, invalid reserved data", hr);
|
|
m_cReserved = 0;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
m_pbReserved = Buf.CurrentPosition();
|
|
if (!Buf.Advance(m_cReserved))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the portion of the Reserved Data that we understand
|
|
// into private data members.
|
|
// It may not be aligned properly, so use CopyMemory.
|
|
//
|
|
TASKRESERVED1 Reserved;
|
|
CopyMemory(&Reserved, m_pbReserved, sizeof Reserved);
|
|
m_hrStartError = Reserved.hrStartError;
|
|
m_rgTaskFlags = Reserved.rgTaskFlags;
|
|
}
|
|
|
|
//
|
|
// Load trigger data.
|
|
//
|
|
hr = this->_LoadTriggersFromBuffer(&Buf);
|
|
if (FAILED(hr))
|
|
{
|
|
ERR_OUT("Loading triggers from storage", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If there is more data after the triggers, it must begin with a
|
|
// signature header and a signature. If there is less data than
|
|
// that, treat it as though the file has no signature.
|
|
// If a signature is present, but its "minimum client version" is
|
|
// greater than our version, treat it as though the file has no
|
|
// signature.
|
|
//
|
|
JOB_SIGNATURE_HEADER SignHead;
|
|
if (Buf.Read(&SignHead, sizeof SignHead) &&
|
|
SignHead.wMinClientVersion <= JOB_SIGNATURE_CLIENT_VERSION)
|
|
{
|
|
m_pbSignature = Buf.CurrentPosition();
|
|
if (!Buf.Advance(SIGNATURE_SIZE))
|
|
{
|
|
schDebugOut((DEB_ERROR, "CJob::Load: si too small, ignoring\n"));
|
|
m_pbSignature = NULL;
|
|
}
|
|
}
|
|
// else m_pbSignature was set to NULL in FreeProperties
|
|
}
|
|
} // end CInputBuffer and flData scope
|
|
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Close the file.
|
|
//
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::SaveP, private
|
|
//
|
|
// Synopsis: saves the object to storage, takes a TCHAR file name
|
|
//
|
|
// Arguments: [ptszFileName] - if null, save to the previously loaded file.
|
|
// [fRemember] - if TRUE, the object becomes associated with
|
|
// the new filename.
|
|
// [flOptions] - can have the following bits set:
|
|
// SAVEP_VARIABLE_LENGTH_DATA:
|
|
// if set, saves all job data except the running
|
|
// instance count. If not set, saves
|
|
// only the fixed length data at the beginning
|
|
// of the job. The file must already exist (the
|
|
// filename must be NULL) in this case.
|
|
//
|
|
// SAVEP_RUNNING_INSTANCE_COUNT:
|
|
// if set, the running instance count is saved
|
|
//
|
|
// SAVEP_PRESERVE_NET_SCHEDULE:
|
|
// if NOT set, the JOB_I_FLAG_NET_SCHEDULE flag
|
|
// is automatically cleared.
|
|
//
|
|
// Returns: HRESULT codes.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT
|
|
CJob::SaveP(LPCTSTR ptszFileName, BOOL fRemember, ULONG flOptions)
|
|
{
|
|
TRACE3(CJob, SaveP);
|
|
|
|
HRESULT hr = S_OK;
|
|
HANDLE hFile = NULL;
|
|
BOOL fSetSecurity = FALSE;
|
|
|
|
//
|
|
// Decide which name to save the file as. Use the one passed in if
|
|
// there is one, otherwise use the previously remembered one.
|
|
//
|
|
LPCTSTR ptszFileToSaveAs = ptszFileName ? ptszFileName : m_ptszFileName;
|
|
if (!(ptszFileToSaveAs && *ptszFileToSaveAs))
|
|
{
|
|
//
|
|
// Can't do a save if there is no file name.
|
|
//
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Figure out whether we will create a new file or open an existing one.
|
|
// If using the passed in filename, then this is a save-as (or if
|
|
// fRemember is false, a save-copy-as) operation and a new file must
|
|
// be created.
|
|
// If using the previously remembered filename, then a new file must
|
|
// be created iff the file wasn't saved before.
|
|
//
|
|
DWORD dwDisposition;
|
|
DWORD dwAttributes;
|
|
|
|
if (!(ptszFileToSaveAs == m_ptszFileName && m_fFileCreated))
|
|
{
|
|
dwDisposition = CREATE_NEW;
|
|
|
|
if (!(flOptions & SAVEP_VARIABLE_LENGTH_DATA))
|
|
{
|
|
//
|
|
// Creating a new file is only valid if all the data is to be
|
|
// saved. Otherwise we would end up with a partial (invalid) file.
|
|
//
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
dwAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
if (IsFlagSet(TASK_FLAG_HIDDEN))
|
|
{
|
|
dwAttributes = FILE_ATTRIBUTE_HIDDEN;
|
|
}
|
|
|
|
//
|
|
// Always write running instance count on file create.
|
|
//
|
|
flOptions |= SAVEP_RUNNING_INSTANCE_COUNT;
|
|
|
|
//
|
|
// On file creation, generate a unique ID for this job.
|
|
// Done for Win95 as well as NT.
|
|
//
|
|
|
|
GenerateUniqueID(&m_uuidJob);
|
|
|
|
//
|
|
// Set security on file creation. This is done after all writes
|
|
// have succeeded and the file has been closed.
|
|
//
|
|
fSetSecurity = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is a save to an existing file.
|
|
//
|
|
dwDisposition = OPEN_EXISTING;
|
|
|
|
dwAttributes = GetFileAttributes(m_ptszFileName);
|
|
if (dwAttributes == -1)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ERR_OUT("CJob::Save, GetFileAttributes", hr);
|
|
return hr;
|
|
}
|
|
|
|
DWORD dwOrgAttributes = dwAttributes;
|
|
|
|
//
|
|
// Remove the read-only attribute.
|
|
//
|
|
|
|
dwAttributes &= ~FILE_ATTRIBUTE_READONLY;
|
|
|
|
//
|
|
// If the hidden flag is set and the hidden attribute has
|
|
// not been set yet, set it now.
|
|
//
|
|
|
|
if (IsFlagSet(TASK_FLAG_HIDDEN))
|
|
{
|
|
dwAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
|
}
|
|
|
|
if (dwAttributes != dwOrgAttributes)
|
|
{
|
|
SetFileAttributes(m_ptszFileName, dwAttributes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create/Open the file.
|
|
//
|
|
hFile = CreateFile(ptszFileToSaveAs,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
dwDisposition,
|
|
dwAttributes |
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ERR_OUT("CJob::Save, file open/create", hr);
|
|
return hr;
|
|
}
|
|
if (FILE_TYPE_DISK != GetFileType(hFile))
|
|
{
|
|
CloseHandle(hFile);
|
|
ERR_OUT("CJob::Save, Not a disk file", E_ACCESSDENIED);
|
|
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
|
|
|
|
if (fRemember || ptszFileToSaveAs == m_ptszFileName)
|
|
{
|
|
m_fFileCreated = TRUE;
|
|
}
|
|
|
|
//
|
|
// Initialize the creator property if not already done so.
|
|
// Set it to the caller's username.
|
|
//
|
|
|
|
if (m_pwszCreator == NULL)
|
|
{
|
|
//
|
|
// Other than out of memory, if an error occurs, leave the
|
|
// creator field alone.
|
|
//
|
|
|
|
TCHAR tszUserName[MAX_USERNAME + 1];
|
|
DWORD ccUserName = MAX_USERNAME + 1;
|
|
|
|
if (GetUserName(tszUserName, &ccUserName))
|
|
{
|
|
// NB : GetUserName returned char count includes the null
|
|
// character.
|
|
//
|
|
m_pwszCreator = new WCHAR[ccUserName];
|
|
|
|
if (m_pwszCreator == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
CHECK_HRESULT(hr);
|
|
goto ErrExit;
|
|
}
|
|
|
|
StringCchCopy(m_pwszCreator, ccUserName, tszUserName);
|
|
}
|
|
}
|
|
|
|
{ // scope to avoid "initialization skipped" error
|
|
|
|
BOOL fUpdateJobState = FALSE;
|
|
|
|
//
|
|
// The disabled flag takes precedence over other states except running.
|
|
//
|
|
if (IsFlagSet(TASK_FLAG_DISABLED))
|
|
{
|
|
if (!IsStatus(SCHED_S_TASK_RUNNING))
|
|
{
|
|
SetStatus(SCHED_S_TASK_DISABLED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsStatus(SCHED_S_TASK_DISABLED))
|
|
{
|
|
//
|
|
// UpdateJobState will set the correct status if no longer
|
|
// disabled.
|
|
//
|
|
fUpdateJobState = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to see if the triggers are dirty. If so, update the job status
|
|
// and flags before writing it out.
|
|
//
|
|
if (IsFlagSet(JOB_I_FLAG_TRIGGERS_DIRTY))
|
|
{
|
|
fUpdateJobState = TRUE;
|
|
}
|
|
|
|
//
|
|
// The flag value JOB_I_FLAG_RUN_PROP_CHANGE is never written to disk.
|
|
// Instead, the need for a wait list rebuild is signalled by clearing
|
|
// JOB_I_FLAG_NO_RUN_PROP_CHANGE. If JOB_I_FLAG_RUN_PROP_CHANGE were to be
|
|
// written to disk, then CheckDir would see this bit set and do a wait
|
|
// list rebuild, but it would also need to clear the bit to prevent
|
|
// successive wait list rebuilds. However, the write to clear the bit
|
|
// would cause an additional change notification and CheckDir call. So, to
|
|
// avoid this thrashing, we signal a wait list rebuild by the absence of
|
|
// the JOB_I_FLAG_NO_RUN_PROP_CHANGE bit.
|
|
//
|
|
if (IsFlagSet(JOB_I_FLAG_RUN_PROP_CHANGE))
|
|
{
|
|
ClearFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
|
|
ClearFlag(JOB_I_FLAG_NO_RUN_PROP_CHANGE);
|
|
fUpdateJobState = TRUE;
|
|
}
|
|
|
|
if (fUpdateJobState)
|
|
{
|
|
UpdateJobState(FALSE);
|
|
}
|
|
|
|
//
|
|
// Regenerate a unique id for the job (a GUID) when the application
|
|
// changes. This is done for security reasons.
|
|
//
|
|
// NB : We need to do this for Win95 as well as NT since we may be
|
|
// editing an NT job from a Win95 machine.
|
|
//
|
|
|
|
if (IsFlagSet(JOB_I_FLAG_APPNAME_CHANGE))
|
|
{
|
|
GenerateUniqueID(&m_uuidJob);
|
|
ClearFlag(JOB_I_FLAG_APPNAME_CHANGE);
|
|
}
|
|
|
|
if (!(flOptions & SAVEP_PRESERVE_NET_SCHEDULE))
|
|
{
|
|
ClearFlag(JOB_I_FLAG_NET_SCHEDULE);
|
|
}
|
|
|
|
//
|
|
// Save Job fixed length data.
|
|
//
|
|
|
|
// Allocate a stack buffer which will be sufficient if we are not
|
|
// saving variable length data.
|
|
// Note that sizeof flStruct > sizeof FIXDLEN_DATA + sizeof WORD,
|
|
// due to struct packing requirements, so don't use sizeof flStruct
|
|
// to compute offsets.
|
|
//
|
|
struct FLSTRUCT
|
|
{
|
|
FIXDLEN_DATA flData;
|
|
WORD cRunningInstances;
|
|
} flStruct;
|
|
|
|
// This code depends on flData and cRunningInstances being contiguous
|
|
schAssert(FIELD_OFFSET(FLSTRUCT, cRunningInstances) == sizeof FIXDLEN_DATA);
|
|
|
|
flStruct.flData.wVersion = m_wVersion;
|
|
flStruct.flData.wFileObjVer = m_wFileObjVer;
|
|
flStruct.flData.uuidJob = m_uuidJob;
|
|
flStruct.flData.wAppNameLenOffset = sizeof(FIXDLEN_DATA) +
|
|
sizeof(m_cRunningInstances);
|
|
flStruct.flData.wTriggerOffset = m_wTriggerOffset;
|
|
flStruct.flData.wErrorRetryCount = m_wErrorRetryCount;
|
|
flStruct.flData.wErrorRetryInterval = m_wErrorRetryInterval;
|
|
flStruct.flData.wIdleWait = m_wIdleWait;
|
|
flStruct.flData.wIdleDeadline = m_wIdleDeadline;
|
|
flStruct.flData.dwPriority = m_dwPriority;
|
|
flStruct.flData.dwMaxRunTime = m_dwMaxRunTime;
|
|
flStruct.flData.ExitCode = m_ExitCode;
|
|
flStruct.flData.hrStatus = m_hrStatus;
|
|
//
|
|
// Don't save the dirty & set account information flags.
|
|
//
|
|
flStruct.flData.rgFlags = m_rgFlags & ~NON_PERSISTED_JOB_FLAGS;
|
|
|
|
flStruct.flData.stMostRecentRunTime = m_stMostRecentRunTime;
|
|
flStruct.cRunningInstances = m_cRunningInstances;
|
|
|
|
//
|
|
// Compute the number of bytes to write to the file. This will be
|
|
// the size of the intermediate buffer to be allocated.
|
|
//
|
|
BYTE * pSource = (BYTE *) &flStruct;
|
|
DWORD cbToWrite = sizeof(flStruct.flData);
|
|
|
|
//
|
|
// Save the running instance data only if (a) the file is being created,
|
|
// or (b) the SAVEP_RUNNING_INSTANCE_COUNT option is set.
|
|
// If SAVEP_VARIABLE_LENGTH_DATA is set, allocate temporary space for the
|
|
// running instance count, regardless of whether we are going to write it.
|
|
//
|
|
if (flOptions & (SAVEP_RUNNING_INSTANCE_COUNT |
|
|
SAVEP_VARIABLE_LENGTH_DATA))
|
|
{
|
|
cbToWrite += sizeof(flStruct.cRunningInstances);
|
|
}
|
|
|
|
if (flOptions & SAVEP_VARIABLE_LENGTH_DATA)
|
|
{
|
|
//
|
|
// Add the space needed to write the strings and their lengths.
|
|
// Save the lengths for use later.
|
|
//
|
|
WORD acStringLen[ARRAY_LEN(s_StringField)]; // array of string lengths
|
|
cbToWrite += sizeof acStringLen;
|
|
|
|
for (int i = 0; i < ARRAY_LEN(acStringLen); i++)
|
|
{
|
|
LPWSTR pwsz = this->*s_StringField[i];
|
|
acStringLen[i] = (pwsz && *pwsz) ? wcslen(pwsz) + 1 : 0;
|
|
cbToWrite += acStringLen[i] * sizeof WCHAR;
|
|
}
|
|
|
|
//
|
|
// Add the space needed to write the user data, the reserved data
|
|
// and their lengths.
|
|
//
|
|
if (m_cReserved < sizeof TASKRESERVED1)
|
|
{
|
|
schAssert(m_cReserved == 0);
|
|
m_cReserved = sizeof TASKRESERVED1;
|
|
}
|
|
cbToWrite += sizeof(m_cbTaskData) + m_cbTaskData
|
|
+ sizeof(m_cReserved) + m_cReserved;
|
|
|
|
//
|
|
// cbToWrite is now the offset to the trigger data. Save it.
|
|
//
|
|
schAssert(cbToWrite <= MAXUSHORT); // BUGBUG Do NOT just assert.
|
|
// Also return a "limit exceeded" error.
|
|
flStruct.flData.wTriggerOffset = m_wTriggerOffset = (WORD) cbToWrite;
|
|
|
|
//
|
|
// Add the space needed to write the triggers and their count.
|
|
//
|
|
WORD cTriggers = m_Triggers.GetCount();
|
|
cbToWrite += sizeof cTriggers + cTriggers * sizeof TASK_TRIGGER;
|
|
|
|
//
|
|
// If the job has a signature, add the space needed to write it.
|
|
//
|
|
if (m_pbSignature != NULL)
|
|
{
|
|
cbToWrite += sizeof(JOB_SIGNATURE_HEADER) + SIGNATURE_SIZE;
|
|
}
|
|
|
|
//
|
|
// We have now computed the required space. Allocate a buffer of
|
|
// that size.
|
|
//
|
|
pSource = new BYTE[cbToWrite];
|
|
if (pSource == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto ErrExit;
|
|
}
|
|
|
|
//
|
|
// Copy data into the buffer.
|
|
//
|
|
BYTE * pCurrent = pSource; // current write position
|
|
|
|
#define WRITE_DATA(pSrc, cbSize) \
|
|
CopyMemory(pCurrent, (pSrc), (cbSize)); \
|
|
pCurrent += (cbSize);
|
|
|
|
// FIXDLEN_DATA and Running Instance Count
|
|
WRITE_DATA(&flStruct, sizeof flStruct.flData +
|
|
sizeof flStruct.cRunningInstances);
|
|
|
|
// Strings
|
|
for (i = 0; i < ARRAY_LEN(acStringLen); i++)
|
|
{
|
|
schAssert(POINTER_IS_ALIGNED(pCurrent, ALIGN_WORD));
|
|
*(WORD *) pCurrent = acStringLen[i];
|
|
pCurrent += sizeof WORD;
|
|
|
|
WRITE_DATA(this->*s_StringField[i],
|
|
acStringLen[i] * sizeof WCHAR);
|
|
}
|
|
|
|
// User data
|
|
schAssert(POINTER_IS_ALIGNED(pCurrent, ALIGN_WORD));
|
|
*(WORD *) pCurrent = m_cbTaskData;
|
|
pCurrent += sizeof WORD;
|
|
|
|
WRITE_DATA(m_pbTaskData, m_cbTaskData);
|
|
// Note that pCurrent may no longer be WORD-aligned
|
|
|
|
// Reserved data
|
|
WRITE_DATA(&m_cReserved, sizeof m_cReserved);
|
|
|
|
// Copy private members into the reserved data block to save
|
|
TASKRESERVED1 Reserved1 = { m_hrStartError, m_rgTaskFlags };
|
|
if (m_pbReserved == NULL)
|
|
{
|
|
schAssert(m_cReserved == sizeof Reserved1);
|
|
WRITE_DATA(&Reserved1, m_cReserved);
|
|
}
|
|
else
|
|
{
|
|
schAssert(m_cReserved >= sizeof Reserved1);
|
|
CopyMemory(m_pbReserved, &Reserved1, sizeof Reserved1);
|
|
WRITE_DATA(m_pbReserved, m_cReserved);
|
|
}
|
|
|
|
// Triggers
|
|
WRITE_DATA(&cTriggers, sizeof cTriggers);
|
|
WRITE_DATA(m_Triggers.GetArray(), sizeof TASK_TRIGGER * cTriggers);
|
|
|
|
// Signature
|
|
if (m_pbSignature != NULL)
|
|
{
|
|
JOB_SIGNATURE_HEADER SignHead;
|
|
SignHead.wSignatureVersion = JOB_SIGNATURE_VERSION;
|
|
SignHead.wMinClientVersion = JOB_SIGNATURE_MIN_CLIENT_VERSION;
|
|
|
|
WRITE_DATA(&SignHead, sizeof SignHead);
|
|
WRITE_DATA(m_pbSignature, SIGNATURE_SIZE);
|
|
}
|
|
|
|
#undef WRITE_DATA
|
|
|
|
schAssert(pCurrent == pSource + cbToWrite);
|
|
}
|
|
|
|
//
|
|
// Actually write the data to the file
|
|
//
|
|
if ((flOptions & SAVEP_VARIABLE_LENGTH_DATA) &&
|
|
!(flOptions & SAVEP_RUNNING_INSTANCE_COUNT))
|
|
{
|
|
//
|
|
// Write FIXDLEN_DATA, skip over the running instance count, and
|
|
// write the variable length data
|
|
//
|
|
DWORD cbWritten;
|
|
if (!WriteFile(hFile, pSource, sizeof FIXDLEN_DATA, &cbWritten, NULL)
|
|
|| cbWritten != sizeof FIXDLEN_DATA)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ERR_OUT("CJob::Save, write of Job fixed length data", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
DWORD dwNewPos = SetFilePointer(hFile,
|
|
sizeof(m_cRunningInstances),
|
|
NULL,
|
|
FILE_CURRENT);
|
|
if (dwNewPos == 0xffffffff)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ERR_OUT("CJob::Save, moving past running instance count", hr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
cbToWrite -= (sizeof FIXDLEN_DATA + sizeof m_cRunningInstances);
|
|
|
|
if (!WriteFile(hFile,
|
|
pSource + (sizeof FIXDLEN_DATA + sizeof m_cRunningInstances),
|
|
cbToWrite,
|
|
&cbWritten,
|
|
NULL)
|
|
|| cbWritten != cbToWrite)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ERR_OUT("CJob::Save, write of Job variable length data", hr);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We can do it all in a single WriteFile.
|
|
//
|
|
DWORD cbWritten;
|
|
if (!WriteFile(hFile, pSource, cbToWrite, &cbWritten, NULL)
|
|
|| cbWritten != cbToWrite)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ERR_OUT("CJob::Save, write of Job", hr);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ((flOptions & SAVEP_VARIABLE_LENGTH_DATA) &&
|
|
!SetEndOfFile(hFile))
|
|
{
|
|
ERR_OUT("CJob::Save, SetEOF", HRESULT_FROM_WIN32(GetLastError()));
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
CloseHandle(hFile);
|
|
hFile = NULL;
|
|
|
|
if (pSource != (BYTE *) &flStruct)
|
|
{
|
|
delete [] pSource;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrExit;
|
|
}
|
|
|
|
} // end scope
|
|
|
|
//
|
|
// Notify the shell of the changes.
|
|
//
|
|
if (ptszFileName != NULL)
|
|
{
|
|
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, ptszFileName, NULL);
|
|
}
|
|
else
|
|
{
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, m_ptszFileName, NULL);
|
|
}
|
|
|
|
//
|
|
// If doing a Save-As, save the new filename.
|
|
//
|
|
if (fRemember && ptszFileName != NULL)
|
|
{
|
|
delete m_ptszFileName;
|
|
m_ptszFileName = new TCHAR[lstrlen(ptszFileName) + 1];
|
|
if (!m_ptszFileName)
|
|
{
|
|
ERR_OUT("CJob::SaveP", E_OUTOFMEMORY);
|
|
hr = E_OUTOFMEMORY;
|
|
goto ErrExit;
|
|
}
|
|
|
|
StringCchCopy(m_ptszFileName, lstrlen(ptszFileName) + 1, ptszFileName);
|
|
}
|
|
|
|
if (ptszFileName == NULL || fRemember)
|
|
{
|
|
//
|
|
// BUGBUG: this is not strictly accurate. There could be a dirty
|
|
// string prop that wouldn't be saved during a light save
|
|
// (SAVEP_VARIABLE_LENGTH_DATA not specified). This source of
|
|
// potential error could be alleviated by breaking
|
|
// JOB_I_FLAG_PROPERTIES_DIRTY into two flags:
|
|
// JOB_I_FLAG_FIXED_PROPS_DIRTY & JOB_I_FLAG_VAR_PROPS_DIRTY
|
|
//
|
|
ClearFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
|
|
if (flOptions & SAVEP_VARIABLE_LENGTH_DATA)
|
|
{
|
|
ClearFlag(JOB_I_FLAG_TRIGGERS_DIRTY);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set default privileges on the file. This is done only for new
|
|
// files created as a result of save.
|
|
//
|
|
|
|
/**********************************
|
|
This ** SHOULD BE ** redundant. Inherits from container.
|
|
if (fSetSecurity)
|
|
{
|
|
//
|
|
// NB : Logic prior to CreateFile guarantees the file name
|
|
// will not be NULL.
|
|
//
|
|
|
|
hr = SetTaskFileSecurity(ptszFileToSaveAs,
|
|
this->IsFlagSet(JOB_I_FLAG_NET_SCHEDULE));
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrExit;
|
|
}
|
|
}
|
|
************************************/
|
|
|
|
return S_OK;
|
|
|
|
ErrExit:
|
|
if (hFile != NULL) CloseHandle(hFile);
|
|
|
|
if (dwDisposition == CREATE_NEW)
|
|
{
|
|
if (!DeleteFile(ptszFileToSaveAs))
|
|
{
|
|
ERR_OUT("CJob::SaveP: DeleteFile", GetLastError());
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::SaveWithRetry, private
|
|
//
|
|
// Synopsis: Use CJob::SaveP to save the job object to disk with failure retry.
|
|
//
|
|
// Arguments: [ptszFileName] - See CJob::SaveP
|
|
// [fRemember] - See CJob::SaveP
|
|
// [flOptions] - See CJob::SaveP
|
|
//
|
|
// Notes: This method is called everywhere SaveP was originally called to
|
|
// add robustness in the event the file is already opened.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CJob::SaveWithRetry(LPCTSTR ptszFileName, BOOL fRemember, ULONG flOptions)
|
|
{
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Write the updated values to the job object. If there are sharing
|
|
// violations, retry up to two times.
|
|
//
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
hr = SaveP(ptszFileName, fRemember, flOptions);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
return S_OK;
|
|
}
|
|
if (hr != HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION))
|
|
{
|
|
//
|
|
// If we have a failure other than sharing violation, we will
|
|
// retry anyway after reporting the error.
|
|
//
|
|
ERR_OUT("SaveWithRetry", hr);
|
|
}
|
|
|
|
//
|
|
// Wait semi-pseudo-random amount of time before trying again.
|
|
//
|
|
Sleep(250 + (rand() % 250));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::IPersistFile::SaveCompleted
|
|
//
|
|
// Synopsis: indicates the caller has saved the file with a call to
|
|
// IPersistFile::Save and is finished working with it
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SaveCompleted(LPCOLESTR pwszFileName)
|
|
{
|
|
TRACE(CJob, SaveCompleted);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::IPersistFile::GetCurFile
|
|
//
|
|
// Synopsis: supplies either the absolute path of the currently loaded
|
|
// script file or the default filename prompt, if there is no
|
|
// currently-associated file
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetCurFile(LPOLESTR * ppwszFileName)
|
|
{
|
|
TRACE(CJob, GetCurFile);
|
|
|
|
HRESULT hr;
|
|
TCHAR * ptszName, tszDefaultName[SCH_SMBUF_LEN];
|
|
WCHAR * pwszName, * pwszBuf = NULL;
|
|
|
|
if (!m_ptszFileName || m_ptszFileName[0] == TEXT('\0'))
|
|
{
|
|
//
|
|
// No file currently loaded, return default prompt 'cause that is
|
|
// what the OLE spec says to do.
|
|
//
|
|
StringCchCopy(tszDefaultName, SCH_SMBUF_LEN, TEXT("*.") TSZ_JOB);
|
|
ptszName = tszDefaultName;
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
ptszName = m_ptszFileName;
|
|
hr = S_OK;
|
|
}
|
|
|
|
pwszName = ptszName;
|
|
int size = wcslen(pwszName);
|
|
LPOLESTR pwz;
|
|
pwz = (LPOLESTR)CoTaskMemAlloc((size + 1) * sizeof(WCHAR));
|
|
if (!pwz)
|
|
{
|
|
*ppwszFileName = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(pwz, size + 1, pwszName);
|
|
*ppwszFileName = pwz;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::FreeProperties
|
|
//
|
|
// Synopsis: Frees variable length property memory
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
CJob::FreeProperties(void)
|
|
{
|
|
for (int iProperty = 0;
|
|
iProperty < ARRAY_LEN(s_StringField);
|
|
iProperty++)
|
|
{
|
|
DELETE_CJOB_FIELD(this->*s_StringField[iProperty])
|
|
}
|
|
|
|
DELETE_CJOB_FIELD(m_pbTaskData)
|
|
m_cbTaskData = 0;
|
|
|
|
DELETE_CJOB_FIELD(m_pbReserved)
|
|
m_cReserved = 0;
|
|
|
|
DELETE_CJOB_FIELD(m_pbSignature)
|
|
m_pbSignature = 0;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: ReadString
|
|
//
|
|
// Synopsis: Reads a wide char string in from an in-memory buffer
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL
|
|
ReadString(CInputBuffer * pBuf, LPWSTR *ppwsz)
|
|
{
|
|
schAssert(POINTER_IS_ALIGNED(pBuf->CurrentPosition(), ALIGN_WORD));
|
|
schAssert(*ppwsz == NULL);
|
|
|
|
//
|
|
// Read the string length
|
|
//
|
|
WORD cch;
|
|
if (!pBuf->Read(&cch, sizeof cch))
|
|
{
|
|
ERR_OUT("ReadString, file lacks string length", 0);
|
|
return FALSE;
|
|
}
|
|
|
|
if (cch != 0)
|
|
{
|
|
LPWSTR pwsz = (LPWSTR) pBuf->CurrentPosition();
|
|
|
|
//
|
|
// The string length mustn't exceed the buffer size
|
|
//
|
|
if (!pBuf->Advance(cch * sizeof WCHAR))
|
|
{
|
|
ERR_OUT("ReadString, string overruns file size", 0);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Verify null termination
|
|
//
|
|
if (pwsz[cch-1] != L'\0')
|
|
{
|
|
ERR_OUT("ReadString, string not null terminated", 0);
|
|
return FALSE;
|
|
}
|
|
|
|
*ppwsz = pwsz;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: GenerateUniqueID
|
|
//
|
|
// Synopsis: Intialize the UUID passed to a unique ID. On NT, UuidCreate
|
|
// initializes it. If UuidCreate fails, default to our custom
|
|
// ID generation code which is used always on Win95.
|
|
//
|
|
// Arguments: [pUuid] -- Ptr to UUID to initialize.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
GenerateUniqueID(GUID * pUuid)
|
|
{
|
|
schAssert(pUuid != NULL);
|
|
|
|
//
|
|
// Call UuidCreate only on NT. If this should fail, drop down to
|
|
// our own id generation.
|
|
//
|
|
|
|
if (UuidCreate(pUuid) == RPC_S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Must generate our own unique id.
|
|
//
|
|
// Set Data 1 to the windows tick count.
|
|
//
|
|
|
|
pUuid->Data1 = GetTickCount();
|
|
|
|
//
|
|
// Set Data2 & Data3 to the current system time milliseconds
|
|
// and seconds values respectively.
|
|
//
|
|
|
|
SYSTEMTIME systime;
|
|
GetSystemTime(&systime);
|
|
|
|
pUuid->Data2 = systime.wMilliseconds;
|
|
pUuid->Data3 = systime.wSecond;
|
|
|
|
//
|
|
// Write the passed uuid ptr address into the first 4 bytes of
|
|
// Data4. Then write the current system time minute value into
|
|
// the following 2. The remaining 2 we'll leave as-is.
|
|
//
|
|
|
|
CopyMemory(&pUuid->Data4, &pUuid, sizeof(GUID *));
|
|
CopyMemory((&pUuid->Data4) + sizeof(GUID *), &systime.wMinute,
|
|
sizeof(systime.wMinute));
|
|
}
|