Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

841 lines
21 KiB

//+----------------------------------------------------------------------------
//
// Job Scheduler Service
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: job_enum.cxx
//
// Contents: job object enumerator implementation
//
// Classes: CEnumJobs
//
// Interfaces: IEnumWorkItems
//
// History: 13-Sep-95 EricB created
//
// Notes: Recursion into subdirs is currently disabled. To reenable,
// define ENUM_SUBDIRS and recompile.
//-----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include "Sched.hxx"
void FreeStrings(LPWSTR * rgpwszNames, int n);
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::IEnumWorkItems::Next, public
//
// Synopsis: Returns the indicated number of job object monikers
//
// Arguments: [cJobs] - the number of jobs to return
// [rgpwszNames] - the array of returned job names
// [pcJobsFetched] - the actual number of jobs returned; can be
// less than or equal to cJobs. Can be NULL if
// cJobs is equal to one.
//
// Returns: S_OK - returned requested number of job names
// S_FALSE - returned less than requested number of names because
// the end of the enumeration sequence was reached.
// E_INVALIDARG or E_OUTOFMEMORY - s.b. obvious
//
// Notes: Each LPWSTR in the array must be caller freed using
// CoTaskMemFree and then the array itself must be freed
// CoTaskMemFree.
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CEnumJobs::Next(ULONG cJobs, LPWSTR ** rgpwszNames, ULONG * pcJobsFetched)
{
HRESULT hr = S_OK;
if (cJobs == 0)
{
return E_INVALIDARG;
}
if (cJobs > 1 && pcJobsFetched == NULL)
{
// as required by IEnumX spec.
//
return E_INVALIDARG;
}
*rgpwszNames = NULL;
if (m_fFindOverrun)
{
if (pcJobsFetched != NULL)
{
*pcJobsFetched = 0;
}
return S_FALSE;
}
TCHAR * ptszName;
WCHAR * pwszName;
//
// find the first requested
//
hr = GetNext(&ptszName);
if (hr != S_OK)
{
if (pcJobsFetched != NULL)
{
*pcJobsFetched = 0;
}
*rgpwszNames = NULL;
return hr;
}
//
// allocate the first job object name string and the pointer to it
//
*rgpwszNames = (LPWSTR *)CoTaskMemAlloc(sizeof(LPWSTR *));
if (*rgpwszNames == NULL)
{
if (pcJobsFetched != NULL)
{
*pcJobsFetched = 0;
}
return E_OUTOFMEMORY;
}
#if defined(UNICODE)
pwszName = ptszName;
#else
WCHAR wszName[MAX_PATH];
hr = AnsiToUnicode(wszName, ptszName, MAX_PATH);
if (FAILED(hr))
{
delete ptszName;
CoTaskMemFree(*rgpwszNames);
if (pcJobsFetched)
{
*pcJobsFetched = 0;
}
*rgpwszNames = NULL;
return hr;
}
pwszName = wszName;
#endif
**rgpwszNames =
(LPWSTR)CoTaskMemAlloc((wcslen(pwszName) + 1) * sizeof(WCHAR));
if (**rgpwszNames == NULL)
{
if (pcJobsFetched != NULL)
{
*pcJobsFetched = 0;
}
CoTaskMemFree(*rgpwszNames);
*rgpwszNames = NULL;
return E_OUTOFMEMORY;
}
wcscpy(**rgpwszNames, pwszName);
delete ptszName;
if (cJobs == 1)
{
if (pcJobsFetched != NULL)
{
*pcJobsFetched = 1;
}
return S_OK;
}
//
// Note a check at entry guarantees that at this point pcJobsFetched !=
// NULL.
//
ULONG i = 1;
//
// find the rest requested
//
while (++i <= cJobs)
{
hr = GetNext(&ptszName);
if (hr != S_OK)
{
//
// Either hr == S_FALSE and we've completed successfully because
// there are no more jobs to enumerate, or else hr is a
// failure code and we must bail.
//
break;
}
LPWSTR * rgpwszTmp = *rgpwszNames;
*rgpwszNames = (LPWSTR *)CoTaskMemAlloc(sizeof(LPWSTR *) * i);
if (*rgpwszNames == NULL)
{
*rgpwszNames = rgpwszTmp; // so cleanup will free strings
hr = E_OUTOFMEMORY;
break;
}
memcpy(*rgpwszNames, rgpwszTmp, sizeof(LPWSTR *) * (i - 1));
CoTaskMemFree(rgpwszTmp);
#if defined(UNICODE)
pwszName = ptszName;
#else
hr = AnsiToUnicode(wszName, ptszName, MAX_PATH);
if (FAILED(hr))
{
break;
}
#endif
(*rgpwszNames)[i - 1] =
(LPWSTR)CoTaskMemAlloc((wcslen(pwszName) + 1) * sizeof(WCHAR));
if ((*rgpwszNames)[i - 1] == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
wcscpy((*rgpwszNames)[i - 1], pwszName);
delete ptszName;
ptszName = NULL;
}
if (FAILED(hr))
{
FreeStrings(*rgpwszNames, i - 1);
delete ptszName;
*pcJobsFetched = 0;
*rgpwszNames = NULL;
}
else
{
*pcJobsFetched = --i;
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::GetNext, private
//
// Synopsis: enumeration helper
//
// Arguments: [pptszName] - the job/queue name, relative to the jobs folder
//
// Returns: S_OK - found next file
// S_FALSE - the end of the enumeration sequence was reached.
// other code - file system or memory error
//
//-----------------------------------------------------------------------------
HRESULT
CEnumJobs::GetNext(LPTSTR * pptszName)
{
HRESULT hr = S_OK;
DWORD dwRet = NO_ERROR;
WIN32_FIND_DATA FindData;
//
// loop until either a matching file is found or the search is done
//
do
{
//
// if the find handle is invalid, then we need to start a find in the
// next directory (which may in fact be the first directory)
//
if (m_hFind == INVALID_HANDLE_VALUE)
{
//
// Note that, unless ENUM_SUBDIRS is defined, PopDir returns the
// string "." the first time it is called and returns S_FALSE the
// second time to stop the enumeration.
//
hr = PopDir(m_tszCurDir);
if (hr == S_FALSE)
{
// we're done
//
m_fFindOverrun = TRUE;
return S_FALSE;
}
TCHAR tszFullDirPath[MAX_PATH];
lstrcpy(tszFullDirPath, m_ptszFolderPath);
#if defined(ENUM_SUBDIRS)
//
// Note that, unless ENUM_SUBDIRS is defined, that m_tszCurDir is
// always ".", so it can be ignored here.
//
lstrcat(tszFullDirPath, TEXT("\\"));
lstrcat(tszFullDirPath, m_tszCurDir);
#endif // ENUM_SUBDIRS
lstrcat(tszFullDirPath, TEXT("\\*"));
m_hFind = FindFirstFile(tszFullDirPath, &FindData);
if (m_hFind == INVALID_HANDLE_VALUE)
{
dwRet = GetLastError();
if (dwRet == ERROR_FILE_NOT_FOUND)
{ // no files in the current dir, check the next dir.
continue;
}
else
{
return HRESULT_FROM_WIN32(dwRet);
}
}
hr = CheckFound(&FindData);
if (hr == S_OK)
{ // match found
break;
}
if (hr != S_FALSE)
{ // an error condition
return hr;
}
}
//
// Continue looking at files in the current dir until a job/queue has
// been found or the dir has been scanned. If the former, break out of
// both loops. If the latter, break out of the inner loop and then
// restart the search on the next dir.
//
do
{
if (!FindNextFile(m_hFind, &FindData))
{
dwRet = GetLastError();
if (dwRet == ERROR_NO_MORE_FILES)
{
FindClose(m_hFind);
m_hFind = INVALID_HANDLE_VALUE;
hr = S_FALSE;
break;
}
else
{
return HRESULT_FROM_WIN32(dwRet);
}
}
hr = CheckFound(&FindData);
if (hr == S_OK)
{ // match found
break;
}
if (hr != S_FALSE)
{ // an error condition
return hr;
}
} while (hr != S_OK);
} while (hr != S_OK);
if (pptszName != NULL && dwRet == NO_ERROR)
{
int cch = lstrlen(FindData.cFileName);
#if defined(ENUM_SUBDIRS)
//
// special case: the initial DirStack element is "." so that the
// job scheduler folder is searched first before decending into
// subdirs - don't add ".\" as a dir to the path
//
if (m_tszCurDir[0] == TEXT('.'))
{
#endif // ENUM_SUBDIRS
*pptszName = new TCHAR[cch + 1];
if (*pptszName == NULL)
{
return E_OUTOFMEMORY;
}
lstrcpy(*pptszName, FindData.cFileName);
#if defined(ENUM_SUBDIRS)
}
else
{
cch += lstrlen(m_tszCurDir) + 1;
*pptszName = new TCHAR[cch + 1];
if (*pptszName == NULL)
{
return E_OUTOFMEMORY;
}
lstrcpy(*pptszName, m_tszCurDir);
lstrcat(*pptszName, TEXT("\\"));
lstrcat(*pptszName, FindData.cFileName);
}
#endif // ENUM_SUBDIRS
}
m_cFound++;
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::CheckFound, private
//
// Synopsis: Checks if the found file is a job or queue. If it is a
// directory, push it onto the dir stack.
//
// Returns: S_OK if a job or queue, S_FALSE if not.
//
// Notes: The file find functions match on both long and short versions
// of file names, so all names of the form *.job* will match
// (a file like foo.jobber will have a short name of foo~1.job).
// Thus, the returned file names must be checked for an exact
// extension match.
//-----------------------------------------------------------------------------
HRESULT
CEnumJobs::CheckFound(LPWIN32_FIND_DATA pFindData)
{
HRESULT hr;
#if defined(ENUM_SUBDIRS)
if (pFindData->dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
{
if (pFindData->cFileName[0] != TEXT('.')) // don't push '.' and '..'
{
hr = PushDir(pFindData->cFileName);
if (FAILED(hr))
{
ERR_OUT("CheckFound: PushDir", hr);
return hr;
}
}
return S_FALSE;
}
#endif // ENUM_SUBDIRS
TCHAR * ptszExt = _tcsrchr(pFindData->cFileName, TEXT('.'));
if (ptszExt)
{
if (lstrcmpi(ptszExt, m_tszJobExt) == 0)
// || lstrcmpi(ptszExt, m_tszQueExt) == 0
{
return S_OK;
}
}
return S_FALSE;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::IEnumWorkItems::Skip, public
//
// Synopsis: Skips the indicated number of jobs in the enumeration
//
// Arguments: [cJobs] - the number of jobs to skip
//
// Returns: S_OK - skipped requested number of job names
// S_FALSE - skipped less than requested number of names because
// the end of the enumeration sequence was reached.
// E_INVALIDARG - if cJobs == 0
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CEnumJobs::Skip(ULONG cJobs)
{
if (cJobs == 0)
{
return E_INVALIDARG;
}
if (m_fFindOverrun)
{
return S_FALSE;
}
HRESULT hr = S_OK;
//
// skip the requested number
//
for (ULONG i = 1; i <= cJobs; i++)
{
hr = GetNext(NULL);
if (hr != S_OK)
{
return hr;
}
}
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::IEnumWorkItems::Reset, public
//
// Synopsis: Sets the enumerator back to its original state
//
// Returns: hresults
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CEnumJobs::Reset(void)
{
if (m_hFind != INVALID_HANDLE_VALUE)
{
FindClose(m_hFind);
m_hFind = INVALID_HANDLE_VALUE;
}
m_fFindOverrun = FALSE;
m_cFound = 0;
ClearDirStack();
m_pdsHead = new DIRSTACK;
if (m_pdsHead == NULL)
{
return E_OUTOFMEMORY;
}
lstrcpy(m_pdsHead->tszDir, TEXT("."));
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::IEnumWorkItems::Clone, public
//
// Synopsis: Creates a copy of the enumerator object with the same state
//
// Arguments: [ppEnumJobs] - a place to return a pointer to the enum object
//
// Returns: hresults
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CEnumJobs::Clone(IEnumWorkItems ** ppEnumJobs)
{
TRACE(CEnumJobs, Clone);
HRESULT hr;
CEnumJobs * pEnumJobs = new CEnumJobs;
if (pEnumJobs == NULL)
{
hr = E_OUTOFMEMORY;
goto ErrCleanup;
}
hr = pEnumJobs->Init(m_ptszFolderPath);
if (FAILED(hr))
{
goto ErrCleanup;
}
if (m_cFound > 0)
{
hr = pEnumJobs->Skip(m_cFound);
if (FAILED(hr))
{
goto ErrCleanup;
}
}
*ppEnumJobs = pEnumJobs;
return S_OK;
ErrCleanup:
delete pEnumJobs;
*ppEnumJobs = NULL;
return hr;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::Init, protected
//
// Synopsis: Initializes the enumeration
//
// Returns: hresults
//
// Notes: Initialization is not done during construction since the only
// way to return ctor errors is to throw an exception.
//-----------------------------------------------------------------------------
HRESULT
CEnumJobs::Init(TCHAR * ptszFolderPath)
{
TRACE(CEnumJobs, Init);
if (ptszFolderPath == NULL)
{
return E_FAIL;
}
m_ptszFolderPath = new TCHAR[lstrlen(ptszFolderPath) + 1];
if (!m_ptszFolderPath)
{
return E_OUTOFMEMORY;
}
lstrcpy(m_ptszFolderPath, ptszFolderPath);
m_pdsHead = new DIRSTACK;
if (m_pdsHead == NULL)
{
return E_OUTOFMEMORY;
}
lstrcpy(m_pdsHead->tszDir, TEXT("."));
lstrcpy(m_tszJobExt, TEXT(".") TSZ_JOB);
// lstrcpy(m_tszQueExt, TEXT(".") g_tszQueueExt);
m_fInitialized = TRUE;
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::CEnumJobs
//
// Synopsis: constructor
//
//-----------------------------------------------------------------------------
CEnumJobs::CEnumJobs(void) :
m_hFind(INVALID_HANDLE_VALUE),
m_pdsHead(NULL),
m_ptszFolderPath(NULL),
m_cFound(0),
m_fInitialized(FALSE),
m_fFindOverrun(FALSE),
m_uRefs(1)
{
m_tszCurDir[0] = TEXT('\0');
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::~CEnumJobs
//
// Synopsis: destructor
//
//-----------------------------------------------------------------------------
CEnumJobs::~CEnumJobs(void)
{
if (m_hFind != INVALID_HANDLE_VALUE)
{
FindClose(m_hFind);
}
ClearDirStack();
if (m_ptszFolderPath)
{
delete m_ptszFolderPath;
}
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::PushDir, private
//
// Synopsis: Pushes a new directory element on the dir stack. The dir stack
// stores directory path segments that are rooted relative to the
// jobs directory.
//
//-----------------------------------------------------------------------------
HRESULT
CEnumJobs::PushDir(LPCTSTR ptszDir)
{
#if defined(ENUM_SUBDIRS)
PDIRSTACK pdsNode = new DIRSTACK;
if (pdsNode == NULL)
{
return E_OUTOFMEMORY;
}
//
// compose relative path
// special case: the initial DirStack element is "." so that the
// job scheduler folder is searched first before decending into
// subdirs - don't add ".\" as a dir to the path
//
if (m_tszCurDir[0] == TEXT('.'))
{
lstrcpy(pdsNode->tszDir, ptszDir);
}
else
{
lstrcpy(pdsNode->tszDir, m_tszCurDir);
lstrcat(pdsNode->tszDir, TEXT("\\"));
lstrcat(pdsNode->tszDir, ptszDir);
}
pdsNode->pdsNext = m_pdsHead;
m_pdsHead = pdsNode;
#endif // ENUM_SUBDIRS
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::PopDir, private
//
// Synopsis: Pops the head element off of the dir stack.
//
//-----------------------------------------------------------------------------
HRESULT
CEnumJobs::PopDir(LPTSTR ptszDir)
{
if (m_pdsHead == NULL)
{
return S_FALSE;
}
lstrcpy(ptszDir, m_pdsHead->tszDir);
PDIRSTACK pdsNode = m_pdsHead->pdsNext;
delete m_pdsHead;
m_pdsHead = pdsNode;
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::ClearDirStack, private
//
// Synopsis: free the stack element memory
//
//-----------------------------------------------------------------------------
void
CEnumJobs::ClearDirStack(void)
{
if (m_pdsHead != NULL)
{
PDIRSTACK pdsNode, pdsNextNode;
pdsNode = m_pdsHead;
do
{
pdsNextNode = pdsNode->pdsNext;
delete pdsNode;
pdsNode = pdsNextNode;
} while (pdsNode);
m_pdsHead = NULL;
}
}
//+----------------------------------------------------------------------------
//
// Function: FreeStrings
//
// Synopsis: Frees the strings contained in the array and then the array
// itself.
//
// Arguments: [rgpwszNames] - the array of strings.
// [n] - the array size.
//
//-----------------------------------------------------------------------------
void
FreeStrings(LPWSTR * rgpwszNames, int n)
{
for (int i = 0; i < n; i++)
{
CoTaskMemFree(rgpwszNames[i]);
}
CoTaskMemFree(rgpwszNames);
}
//+----------------------------------------------------------------------------
//
// CEnumJobs IUnknown methods
//
//-----------------------------------------------------------------------------
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::IUnknown::QueryInterface
//
// Synopsis: Returns requested interface pointer
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CEnumJobs::QueryInterface(REFIID riid, void ** ppvObject)
{
if (IID_IUnknown == riid)
{
*ppvObject = (IUnknown *)(IEnumWorkItems *)this;
}
else if (IID_IEnumWorkItems == riid)
{
*ppvObject = (IUnknown *)(IEnumWorkItems *)this;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::IUnknown::AddRef
//
// Synopsis: increments reference count
//
// Returns: the reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CEnumJobs::AddRef(void)
{
return InterlockedIncrement((long *)&m_uRefs);
}
//+----------------------------------------------------------------------------
//
// Member: CEnumJobs::IUnknown::Release
//
// Synopsis: Decrements the object's reference count and frees it when
// no longer referenced.
//
// Returns: zero if the reference count is zero or non-zero otherwise
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CEnumJobs::Release(void)
{
unsigned long uTmp;
if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
{
delete this;
}
return uTmp;
}
// BUGBUG: need a class factory if the interface is going to be exposed to OA