|
|
//+----------------------------------------------------------------------------
//
// Job Scheduler Service
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: sch_itf.cxx
//
// Contents: job scheduler service interface impementation
//
// Classes: CSchedule
//
// Interfaces: ITaskScheduler
//
// History: 08-Sep-95 EricB created
//
//-----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include "Sched.hxx"
#include "..\inc\misc.hxx"
#include "..\inc\network.hxx"
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::GetTargetComputer, public
//
// Synopsis: Returns the name of the machine towards which the interface is
// currently targetted.
//
// Arguments: [ppwszComputer] - the returned buffer with the machine name
//
// Returns: hresults
//
// Notes: The string is callee allocated and caller freed with
// CoTaskMemFree.
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::GetTargetComputer(LPWSTR * ppwszComputer) { TRACE(CSchedule, GetTargetComputer);
HRESULT hr; DWORD cch = SA_MAX_COMPUTERNAME_LENGTH + 1; TCHAR tszLocalName[SA_MAX_COMPUTERNAME_LENGTH + 3] = TEXT("\\\\"); TCHAR * ptszTargetMachine; WCHAR * pwszTargetMachine;
if (m_ptszTargetMachine) { ptszTargetMachine = m_ptszTargetMachine; cch = lstrlen(ptszTargetMachine) + 1; } else // A NULL m_ptszTargetMachine means that we are targetted locally
{ if (!GetComputerName(tszLocalName + 2, &cch)) { hr = HRESULT_FROM_WIN32(GetLastError()); ERR_OUT("GetTargetComputer: GetComputerName", hr); return hr; }
ptszTargetMachine = tszLocalName; cch += 3; // 2 for the leading slashes + 1 for the NULL
}
pwszTargetMachine = ptszTargetMachine;
*ppwszComputer = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR));
if (*ppwszComputer == NULL) { return E_OUTOFMEMORY; }
s_wcscpy(*ppwszComputer, pwszTargetMachine);
return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::SetTargetComputer, public
//
// Synopsis: Sets the machine towards which subsequent ITaskScheduler
// calls will be directed
//
// Arguments: [pwszComputer] - the machine name string
//
// Returns: hresults
//
// Notes: The string is Caller allocated and freed. The machine name
// must include two leading backslashes.
// The caller may indicate using the local machine in one of two
// ways: by setting pwszComputer to NULL or to the UNC name of the
// local machine.
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::SetTargetComputer(LPCWSTR pwszComputer) { TRACE(CSchedule, SetTargetComputer); HRESULT hr; DWORD cch; BOOL fLocal = FALSE; //
// Parameter validation. A null param means to target the local computer.
//
if (!pwszComputer) { fLocal = TRUE; }
LPCTSTR tszPassedInName = pwszComputer; if (!fLocal) { //
// Get the local machine name to compare with that passed in.
//
TCHAR tszLocalName[SA_MAX_COMPUTERNAME_LENGTH + 1]; cch = SA_MAX_COMPUTERNAME_LENGTH + 1; if (!GetComputerName(tszLocalName, &cch)) { hr = HRESULT_FROM_WIN32(GetLastError()); ERR_OUT("SetTargetComputer: GetComputerName", hr); return hr; }
TCHAR tszFQDN[SA_MAX_COMPUTERNAME_LENGTH + 1]; cch = SA_MAX_COMPUTERNAME_LENGTH + 1; if (!GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, tszFQDN, &cch)) { hr = HRESULT_FROM_WIN32(GetLastError()); ERR_OUT("SetTargetComputer: GetComputerNameEx", hr); return hr; }
//
// skip over first two characters ("\\") of tszPassedInName when comparing
//
fLocal = (lstrcmpi(tszPassedInName + 2, tszLocalName) == 0) || (lstrcmpi(tszPassedInName + 2, tszFQDN) == 0); }
//
// If targeted remotely, get the folder path out of that machine's
// registry.
//
TCHAR tszFolderPath[MAX_PATH]; if (!fLocal) { //
// Open the remote registry.
//
long lErr; HKEY hRemoteKey, hSchedKey;
lErr = RegConnectRegistry(tszPassedInName, HKEY_LOCAL_MACHINE, &hRemoteKey); if (lErr != ERROR_SUCCESS) { schDebugOut((DEB_ERROR, "SetTargetComputer: RegConnectRegistry " "failed with error %ld\n", lErr)); return(HRESULT_FROM_WIN32(lErr)); }
lErr = RegOpenKeyEx(hRemoteKey, SCH_AGENT_KEY, 0, KEY_READ, &hSchedKey); if (lErr != ERROR_SUCCESS) { RegCloseKey(hRemoteKey);
if (lErr == ERROR_BADKEY || lErr == ERROR_FILE_NOT_FOUND) { return SCHED_E_SERVICE_NOT_INSTALLED; }
schDebugOut((DEB_ERROR, "SetTargetComputer: RegOpenKeyEx " "of Scheduler key failed with error %ld\n", lErr)); return HRESULT_FROM_WIN32(lErr); } //
// Get the jobs folder location from the remote registry.
//
DWORD cb = MAX_PATH * sizeof(TCHAR); TCHAR tszRegFolderValue[MAX_PATH]; lErr = RegQueryValueEx(hSchedKey, SCH_FOLDER_VALUE, NULL, NULL, (LPBYTE)tszRegFolderValue, &cb); if (lErr != ERROR_SUCCESS) { // use default if value absent
lstrcpy(tszRegFolderValue, TEXT("%SystemRoot%\\Tasks")); } RegCloseKey(hSchedKey);
//
// BUGBUG: temporary code to expand %SystemRoot% or %WinDir%
// The installer will have to write a full path to the registry 'cause
// expanding arbitrary environment strings remotely is too much work.
//
cch = ARRAY_LEN("%SystemRoot%") - 1; if (_tcsncicmp(tszRegFolderValue, TEXT("%SystemRoot%"), cch) != 0) { cch = ARRAY_LEN("%WinDir%") - 1; if (_tcsncicmp(tszRegFolderValue, TEXT("%WinDir%"), cch) != 0) { cch = 0; } }
if (cch != 0) { HKEY hCurVerKey; lErr = RegOpenKeyEx(hRemoteKey, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_ALL_ACCESS, &hCurVerKey); if (lErr != ERROR_SUCCESS) { RegCloseKey(hRemoteKey); schDebugOut((DEB_ERROR, "SetTargetComputer: RegOpenKeyEx " "of CurrentVersion key failed with error %ld\n", lErr)); return HRESULT_FROM_WIN32(lErr); } TCHAR tszSystemRoot[MAX_PATH]; cb = MAX_PATH * sizeof(TCHAR); lErr = RegQueryValueEx(hCurVerKey, TEXT("SystemRoot"), NULL, NULL, (LPBYTE)tszSystemRoot, &cb); if (lErr != ERROR_SUCCESS) { RegCloseKey(hCurVerKey); RegCloseKey(hRemoteKey); schDebugOut((DEB_ERROR, "SetTargetComputer: RegQueryValueEx " "of CurrentVersion key failed with error %ld\n", lErr)); return HRESULT_FROM_WIN32(lErr); } RegCloseKey(hCurVerKey); lstrcpy(tszFolderPath, tszSystemRoot); lstrcat(tszFolderPath, tszRegFolderValue + cch); } else { lstrcpy(tszFolderPath, tszRegFolderValue); } //
// end of temporary code to expand %SystemRoot%
//
RegCloseKey(hRemoteKey);
//
// Check the folder path for being a fully qualified path name where
// the first char is the drive designator and the second char is a
// colon.
//
if (!s_isDriveLetter(tszFolderPath[0]) || tszFolderPath[1] != TEXT(':')) { ERR_OUT("SetTargetComputer: registry path", ERROR_BAD_PATHNAME); return HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME); } //
// The UNC path to the job folder will be the result of concatonating
// the machine name and the expanded folder path. The drive designator
// in the folder path will be turned in an administrative share name
// by replacing the colon with a dollar sign and will look like:
// \\machine\c$\windir\jobs
// so that the count below includes the slash trailing the machine name
// plus the terminating null.
//
cch = lstrlen(tszPassedInName) + 1 + lstrlen(tszFolderPath) + 1; } else // Targetted locally.
{ //
// Use the local path. Include one for the null terminator.
//
cch = lstrlen(g_TasksFolderInfo.ptszPath) + 1; }
//
// Allocate the ITaskScheduler folder path string buffer.
//
TCHAR * ptszPathBuf = new TCHAR[cch]; if (!ptszPathBuf) { ERR_OUT("SetTargetComputer: Job folder path buffer allocation", E_OUTOFMEMORY); return E_OUTOFMEMORY; }
//
// Allocate the ITaskScheduler machine name string buffer.
//
TCHAR * ptszTargetMachine; if (!fLocal) { cch = lstrlen(tszPassedInName) + 1; ptszTargetMachine = new TCHAR[cch]; if (!ptszTargetMachine) { ERR_OUT("CSchedule::SetTargetComputer", E_OUTOFMEMORY); delete ptszPathBuf; return E_OUTOFMEMORY; } }
//
// Now that all failable operation have completed sucessfully, we can
// update the machine name and folder path members.
//
if (m_ptszTargetMachine) { delete m_ptszTargetMachine; }
if (m_ptszFolderPath) { delete m_ptszFolderPath; }
//
// Save the new machine name.
//
if (fLocal) { //
// If we are targetted locally, the machine name member is set to
// NULL.
//
m_ptszTargetMachine = NULL; } else { m_ptszTargetMachine = ptszTargetMachine; lstrcpy(m_ptszTargetMachine, tszPassedInName); }
//
// Save the folder path name.
//
m_ptszFolderPath = ptszPathBuf;
if (fLocal) { lstrcpy(m_ptszFolderPath, g_TasksFolderInfo.ptszPath); } else { //
// Convert the folder location to an UNC path.
//
// Turn the drive designator into the admin share by replacing the
// colon with the dollar sign.
//
tszFolderPath[1] = TEXT('$'); //
// Compose the UNC path.
//
lstrcpy(m_ptszFolderPath, tszPassedInName); lstrcat(m_ptszFolderPath, TEXT("\\")); lstrcat(m_ptszFolderPath, tszFolderPath); }
schDebugOut((DEB_ITRACE, "SetTargetComputer: path to sched folder: \"" FMT_TSTR "\"\n", m_ptszFolderPath));
return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::Enum, public
//
// Synopsis: Returns a job/queue object enumerator.
//
// Arguments: [ppEnumJobs] - a place to return a pointer to the enumerator
//
// Returns: hresults
//
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::Enum(IEnumWorkItems ** ppEnumJobs) { TRACE(CSchedule, Enum); HRESULT hr;
CEnumJobs * pEnumJobs = new CEnumJobs; if (pEnumJobs == NULL) { *ppEnumJobs = NULL; return E_OUTOFMEMORY; }
hr = pEnumJobs->Init(m_ptszFolderPath); if (FAILED(hr)) { delete pEnumJobs; *ppEnumJobs = NULL; }
*ppEnumJobs = pEnumJobs;
return hr; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::NewWorkItem, public
//
// Synopsis: Create a new job object.
//
// Arguments: [pwszJobName] - the name of the new job *REQUIRED*
// [riid] - the interface desired
// [ppunk] - a place to return a pointer to the new job object
//
// Returns: hresults
//
// Notes: ppwszJobName is caller allocated and freed. The CJob::Save
// method will copy it before returning. The job name must conform
// to NT file naming conventions but must not include
// [back]slashes because nesting within the job object folder is
// not allowed.
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::NewWorkItem(LPCWSTR pwszJobName, REFCLSID rclsid, REFIID riid, IUnknown ** ppunk) { TRACE(CSchedule, NewWorkItem);
*ppunk = NULL;
if (!IsEqualCLSID(rclsid, CLSID_CTask)) { return CLASS_E_CLASSNOTAVAILABLE; }
TCHAR * ptszFullName; HANDLE hFile;
HRESULT hr = CheckJobName(pwszJobName, &ptszFullName); if (FAILED(hr)) { ERR_OUT("CSchedule::NewWorkItem: CheckJobName", hr); return hr; }
CJob * pJob = CJob::Create(); if (pJob == NULL) { delete [] ptszFullName; return E_OUTOFMEMORY; }
//
// Do the QI before the CreateFile so that if the caller asks for a non-
// supported interface, the failure will not result in disk operations.
//
hr = pJob->QueryInterface(riid, (void **)ppunk); if (FAILED(hr)) { ERR_OUT("CSchedule::NewWorkItem: QueryInterface(riid)", hr); goto CleanExit; } // the above QI increased the refcount to 2, so set it back to 1
pJob->Release();
//
// Per the spec for this method, the file must not already exist.
//
hFile = CreateFile(ptszFullName, 0, // desired access: none
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode: all
NULL, // security attributes
OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) { DWORD dwErr = GetLastError();
if (dwErr == ERROR_FILE_NOT_FOUND) { //
// This is good. Save the new filename.
//
pJob->m_ptszFileName = ptszFullName; return S_OK; } else { hr = HRESULT_FROM_WIN32(dwErr); ERR_OUT("CSchedule::NewWorkItem: CreateFile", hr); } } else { //
// Opened successfully - the file exists
//
CloseHandle(hFile); hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS); ERR_OUT("CSchedule::NewWorkItem", hr); }
CleanExit:
delete [] ptszFullName; delete pJob; // on error, completely destroy the job object
*ppunk = NULL;
return hr; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::AddWorkItem, public
//
// Synopsis: Saves the job to the job scheduler folder.
//
// Arguments: [pwszJobName] - the name of the job *REQUIRED*
// [pJob] - pointer to the job object
//
// Returns: hresults
//
// Notes: Same job name conditions as above.
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::AddWorkItem(LPCWSTR pwszJobName, IScheduledWorkItem * pWorkItem) { TRACE(CSchedule, AddWorkItem); TCHAR * ptszFullName;
HRESULT hr = CheckJobName(pwszJobName, &ptszFullName); if (FAILED(hr)) { ERR_OUT("CSchedule::AddWorkItem: CheckJobName", hr); return hr; } IPersistFile * pFile;
hr = pWorkItem->QueryInterface(IID_IPersistFile, (void **)&pFile); if (FAILED(hr)) { ERR_OUT("CSchedule::AddWorkItem: QI(IPersistFile)", hr); delete [] ptszFullName; return hr; }
WCHAR * pwszName;
#if !defined(UNICODE)
int cch = lstrlen(ptszFullName) + 1; pwszName = new WCHAR[cch]; if (!pwszName) { ERR_OUT("CSchedule::AddWorkItem", E_OUTOFMEMORY); pFile->Release(); delete [] ptszFullName; return E_OUTOFMEMORY; }
MultiByteToWideChar(CP_ACP, 0, ptszFullName, -1, pwszName, cch);
#else
pwszName = ptszFullName;
#endif
hr = pFile->Save(pwszName, TRUE);
// Add this if we want nested folders
//
// if (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
// {
// //
// // Create folders as needed
// //
// hr = CreateFolders(ptszFullName, TRUE);
// if (FAILED(hr))
// {
// ERR_OUT("AddWorkItem: CreateFolders", hr);
// pFile->Release();
// delete [] ptszFullName;
// #if !defined(UNICODE)
// delete [] pwszName;
// #endif
// return hr;
// }
// //
// // Try again
// //
// hr = pFile->Save(pwszName, TRUE);
// }
pFile->Release(); delete [] ptszFullName; #if !defined(UNICODE)
delete [] pwszName; #endif
return hr; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::Delete, public
//
// Synopsis: Deletes the job/queue.
//
// Arguments: [pwszJobName] - indicates the job/queue to delete
//
// Returns: hresults
//
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::Delete(LPCWSTR pwszJobName) { TRACE(CSchedule, Delete); HRESULT hr; TCHAR * ptszFullName;
hr = CheckJobName(pwszJobName, &ptszFullName); if (FAILED(hr)) { ERR_OUT("CSchedule::Delete: CheckJobName", hr); return hr; }
if (!DeleteFile(ptszFullName)) { hr = HRESULT_FROM_WIN32(GetLastError()); ERR_OUT("CSchedule::Delete: DeleteFile", hr); delete ptszFullName; return hr; } delete ptszFullName; return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::Activate, public
//
// Synopsis: Given a valid name, returns a pointer to the activated job
// object
//
// Arguments: [pwszName] - the name of the job to activate
// [riid] - the interface to return
// [ppunk] - a pointer to the job object interface
//
// Returns: hresults
//
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::Activate(LPCWSTR pwszName, REFIID riid, IUnknown ** ppunk) { TRACE(CSchedule, Activate); TCHAR * ptszFullName; HRESULT hr = CheckJobName(pwszName, &ptszFullName); if (FAILED(hr)) { *ppunk = NULL; return hr; }
CJob * pJob;
//
// CJob is a single-use, in-proc handler, so no need to get OLE in the
// loop here. Use new (called by CJob::Create) instead of CoCreateInstance.
//
pJob = CJob::Create(); if (pJob == NULL) { *ppunk = NULL; delete [] ptszFullName; return E_OUTOFMEMORY; }
hr = pJob->LoadP(ptszFullName, 0, TRUE, TRUE);
delete [] ptszFullName;
if (FAILED(hr)) { ERR_OUT("CSchedule::Activate, Load", hr); *ppunk = NULL; pJob->Release(); // on error, completely release the job object
return hr; }
hr = pJob->QueryInterface(riid, (void **)ppunk); if (FAILED(hr)) { ERR_OUT("CSchedule::Activate: QueryInterface(riid)", hr); *ppunk = NULL; pJob->Release(); // on error, completely release the job object
return hr; }
//
// The above QI increased the refcount to 2, so set it back to 1.
//
pJob->Release(); return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ITaskScheduler::IsOfType, public
//
// Synopsis: Does this object support the desired interface?
//
// Arguments: [pwszName] - indicates the object name
// [riid] - indicates the interface of interest, typically
// IID_ITask or IID_IScheduledQueue
//
// Returns: S_OK if it is, S_FALSE otherwise.
//
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::IsOfType(LPCWSTR pwszName, REFIID riid) { TRACE(CSchedule, IsOfType);
// CODEWORK: A heavyweight implementation for now. It could possibly
// be optimized by doing the QueryInterface before the LoadP, and
// doing a lightweight LoadP.
IUnknown * punk; HRESULT hr = Activate(pwszName, riid, &punk); if (SUCCEEDED(hr)) { punk->Release(); hr = S_OK; } else { if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_DATA) || hr == SCHED_E_UNKNOWN_OBJECT_VERSION || hr == E_NOINTERFACE) { //
// These errors mean that the object is definitely not of a
// type that we support. We translate them to S_FALSE.
// Other errors could include file-not-found, access-denied,
// invalid-arg, etc. We return those errors unmodified.
//
hr = S_FALSE; } }
return hr; }
|