//+---------------------------------------------------------------------------- // // 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