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.
1436 lines
39 KiB
1436 lines
39 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// Job Scheduler Job Object Handler
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996.
|
|
//
|
|
// File: job.cxx
|
|
//
|
|
// Contents: ITask interface methods
|
|
//
|
|
// Classes: CJob
|
|
//
|
|
// Interfaces: ITask
|
|
//
|
|
// History: 23-May-95 EricB created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "..\pch\headers.hxx"
|
|
#pragma hdrstop
|
|
#include "job.hxx"
|
|
#include <StrSafe.h>
|
|
|
|
WCHAR wszEmpty[] = L"";
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::Run
|
|
//
|
|
// Synopsis: Run the job now. Sets a service bit on the job file. The
|
|
// running service will notice this bit being set and will run
|
|
// this job.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::Run(void)
|
|
{
|
|
TRACE(CJob, Run)
|
|
|
|
if (!IsFlagSet(JOB_I_FLAG_HAS_APPNAME))
|
|
{
|
|
return SCHED_E_TASK_NOT_READY;
|
|
}
|
|
|
|
//
|
|
// Set the magic bit.
|
|
//
|
|
|
|
SetFlag(JOB_I_FLAG_RUN_NOW);
|
|
|
|
//
|
|
// Save the service flags to disk so that the change can be noted by the
|
|
// service. Preserve the net schedule flag, thus allowing a user to force
|
|
// a run of an AT job without clearing the AT bit.
|
|
//
|
|
|
|
return SaveWithRetry(NULL, FALSE, SAVEP_PRESERVE_NET_SCHEDULE);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::Terminate
|
|
//
|
|
// Synopsis: Abort this job, if it is running. Do so by setting an abort
|
|
// flag on the job object. The scheduler service local to the
|
|
// job object will detect this change and abort the job.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::Terminate(void)
|
|
{
|
|
TRACE(CJob, Terminate)
|
|
|
|
if (m_cRunningInstances > 0)
|
|
{
|
|
//
|
|
// Set the abort status bit and rewrite the state. This will instruct
|
|
// the service to process the job, detect the abort bit flag, and
|
|
// abort the job. As with the Run method, this doesn't zap the AT
|
|
// flag.
|
|
//
|
|
|
|
SetFlag(JOB_I_FLAG_ABORT_NOW);
|
|
|
|
return SaveWithRetry(NULL, FALSE, SAVEP_PRESERVE_NET_SCHEDULE);
|
|
}
|
|
else
|
|
{
|
|
return(SCHED_E_TASK_NOT_RUNNING);
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::CreateTrigger
|
|
//
|
|
// Synopsis: Create a new trigger, add it to the job object, and return a
|
|
// pointer to it.
|
|
//
|
|
// Arguments: [piNewTrigger] - the index of the new trigger, optional
|
|
// [ppTrigger] - a pointer to the new trigger
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The trigger is AddRef'd in CreateTriggerP
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::CreateTrigger(WORD * piNewTrigger, ITaskTrigger ** ppTrigger)
|
|
{
|
|
TRACE3(CJob, CreateTrigger)
|
|
|
|
*ppTrigger = NULL; // Init in case of error.
|
|
|
|
SYSTEMTIME st;
|
|
GetLocalTime(&st);
|
|
|
|
TASK_TRIGGER jt = {
|
|
sizeof(TASK_TRIGGER), // Trigger size.
|
|
m_Triggers.GetCount(), // Reserved (trigger index).
|
|
st.wYear, // Beginning year.
|
|
st.wMonth, // Beginning month.
|
|
st.wDay, // Beginning day.
|
|
0, // Ending year.
|
|
0, // Ending month.
|
|
0, // Ending day.
|
|
st.wHour, // Starting hour.
|
|
st.wMinute, // Starting minute.
|
|
0, // Minutes duration.
|
|
0, // Minutes interval.
|
|
JOB_TRIGGER_I_FLAG_NOT_SET, // Flags.
|
|
TASK_TIME_TRIGGER_DAILY, // Trigger type.
|
|
1, // Trigger union.
|
|
0, // Reserved2. Unused.
|
|
0 // Random minutes interval.
|
|
};
|
|
|
|
CTrigger * pt = new CTrigger(jt.Reserved1, this);
|
|
|
|
if (pt == NULL)
|
|
{
|
|
CHECK_HRESULT(E_OUTOFMEMORY);
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
HRESULT hr = m_Triggers.Add(jt);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
delete pt;
|
|
CHECK_HRESULT(hr);
|
|
return(hr);
|
|
}
|
|
|
|
this->SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
this->SetTriggersDirty();
|
|
|
|
*piNewTrigger = jt.Reserved1;
|
|
*ppTrigger = pt;
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::DeleteTrigger
|
|
//
|
|
// Synopsis: Remove a run trigger.
|
|
//
|
|
// Arguments: [iTrigger] - the index of the trigger to be removed
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::DeleteTrigger(WORD iTrigger)
|
|
{
|
|
TRACE3(CJob, DeleteTrigger)
|
|
|
|
TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
|
|
|
|
if (pjt != NULL)
|
|
{
|
|
m_Triggers.Remove(iTrigger);
|
|
|
|
// Fixup remaining indices to account for deletion.
|
|
//
|
|
for (WORD i = iTrigger; i < m_Triggers.GetCount(); i++)
|
|
{
|
|
m_Triggers[i].Reserved1--;
|
|
}
|
|
|
|
this->SetTriggersDirty();
|
|
|
|
if (!m_Triggers.GetCount())
|
|
{
|
|
this->ClearFlag(JOB_I_FLAG_HAS_TRIGGERS);
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
else
|
|
{
|
|
return(SCHED_E_TRIGGER_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::_GetTrigger, private
|
|
//
|
|
// Synopsis: Return the TASK_TRIGGER associated with the index. The
|
|
// TASK_TRIGGER reserved field specifies the trigger index.
|
|
//
|
|
// Arguments: [iTrigger] -- Trigger index.
|
|
//
|
|
// Returns: TASK_TRIGGER * -- Trigger found.
|
|
// NULL -- Trigger not found.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
TASK_TRIGGER *
|
|
CJob::_GetTrigger(WORD iTrigger)
|
|
{
|
|
for (WORD i = 0; i < m_Triggers.GetCount(); i++)
|
|
{
|
|
if (m_Triggers[i].Reserved1 == iTrigger)
|
|
{
|
|
return(&m_Triggers[i]);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetTriggerCount
|
|
//
|
|
// Synopsis: Return the count of run triggers.
|
|
//
|
|
// Arguments: [pwCount] - the address where the count is to be returned
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetTriggerCount(WORD * pwCount)
|
|
{
|
|
TRACE3(CJob, GetTriggerCount)
|
|
|
|
*pwCount = m_Triggers.GetCount();
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetTrigger
|
|
//
|
|
// Synopsis: Return an ITaskTrigger pointer to the indicated trigger.
|
|
//
|
|
// Arguments: [iTrigger] - the index of the trigger to fetch
|
|
// [ppTrigger] - the returned trigger pointer
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The trigger is AddRef'd in GetTriggerObj(m_Triggers.GetTrigger)
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetTrigger(WORD iTrigger, ITaskTrigger ** ppTrigger)
|
|
{
|
|
TRACE3(CJob, GetTrigger)
|
|
|
|
*ppTrigger = NULL; // Init in case of error.
|
|
|
|
TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
|
|
|
|
if (pjt != NULL)
|
|
{
|
|
CTrigger * pt = new CTrigger(iTrigger, this);
|
|
|
|
if (pt == NULL)
|
|
{
|
|
CHECK_HRESULT(E_OUTOFMEMORY);
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
*ppTrigger = pt;
|
|
return(S_OK);
|
|
}
|
|
|
|
return(SCHED_E_TRIGGER_NOT_FOUND);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetTriggerString
|
|
//
|
|
// Synopsis: Return the indicated run trigger as a string.
|
|
//
|
|
// Arguments: [iTrigger] - the index of the trigger to convert
|
|
// [ppwszTrigger] - the returned string buffer
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The string is callee allocated and caller freed with
|
|
// CoTaskMemFree.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetTriggerString(WORD iTrigger, LPWSTR * ppwszTrigger)
|
|
{
|
|
TRACE3(CJob, GetTriggerString)
|
|
|
|
*ppwszTrigger = NULL; // Init in case of error.
|
|
|
|
TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
|
|
|
|
if (pjt != NULL)
|
|
{
|
|
return(::StringFromTrigger(pjt, ppwszTrigger, NULL));
|
|
}
|
|
|
|
return(SCHED_E_TRIGGER_NOT_FOUND);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetApplicationName
|
|
//
|
|
// Synopsis: Set the Application Name String property
|
|
//
|
|
// Arguments: [pwszApplicationName] - the name of the app to execute.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The string is caller allocated and freed.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetApplicationName(LPCWSTR pwszApplicationName)
|
|
{
|
|
TRACE3(CJob, SetApplicationName)
|
|
|
|
if (!pwszApplicationName)
|
|
return E_INVALIDARG;
|
|
|
|
if (wcslen(pwszApplicationName) > MAX_PATH)
|
|
return E_INVALIDARG;
|
|
|
|
//
|
|
// We don't use a try/catch
|
|
// because the COM interface runs in the caller's context and it is their
|
|
// responsibility to ensure good params. This latter statement is true for
|
|
// all scheduler COM interface methods.
|
|
//
|
|
if (*pwszApplicationName == L'\0')
|
|
{
|
|
//
|
|
// The caller wants the command set to an empty string.
|
|
//
|
|
|
|
ClearFlag(JOB_I_FLAG_HAS_APPNAME);
|
|
|
|
//
|
|
// We are using a null pointer for an empty string as an optimization.
|
|
//
|
|
if (m_pwszApplicationName == NULL)
|
|
{
|
|
//
|
|
// Nothing to do.
|
|
//
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Setting this flag will instruct the persist code to
|
|
// regenerate a GUID for this job. This is done for security
|
|
// reasons.
|
|
//
|
|
// NB : This must be done for Win95 as well as NT.
|
|
//
|
|
|
|
SetFlag(JOB_I_FLAG_APPNAME_CHANGE);
|
|
|
|
DELETE_CJOB_FIELD(m_pwszApplicationName);
|
|
//
|
|
// We want this change to trigger a wait list rebuild.
|
|
//
|
|
SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Update the flags and status.
|
|
//
|
|
SetFlag(JOB_I_FLAG_HAS_APPNAME);
|
|
|
|
if (IsStatus(SCHED_S_TASK_NOT_SCHEDULED))
|
|
{
|
|
//
|
|
// Note that if the status went from SCHED_S_TASK_NOT_SCHEDULED to
|
|
// SCHED_S_TASK_HAS_NOT_RUN or SCHED_S_TASK_READY, then we
|
|
// want CheckDir to issue a wait list rebuild because the job has
|
|
// gone from a non-runable to a runable state. Thus, in the if
|
|
// clause below we set JOB_I_FLAG_RUN_PROP_CHANGE.
|
|
//
|
|
if (IsFlagSet(JOB_I_FLAG_HAS_TRIGGERS) &&
|
|
!IsFlagSet(JOB_I_FLAG_NO_VALID_TRIGGERS))
|
|
{
|
|
if (m_stMostRecentRunTime.wYear == 0)
|
|
{
|
|
//
|
|
// Job has never run if last-run-time property is null
|
|
// (all elements will be zero if null, testing year is
|
|
// sufficient).
|
|
//
|
|
SetStatus(SCHED_S_TASK_HAS_NOT_RUN);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Job has run in the past, so it is now waiting to run
|
|
// again.
|
|
//
|
|
SetStatus(SCHED_S_TASK_READY);
|
|
}
|
|
//
|
|
// We want this change to trigger a wait list rebuild.
|
|
//
|
|
SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
|
|
}
|
|
}
|
|
|
|
TCHAR tszAppName[MAX_PATH+1]; // define buffer size as 1 more than max in
|
|
SecureZeroMemory(tszAppName, sizeof tszAppName); // order to leave space for NULL terminator
|
|
|
|
if (IsLocalFilename(m_ptszFileName))
|
|
{
|
|
lstrcpyn(tszAppName, pwszApplicationName, MAX_PATH); // lstrcpyn needs to know maximum # of chars,
|
|
// this needs to be 1 less than buffer size
|
|
ProcessApplicationName(tszAppName, MAX_PATH+1,
|
|
m_pwszWorkingDirectory ?
|
|
m_pwszWorkingDirectory :
|
|
wszEmpty);
|
|
}
|
|
else
|
|
{
|
|
lstrcpyn(tszAppName, pwszApplicationName, MAX_PATH); // # of chars, not buffer size
|
|
StripLeadTrailSpace(tszAppName);
|
|
DeleteQuotes(tszAppName);
|
|
}
|
|
|
|
//
|
|
// Allocate a new string, adding one for the null terminator
|
|
//
|
|
|
|
ULONG cchAppName = lstrlen(tszAppName) + 1;
|
|
LPWSTR pwszTmp = new WCHAR[cchAppName];
|
|
|
|
if (pwszTmp == NULL)
|
|
{
|
|
ERR_OUT("CJob::SetApplicationName", E_OUTOFMEMORY);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(pwszTmp, cchAppName, tszAppName);
|
|
|
|
//
|
|
// Setting this flag will instruct the persist code to
|
|
// regenerate a GUID for this job. This is done for security
|
|
// reasons.
|
|
//
|
|
// NB : This must be done for Win95 as well as NT.
|
|
//
|
|
// Ensure first, that the application has indeed changed.
|
|
//
|
|
|
|
if (m_pwszApplicationName != NULL && lstrcmpiW(
|
|
m_pwszApplicationName,
|
|
pwszTmp) != 0)
|
|
{
|
|
SetFlag(JOB_I_FLAG_APPNAME_CHANGE);
|
|
}
|
|
|
|
DELETE_CJOB_FIELD(m_pwszApplicationName);
|
|
m_pwszApplicationName = pwszTmp;
|
|
}
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetApplicationName
|
|
//
|
|
// Synopsis: Get the ApplicationName String property
|
|
//
|
|
// Arguments: [ppwszApplicationName] - the returned string buffer
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The command string is passed to CreateProcess to be executed
|
|
// at task run time.
|
|
// The string is callee allocated and caller freed with
|
|
// CoTaskMemFree.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetApplicationName(LPWSTR * ppwszApplicationName)
|
|
{
|
|
TRACE3(CJob, GetApplicationName)
|
|
WCHAR * pwszCmd;
|
|
|
|
if (m_pwszApplicationName == NULL)
|
|
{
|
|
//
|
|
// Return an empty string rather than a null pointer
|
|
//
|
|
pwszCmd = wszEmpty;
|
|
}
|
|
else
|
|
{
|
|
pwszCmd = m_pwszApplicationName;
|
|
}
|
|
|
|
// add one for the null.
|
|
*ppwszApplicationName = (LPWSTR)CoTaskMemAlloc((wcslen(pwszCmd) + 1) *
|
|
sizeof(WCHAR));
|
|
|
|
if (*ppwszApplicationName == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(*ppwszApplicationName, wcslen(pwszCmd) + 1, pwszCmd);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetParameters
|
|
//
|
|
// Synopsis: Set the Parameters String property
|
|
//
|
|
// Arguments: [pwszParameters] - the application parameters string
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The Parameters string is appended to the Application Name and
|
|
// passed to CreateProcess as the command line to be executed at
|
|
// task run time.
|
|
// The string is caller allocated and freed.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetParameters(LPCWSTR pwszParameters)
|
|
{
|
|
TRACE3(CJob, SetParameters)
|
|
|
|
if (*pwszParameters == L'\0')
|
|
{
|
|
//
|
|
// The caller wants the Parameters set to an empty string.
|
|
//
|
|
// We are using a null pointer for an empty string as an optimization.
|
|
//
|
|
DELETE_CJOB_FIELD(m_pwszParameters);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate a new string, adding one for the null terminator
|
|
//
|
|
LPWSTR pwszTmp = new WCHAR[wcslen(pwszParameters) + 1];
|
|
if (pwszTmp == NULL)
|
|
{
|
|
ERR_OUT("CJob::SetParameters", E_OUTOFMEMORY);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(pwszTmp, wcslen(pwszParameters) + 1, pwszParameters);
|
|
|
|
DELETE_CJOB_FIELD(m_pwszParameters);
|
|
|
|
m_pwszParameters = pwszTmp;
|
|
}
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetParameters
|
|
//
|
|
// Synopsis: Get the Parameters String property
|
|
//
|
|
// Arguments: [ppwszParameters] - the returned string buffer
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The Parameters string is appended to the Application Name and
|
|
// passed to CreateProcess as the command line to be executed at
|
|
// task run time.
|
|
// The string is callee allocated and caller freed with
|
|
// CoTaskMemFree.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetParameters(LPWSTR * ppwszParameters)
|
|
{
|
|
TRACE3(CJob, GetParameters)
|
|
WCHAR * pwszTmp;
|
|
|
|
if (m_pwszParameters == NULL)
|
|
{
|
|
//
|
|
// Return an empty string rather than a null pointer
|
|
//
|
|
pwszTmp = wszEmpty;
|
|
}
|
|
else
|
|
{
|
|
pwszTmp = m_pwszParameters;
|
|
}
|
|
|
|
// add one for the null.
|
|
*ppwszParameters = (LPWSTR)CoTaskMemAlloc((wcslen(pwszTmp) + 1) *
|
|
sizeof(WCHAR));
|
|
|
|
if (*ppwszParameters == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(*ppwszParameters,wcslen(pwszTmp) + 1, pwszTmp);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetWorkingDirectory
|
|
//
|
|
// Synopsis: Set the Working Directory (current directory) property
|
|
//
|
|
// Arguments: [pwszWorkingDir] - the name to use
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The string is caller allocated and freed
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetWorkingDirectory(LPCWSTR pwszWorkingDirectory)
|
|
{
|
|
TRACE3(CJob, SetWorkingDirectory)
|
|
|
|
if (*pwszWorkingDirectory == L'\0')
|
|
{
|
|
//
|
|
// The caller wants the WorkingDirectory set to an empty string.
|
|
//
|
|
DELETE_CJOB_FIELD(m_pwszWorkingDirectory);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate a new string, adding one for the null terminator
|
|
//
|
|
LPWSTR pwszTmp = new WCHAR[wcslen(pwszWorkingDirectory) + 1];
|
|
if (pwszTmp == NULL)
|
|
{
|
|
ERR_OUT("CJob::SetWorkingDirectory", E_OUTOFMEMORY);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(pwszTmp, wcslen(pwszWorkingDirectory) + 1, pwszWorkingDirectory);
|
|
|
|
DELETE_CJOB_FIELD(m_pwszWorkingDirectory);
|
|
|
|
m_pwszWorkingDirectory = pwszTmp;
|
|
|
|
//
|
|
// Remove double quotes from working directory path; they're not supported
|
|
// by SetCurrentDirectory.
|
|
//
|
|
|
|
DeleteQuotes(m_pwszWorkingDirectory);
|
|
}
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetWorkingDir
|
|
//
|
|
// Synopsis: Get the Working Directory (current directory) property
|
|
//
|
|
// Arguments: [ppwszWorkingDirectory] - the returned string buffer
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The string is callee allocated and caller freed with
|
|
// CoTaskMemFree.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetWorkingDirectory(LPWSTR * ppwszWorkingDirectory)
|
|
{
|
|
TRACE3(CJob, GetWorkingDirectory)
|
|
WCHAR * pwszWorkingDir;
|
|
|
|
if (m_pwszWorkingDirectory == NULL)
|
|
{
|
|
//
|
|
// Return an empty string rather than a null pointer
|
|
//
|
|
pwszWorkingDir = wszEmpty;
|
|
}
|
|
else
|
|
{
|
|
pwszWorkingDir = m_pwszWorkingDirectory;
|
|
}
|
|
|
|
// add one for the null.
|
|
*ppwszWorkingDirectory = (LPWSTR)CoTaskMemAlloc(
|
|
(wcslen(pwszWorkingDir) + 1) * sizeof(WCHAR));
|
|
|
|
if (*ppwszWorkingDirectory == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(*ppwszWorkingDirectory,wcslen(pwszWorkingDir) + 1, pwszWorkingDir);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetComment
|
|
//
|
|
// Synopsis: Set the comment field.
|
|
//
|
|
// Arguments: [pwszComment] - the comment string value, caller alloc'd and
|
|
// freed
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetComment(LPCWSTR pwszComment)
|
|
{
|
|
TRACE3(CJob, SetComment)
|
|
|
|
if (*pwszComment == L'\0')
|
|
{
|
|
//
|
|
// The caller wants the Comment set to an empty string.
|
|
//
|
|
DELETE_CJOB_FIELD(m_pwszComment);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate a new string, adding one for the null terminator
|
|
//
|
|
LPWSTR pwszTmp = new WCHAR[wcslen(pwszComment) + 1];
|
|
if (pwszTmp == NULL)
|
|
{
|
|
ERR_OUT("CJob::SetComment", E_OUTOFMEMORY);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(pwszTmp, wcslen(pwszComment) + 1, pwszComment);
|
|
|
|
DELETE_CJOB_FIELD(m_pwszComment);
|
|
|
|
m_pwszComment = pwszTmp;
|
|
}
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetComment
|
|
//
|
|
// Synopsis: Get the comment field property.
|
|
//
|
|
// Arguments: [ppwszComment] - the returned string buffer
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The string is callee allocated and caller freed with
|
|
// CoTaskMemFree.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetComment(LPWSTR * ppwszComment)
|
|
{
|
|
TRACE3(CJob, GetComment)
|
|
WCHAR * pwszCmt;
|
|
|
|
if (m_pwszComment == NULL)
|
|
{
|
|
//
|
|
// Return an empty string rather than a null pointer
|
|
//
|
|
pwszCmt = wszEmpty;
|
|
}
|
|
else
|
|
{
|
|
pwszCmt = m_pwszComment;
|
|
}
|
|
|
|
// add one for the null.
|
|
*ppwszComment = (LPWSTR)CoTaskMemAlloc((wcslen(pwszCmt) + 1) *
|
|
sizeof(WCHAR));
|
|
|
|
if (*ppwszComment == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(*ppwszComment, wcslen(pwszCmt) + 1, pwszCmt);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::_SetSignature
|
|
//
|
|
// Synopsis: Set the job's signature.
|
|
//
|
|
// Arguments: [pbSignature] - assumed to be SIGNATURE_SIZE bytes long.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT
|
|
CJob::_SetSignature(const BYTE * pbSignature)
|
|
{
|
|
TRACE3(CJob, SetSignature)
|
|
|
|
LPBYTE pb = new BYTE[SIGNATURE_SIZE];
|
|
|
|
if (pb == NULL)
|
|
{
|
|
ERR_OUT("CJob::SetSignature", E_OUTOFMEMORY);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
CopyMemory(pb, pbSignature, SIGNATURE_SIZE);
|
|
|
|
DELETE_CJOB_FIELD(m_pbSignature);
|
|
|
|
m_pbSignature = pb;
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetPriority
|
|
//
|
|
// Synopsis: Set the priority property
|
|
//
|
|
// Arguments: [dwPriority] - the priority value
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: Controls the priority at which the job will run. Applies to NT
|
|
// only, a no-op on Win95. This must be one of the four values
|
|
// from CreateProcess's priority class.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetPriority(DWORD dwPriority)
|
|
{
|
|
TRACE3(CJob, SetPriority)
|
|
//
|
|
// Check for valid priority values
|
|
//
|
|
switch (dwPriority)
|
|
{
|
|
case IDLE_PRIORITY_CLASS:
|
|
case NORMAL_PRIORITY_CLASS:
|
|
case HIGH_PRIORITY_CLASS:
|
|
case REALTIME_PRIORITY_CLASS:
|
|
break;
|
|
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
m_dwPriority = dwPriority;
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetPriority
|
|
//
|
|
// Synopsis: Get the priority property
|
|
//
|
|
// Arguments: [pdwPriority] - priority return address
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetPriority(DWORD * pdwPriority)
|
|
{
|
|
TRACE3(CJob, GetPriority)
|
|
|
|
*pdwPriority = m_dwPriority;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetMaxRunTime
|
|
//
|
|
// Synopsis: Set the MaximumRunTime property
|
|
//
|
|
// Arguments: [dwMaxRunTime] - new MaxRunTime value in milliseconds
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetMaxRunTime(DWORD dwMaxRunTime)
|
|
{
|
|
TRACE3(CJob, SetMaxRunTime)
|
|
|
|
m_dwMaxRunTime = dwMaxRunTime;
|
|
|
|
// Cause a wait list rebuild
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY | JOB_I_FLAG_RUN_PROP_CHANGE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetMaxRunTime
|
|
//
|
|
// Synopsis: Get the MaximumRunTime property
|
|
//
|
|
// Arguments: [pdwMaxRunTime] - MaxRunTime return address (milliseconds)
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetMaxRunTime(DWORD * pdwMaxRunTime)
|
|
{
|
|
TRACE3(CJob, GetMaxRunTime)
|
|
|
|
*pdwMaxRunTime = m_dwMaxRunTime;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetFlags
|
|
//
|
|
// Synopsis: Set the bit flags for the various boolean properties
|
|
//
|
|
// Arguments: [fLogConfig] - Boolean: should changes be logged true/false.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetFlags(DWORD rgFlags)
|
|
{
|
|
TRACE3(CJob, SetFlags)
|
|
|
|
if ((rgFlags ^ m_rgFlags) &
|
|
(TASK_FLAG_DISABLED |
|
|
TASK_FLAG_START_ONLY_IF_IDLE |
|
|
TASK_FLAG_KILL_ON_IDLE_END |
|
|
TASK_FLAG_DONT_START_IF_ON_BATTERIES |
|
|
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES |
|
|
TASK_FLAG_RUN_ONLY_IF_DOCKED |
|
|
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET |
|
|
TASK_FLAG_RESTART_ON_IDLE_RESUME |
|
|
TASK_FLAG_SYSTEM_REQUIRED))
|
|
{
|
|
//
|
|
// If any flag that could affect the CRun objects in the wait
|
|
// list has changed, signal a wait list rebuild.
|
|
// (Omitted flags: TASK_FLAG_HIDDEN, TASK_FLAG_INTERACTIVE,
|
|
// TASK_FLAG_DELETE_WHEN_DONE)
|
|
// CODEWORK Possible optimization: Omit some more flags and
|
|
// defer reading their settings into the CRun object until the
|
|
// time of running the job
|
|
//
|
|
SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
|
|
}
|
|
|
|
//
|
|
// Only set the lower word of the internal flag property. The upper word
|
|
// is reserved for internal use.
|
|
//
|
|
rgFlags &= ~JOB_INTERNAL_FLAG_MASK;
|
|
m_rgFlags &= JOB_INTERNAL_FLAG_MASK;
|
|
SetFlag(rgFlags);
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetFlags
|
|
//
|
|
// Synopsis: Get the bit flags for the various boolean properties
|
|
//
|
|
// Arguments: [prgFlags] - returned value placed here
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetFlags(DWORD * prgFlags)
|
|
{
|
|
TRACE3(CJob, GetFlags)
|
|
|
|
//
|
|
// Only return the lower word of the internal flag property. The upper
|
|
// word is reserved for internal use.
|
|
// Also return whether this is an At job.
|
|
//
|
|
*prgFlags = m_rgFlags & (~JOB_INTERNAL_FLAG_MASK | JOB_I_FLAG_NET_SCHEDULE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetTaskFlags
|
|
//
|
|
// Synopsis: Sets the job's task flags.
|
|
//
|
|
// Arguments: [dwFlags] - flags to be set.
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetTaskFlags(DWORD dwFlags)
|
|
{
|
|
TRACE3(CJob, SetTaskFlags)
|
|
|
|
//
|
|
// Only set the lower word of the internal flag property. The upper word
|
|
// is reserved for internal use.
|
|
// BUGBUG return an error on invalid flag bits
|
|
//
|
|
m_rgTaskFlags = (m_rgTaskFlags & JOB_INTERNAL_FLAG_MASK) |
|
|
(dwFlags & ~JOB_INTERNAL_FLAG_MASK);
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetTaskFlags
|
|
//
|
|
// Synopsis: Returns the job's task flags.
|
|
//
|
|
// Arguments: [pdwFlags] - return value pointer.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetTaskFlags(DWORD * pdwFlags)
|
|
{
|
|
TRACE3(CJob, GetTaskFlags)
|
|
|
|
//
|
|
// Only return the lower word of the internal flag property. The upper
|
|
// word is reserved for internal use.
|
|
//
|
|
*pdwFlags = m_rgTaskFlags & ~JOB_INTERNAL_FLAG_MASK;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetWorkItemData
|
|
//
|
|
// Synopsis: Sets the task data. Provides optional, per-task, binary
|
|
// storage for the caller.
|
|
//
|
|
// Arguments: [cbData] -- number of bytes in buffer.
|
|
// [rgbData] -- buffer of data to copy.
|
|
//
|
|
// Returns: S_OK
|
|
// E_INVALIDARG
|
|
// E_OUTOFMEMORY
|
|
//
|
|
// Notes: The buffer is caller allocated and freed.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetWorkItemData(WORD cbData, BYTE rgbData[])
|
|
{
|
|
TRACE3(CJob, SetWorkItemData)
|
|
|
|
if ((cbData != 0 && rgbData == NULL) ||
|
|
cbData == 0 && rgbData != NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
BYTE * pbData;
|
|
|
|
if (cbData)
|
|
{
|
|
pbData = new BYTE[cbData];
|
|
|
|
if (pbData == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
CopyMemory(pbData, rgbData, cbData);
|
|
}
|
|
else
|
|
{
|
|
pbData = NULL;
|
|
}
|
|
|
|
DELETE_CJOB_FIELD(m_pbTaskData);
|
|
m_pbTaskData = pbData;
|
|
m_cbTaskData = cbData;
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetWorkItemData
|
|
//
|
|
// Synopsis: Gets the task data.
|
|
//
|
|
// Arguments: [pcbData] -- returns the number of bytes in buffer.
|
|
// [prgbData] -- returns the buffer of data.
|
|
//
|
|
// Returns: S_OK for success.
|
|
// E_INVALIDARG
|
|
// E_OUTOFMEMORY.
|
|
//
|
|
// Notes: The buffer is callee allocated and caller freed with
|
|
// CoTaskMemFree. If there is no user data, then *pcBytes is set
|
|
// to zero and *ppBytes is set to NULL.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetWorkItemData(PWORD pcbData, PBYTE * prgbData)
|
|
{
|
|
TRACE3(CJob, GetWorkItemData)
|
|
|
|
if (m_pbTaskData != NULL)
|
|
{
|
|
*prgbData = (PBYTE)CoTaskMemAlloc(m_cbTaskData);
|
|
|
|
if (*prgbData == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
CopyMemory(*prgbData, m_pbTaskData, m_cbTaskData);
|
|
*pcbData = m_cbTaskData;
|
|
}
|
|
else
|
|
{
|
|
*pcbData = 0;
|
|
*prgbData = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetMostRecentRunTime
|
|
//
|
|
// Synopsis: Returns the time that the job last ran.
|
|
//
|
|
// Arguments: [pstLastRun] - value returned here.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetMostRecentRunTime(SYSTEMTIME * pstLastRun)
|
|
{
|
|
TRACE3(CJob, GetLastRunTime)
|
|
|
|
*pstLastRun = m_stMostRecentRunTime;
|
|
|
|
if (m_stMostRecentRunTime.wYear == 0)
|
|
{
|
|
//
|
|
// Job has never run if last-run-time property is null
|
|
// (all elements will be zero if null, testing year is
|
|
// sufficient).
|
|
//
|
|
return SCHED_S_TASK_HAS_NOT_RUN;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetNextRunTime
|
|
//
|
|
// Synopsis: Returns the next time that the job is scheduled to run.
|
|
//
|
|
// Arguments: [pstNextRun] - pointer to return value through
|
|
//
|
|
// Returns: S_OK: the next run time was returned.
|
|
// S_FALSE: there are no more scheduled runs.
|
|
// SCHED_S_EVENT_TRIGGER: there are only event triggers.
|
|
// SCHED_S_TASK_NO_VALID_TRIGGERS: either there are no triggers or
|
|
// the triggers are disabled or not set.
|
|
// HRESULT - failure code.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetNextRunTime(SYSTEMTIME * pstNextRun)
|
|
{
|
|
TRACE3(CJob, GetNextRunTime)
|
|
|
|
HRESULT hr;
|
|
SYSTEMTIME stNow, stEmpty = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
GetLocalTime(&stNow);
|
|
|
|
CTimeRunList RunList;
|
|
|
|
WORD cRuns = 0;
|
|
|
|
hr = GetRunTimesP(&stNow, NULL, &cRuns, 1, &RunList, NULL);
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
*pstNextRun = stEmpty;
|
|
return hr;
|
|
}
|
|
else if (cRuns == 0)
|
|
{
|
|
*pstNextRun = stEmpty;
|
|
return S_FALSE;
|
|
}
|
|
|
|
FILETIME ft;
|
|
RunList.PeekHeadTime(&ft);
|
|
|
|
if (!FileTimeToSystemTime(&ft, pstNextRun))
|
|
{
|
|
*pstNextRun = stEmpty;
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetExitCode
|
|
//
|
|
// Synopsis: Returns the exit code of the jobs last run.
|
|
//
|
|
// Arguments: [pExitCode] - return value pointer.
|
|
//
|
|
// Returns: HRESULT - error from the last attempt to start the job.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetExitCode(DWORD * pExitCode)
|
|
{
|
|
TRACE3(CJob, GetExitCode)
|
|
|
|
*pExitCode = m_ExitCode;
|
|
|
|
return m_hrStartError;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetStatus
|
|
//
|
|
// Synopsis: Returns the current status of this job object.
|
|
//
|
|
// Arguments: [phrStatus] - yup, the return value.
|
|
//
|
|
// Returns: HRESULTS
|
|
//
|
|
// Notes: The running status is implicit in a non-zero instance count.
|
|
// Otherwise, return the status property. Note also that the
|
|
// job's status and other properties that are obtained by the
|
|
// load from disk are a snapshot that can become stale.
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetStatus(HRESULT * phrStatus)
|
|
{
|
|
TRACE3(CJob, GetStatus)
|
|
|
|
if (m_cRunningInstances > 0)
|
|
{
|
|
*phrStatus = SCHED_S_TASK_RUNNING;
|
|
}
|
|
else
|
|
{
|
|
*phrStatus = m_hrStatus;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::SetCreator
|
|
//
|
|
// Synopsis: Set the creator property.
|
|
//
|
|
// Arguments: [pwszCreator] -- Creator string.
|
|
//
|
|
// Returns: S_OK
|
|
// E_INVALIDARG
|
|
// E_OUTOFMEMORY
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::SetCreator(LPCWSTR pwszCreator)
|
|
{
|
|
TRACE3(CJob, SetCreator);
|
|
|
|
LPWSTR pwsz;
|
|
|
|
//
|
|
// Handle empty string.
|
|
//
|
|
|
|
if (*pwszCreator)
|
|
{
|
|
pwsz = new WCHAR[wcslen(pwszCreator) + 1];
|
|
|
|
if (pwsz == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(pwsz, wcslen(pwszCreator) + 1, pwszCreator);
|
|
}
|
|
else
|
|
{
|
|
pwsz = NULL;
|
|
}
|
|
|
|
DELETE_CJOB_FIELD(m_pwszCreator);
|
|
|
|
m_pwszCreator = pwsz;
|
|
|
|
SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CJob::ITask::GetCreator
|
|
//
|
|
// Synopsis: Fetch the creator property.
|
|
//
|
|
// Arguments: [ppwszCreator] -- Returned string.
|
|
//
|
|
// Returns: S_OK
|
|
// E_INVALIDARG
|
|
// E_OUTOFMEMORY
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CJob::GetCreator(LPWSTR * ppwszCreator)
|
|
{
|
|
TRACE3(CJob, GetCreator);
|
|
|
|
//
|
|
// Handle empty string.
|
|
//
|
|
|
|
LPWSTR pwsz = (m_pwszCreator == NULL ? wszEmpty : m_pwszCreator);
|
|
|
|
*ppwszCreator = (LPWSTR)CoTaskMemAlloc(
|
|
(wcslen(pwsz) + 1) * sizeof(WCHAR));
|
|
|
|
if (*ppwszCreator == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
StringCchCopy(*ppwszCreator, wcslen(pwsz) + 1, pwsz);
|
|
|
|
return S_OK;
|
|
}
|