//+---------------------------------------------------------------------------- // // 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 // //----------------------------------------------------------------------------- #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; } if (!rgpwszNames) { 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; } pwszName = ptszName; size_t cch = wcslen(pwszName) + 1; **rgpwszNames = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR)); if (**rgpwszNames == NULL) { if (pcJobsFetched != NULL) { *pcJobsFetched = 0; } CoTaskMemFree(*rgpwszNames); *rgpwszNames = NULL; return E_OUTOFMEMORY; } StringCchCopy(**rgpwszNames, cch, 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); pwszName = ptszName; cch = wcslen(pwszName) + 1; (*rgpwszNames)[i - 1] = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR)); if ((*rgpwszNames)[i - 1] == NULL) { hr = E_OUTOFMEMORY; break; } StringCchCopy((*rgpwszNames)[i - 1], cch, 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, MAX_PATH + 1); if (hr == S_FALSE) { // we're done // m_fFindOverrun = TRUE; return S_FALSE; } TCHAR tszFullDirPath[MAX_PATH + 1]; StringCchCopy(tszFullDirPath, MAX_PATH + 1, m_ptszFolderPath); StringCchCat(tszFullDirPath, MAX_PATH + 1, 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); *pptszName = new TCHAR[cch + 1]; if (*pptszName == NULL) { return E_OUTOFMEMORY; } StringCchCopy(*pptszName, cch + 1, FindData.cFileName); } 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; 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; } StringCchCopy(m_pdsHead->tszDir, MAX_PATH + 1, 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); if (!ppEnumJobs) { return E_INVALIDARG; } 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; } size_t cch = lstrlen(ptszFolderPath) + 1; m_ptszFolderPath = new TCHAR[cch]; if (!m_ptszFolderPath) { return E_OUTOFMEMORY; } StringCchCopy(m_ptszFolderPath, cch, ptszFolderPath); m_pdsHead = new DIRSTACK; if (m_pdsHead == NULL) { return E_OUTOFMEMORY; } StringCchCopy(m_pdsHead->tszDir, MAX_PATH + 1, TEXT(".")); StringCchCopy(m_tszJobExt, SCH_SMBUF_LEN, TEXT(".") TSZ_JOB); 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::PopDir, private // // Synopsis: Pops the head element off of the dir stack. // //----------------------------------------------------------------------------- HRESULT CEnumJobs::PopDir(LPTSTR ptszDir, size_t cchBuff) { if (m_pdsHead == NULL) { return S_FALSE; } StringCchCopy(ptszDir, cchBuff, 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 (!ppvObject) { return E_INVALIDARG; } 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