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.
 
 
 
 
 
 

1386 lines
40 KiB

/******************************************************************
Printer.CPP -- WMI provider class implementation
Generated by Microsoft WMI Code Generation Engine
TO DO: - See individual function headers
- When linking, make sure you link to framedyd.lib &
msvcrtd.lib (debug) or framedyn.lib & msvcrt.lib (retail).
Description:
******************************************************************/
#include "pchealth.h"
#include "Printer.h"
#include "exdisp.h"
/////////////////////////////////////////////////////////////////////////////
// tracing stuff
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__;
#define THIS_FILE __szTraceSourceFile
#define TRACE_ID DCID_PRINTERDRIVER
/////////////////////////////////////////////////////////////////////////////
// initialization
CPrinter MyPrinterSet(PROVIDER_NAME_PRINTER, PCH_NAMESPACE);
/////////////////////////////////////////////////////////////////////////////
// Property names
// PCH
const static WCHAR *c_wszDate = L"Date";
const static WCHAR *c_wszDefault = L"Default";
const static WCHAR *c_wszFilename = L"Filename";
const static WCHAR *c_wszManufacturer = L"Manufacturer";
const static WCHAR *c_wszName = L"Name";
const static WCHAR *c_wszPath = L"Path";
const static WCHAR *c_wszPaused = L"Paused";
const static WCHAR *c_wszSize = L"Size";
const static WCHAR *c_wszVersion = L"Version";
const static WCHAR *c_wszSpooler = L"SpoolEnabled";
const static WCHAR *c_wszNetwork = L"Network";
const static WCHAR *c_wszNSTimeout = L"NSTimeout";
const static WCHAR *c_wszRetryTimeout = L"RetryTimeout";
// Win32
const static WCHAR *c_wszPortName = L"PortName";
const static WCHAR *c_wszFileSize = L"FileSize";
const static WCHAR *c_wszLastModified = L"LastModified";
const static WCHAR *c_wszDeviceID = L"DeviceID";
// method parameters
const static WCHAR *c_wszURL = L"strURL";
const static WCHAR *c_wszRetVal = L"ReturnValue";
const static WCHAR *c_wszEnable = L"fEnable";
const static WCHAR *c_wszTxTimeoutP = L"uitxTimeout";
const static WCHAR *c_wszDNSTimeoutP = L"uidnsTimeout";
// misc
const static TCHAR *c_szRegPathPrn = _T("SYSTEM\\CurrentControlSet\\Control\\Print\\Printers\\");
const static TCHAR *c_szTxTimeout = _T("txTimeout");
const static TCHAR *c_szDNSTimeout = _T("dnsTimeout");
CComBSTR g_bstrDeviceID = L"DeviceID";
CComBSTR g_bstrAttrib = L"Attributes";
//////////////////////////////////////////////////////////////////////////////
// utility functions
// ***************************************************************************
// ***** IMPORTANT NOTE *****
// You must free the value you get returned via ppPrnInfo via MyFree()
HRESULT GetPrinterInfo(LPTSTR szPrinter, LPBYTE *ppPrnInfo,
HANDLE *phPrinter, DWORD dwLevel)
{
USES_CONVERSION;
TraceFunctEnter("GetPrinterInfo");
HRESULT hr = NOERROR;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
LPBYTE pbBuff = NULL;
DWORD cbRead, cbNeed;
BOOL fOk;
if (szPrinter == NULL)
{
hr = E_INVALIDARG;
goto done;
}
// yay! Now we have a printer name we can call OpenPrinter with.
fOk = OpenPrinter(szPrinter, &hPrinter, NULL);
if (fOk == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Unable to open printer %ls: 0x%08x", szPrinter,
hr);
goto done;
}
// only need to get this if the user wants it...
if (ppPrnInfo != NULL)
{
// GetPrinter expects a buffer larger than PRINTER_INFO_2 all by itself...
// So gotta figure out how big of a buffer it wants and allocate it...
fOk = GetPrinter(hPrinter, dwLevel, NULL, 0, &cbNeed);
if (fOk == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Unable to get printer info for %ls: 0x%08x",
szPrinter, hr);
goto done;
}
pbBuff = (LPBYTE)MyAlloc(cbNeed);
if (pbBuff == NULL)
{
hr = E_OUTOFMEMORY;
ErrorTrace(TRACE_ID, "Out of memory allocating buffer for printer data");
goto done;
}
fOk = GetPrinter(hPrinter, dwLevel, pbBuff, cbNeed, &cbRead);
if (fOk == FALSE || cbRead > cbNeed)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Unable to get printer info for %ls: 0x%08x",
szPrinter, hr);
goto done;
}
*ppPrnInfo = pbBuff;
pbBuff = NULL;
}
if (phPrinter != NULL)
{
*phPrinter = hPrinter;
hPrinter = INVALID_HANDLE_VALUE;
}
done:
if (pbBuff != NULL)
MyFree(pbBuff);
if (hPrinter != INVALID_HANDLE_VALUE)
ClosePrinter(hPrinter);
TraceFunctLeave();
return hr;
}
// ***************************************************************************
HRESULT FindJobError(HANDLE hPrinter, DWORD cJobs, LPTSTR szUser,
DWORD *pdwStatus, DWORD *pdwID)
{
USES_CONVERSION;
TraceFunctEnter("FindJobError");
JOB_INFO_2 *rgJobInfo = NULL;
HRESULT hr = NOERROR;
DWORD cbNeed, cbRead, cFetched, i;
BOOL fOk;
if (szUser == NULL || pdwStatus == NULL || pdwID == NULL)
{
ErrorTrace(TRACE_ID, "Invalid parameters");
hr = E_INVALIDARG;
goto done;
}
// EnumJobs requires a random amount of space to fill up. Find out
// how much it wants this time.
fOk = EnumJobs(hPrinter, 0, cJobs, 2, NULL, 0, &cbNeed, &cFetched);
if (fOk == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "EnumJobs failed: 0x%08x", hr);
goto done;
}
rgJobInfo = (JOB_INFO_2 *)MyAlloc(cbNeed);
if (rgJobInfo == NULL)
{
hr = E_OUTOFMEMORY;
ErrorTrace(TRACE_ID, "Out of memory");
goto done;
}
// actually get the data
fOk = EnumJobs(hPrinter, 0, cJobs, 2, (LPBYTE)rgJobInfo, cbNeed, &cbRead,
&cFetched);
if (fOk == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "EnumJobs failed: 0x%08x", hr);
goto done;
}
// we are looking for two things:
// if the current user has job that failed
for(i = 0; i < cJobs; i++)
{
if (rgJobInfo[i].pUserName != NULL &&
_tcscmp(rgJobInfo[i].pUserName, szUser) == 0)
{
if ((rgJobInfo[i].Status & (JOB_STATUS_PAUSED |
JOB_STATUS_DELETING |
JOB_STATUS_ERROR |
JOB_STATUS_OFFLINE |
JOB_STATUS_PAPEROUT |
JOB_STATUS_BLOCKED_DEVQ |
JOB_STATUS_PAUSED |
JOB_STATUS_USER_INTERVENTION)) != 0)
{
*pdwID = rgJobInfo[i].JobId;
*pdwStatus = rgJobInfo[i].Status;
hr = NOERROR;
goto done;
}
}
}
// if anyone has a job that failed
for(i = 0; i < cJobs; i++)
{
if ((rgJobInfo[i].Status & JOB_STATUS_PRINTING) != 0 &&
(rgJobInfo[i].Status & (JOB_STATUS_ERROR |
JOB_STATUS_OFFLINE |
JOB_STATUS_PAPEROUT |
JOB_STATUS_BLOCKED_DEVQ |
JOB_STATUS_USER_INTERVENTION)) != 0)
{
_tcscpy(szUser, rgJobInfo[i].pUserName);
*pdwID = rgJobInfo[i].JobId;
*pdwStatus = rgJobInfo[i].Status;
hr = NOERROR;
goto done;
}
}
*pdwID = (DWORD)-1;
*pdwStatus = 0;
done:
if (rgJobInfo != NULL)
MyFree(rgJobInfo);
TraceFunctLeave();
return hr;
}
//////////////////////////////////////////////////////////////////////////////
// construction / destruction
// ***************************************************************************
CPrinter::CPrinter (LPCWSTR lpwszName, LPCWSTR lpwszNameSpace) :
Provider(lpwszName, lpwszNameSpace)
{
m_pParamOut = NULL;
m_pCurrent = NULL;
m_pParamIn = NULL;
m_lFlags = 0;
}
// ***************************************************************************
CPrinter::~CPrinter ()
{
}
//////////////////////////////////////////////////////////////////////////////
// internal methods
// ****************************************************************************
HRESULT CPrinter::GetInstanceData(IWbemClassObjectPtr pObj, CInstance *pInst)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::GetInstanceData");
IWbemClassObjectPtr pFileObj = NULL;
PRINTER_INFO_2 *pPrnInfo2 = NULL;
PRINTER_INFO_5 *pPrnInfo5 = NULL;
struct _stat filestat;
CComVariant varValue;
CComBSTR bstrPrinterDriverWithPath;
CComBSTR bstrPrinterDriver;
CComBSTR bstrProperty;
HRESULT hr = WBEM_S_NO_ERROR;
DWORD dwStatus, dwErr;
ULONG ulPrinterRetVal = 0;
ULONG uiReturn = 0;
TCHAR szDeviceID[MAX_PATH];
TCHAR szBuffer[MAX_PATH];
TCHAR *pchToken;
BOOL fDriverFound;
BOOL fLocal = TRUE;
// ** name
CopyProperty(pObj, c_wszDeviceID, pInst, c_wszName);
// ** path
CopyProperty(pObj, c_wszPortName, pInst, c_wszPath);
// ** spoolenabled
CopyProperty(pObj, c_wszSpooler, pInst, c_wszSpooler);
// get the attribute property from the passed in printer object. With that
// we can get all sorts of info (default, network / local, etc)
hr = pObj->Get(g_bstrAttrib, 0, &varValue, NULL, NULL);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "Unable to get attribute property from WMI: 0x%08x",
hr);
}
else if (V_VT(&varValue) != VT_I4)
{
hr = VariantChangeType(&varValue, &varValue, 0, VT_I4);
if (FAILED(hr))
ErrorTrace(TRACE_ID, "Unable to convert type: 0x%08x", hr);
}
if (SUCCEEDED(hr))
{
DWORD dwAttribs;
dwAttribs = V_I4(&varValue);
// ** default
varValue = VARIANT_FALSE;
if ((dwAttribs & PRINTER_ATTRIBUTE_DEFAULT) != 0)
varValue = VARIANT_TRUE;
if (pInst->SetVariant(c_wszDefault, varValue) == FALSE)
ErrorTrace(TRACE_ID, "SetVariant on Default failed");
// ** network
varValue = VARIANT_FALSE;
if ((dwAttribs & PRINTER_ATTRIBUTE_NETWORK) != 0)
{
varValue = VARIANT_TRUE;
fLocal = FALSE;
}
if (pInst->SetVariant(c_wszNetwork, varValue) == FALSE)
ErrorTrace(TRACE_ID, "SetVariant on Network failed");
}
// we need the deviceID to do a whole bunch of stuff...
varValue.Clear();
hr = pObj->Get(g_bstrDeviceID, 0, &varValue, NULL, NULL);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "Unable to get attribute property from WMI: 0x%08x",
hr);
}
else if (V_VT(&varValue) != VT_BSTR)
{
hr = VariantChangeType(&varValue, &varValue, 0, VT_BSTR);
if (FAILED(hr))
ErrorTrace(TRACE_ID, "Unable to convert type: 0x%08x", hr);
}
if (SUCCEEDED(hr))
{
// since we're going to need it a lot as a TCHAR, convert the
// name of the printer to one...
_tcscpy(szDeviceID, OLE2T(V_BSTR(&varValue)));
// ** paused
hr = GetPrinterInfo(szDeviceID, (LPBYTE *)&pPrnInfo2, NULL, 2);
if (SUCCEEDED(hr))
{
varValue.Clear();
varValue = VARIANT_FALSE;
if ((pPrnInfo2->Status & PRINTER_STATUS_PAUSED) != 0)
varValue = VARIANT_TRUE;
if (pInst->SetVariant(c_wszPaused, varValue) == FALSE)
ErrorTrace(TRACE_ID, "SetVariant on Paused failed");
MyFree(pPrnInfo2);
pPrnInfo2 = NULL;
}
// ** timeout values
hr = GetPrinterInfo(szDeviceID, (LPBYTE *)&pPrnInfo5, NULL, 5);
if (SUCCEEDED(hr))
{
varValue.Clear();
V_VT(&varValue) = VT_I4;
V_I4(&varValue) = pPrnInfo5->DeviceNotSelectedTimeout;
if (pInst->SetVariant(c_wszNSTimeout, varValue) == FALSE)
ErrorTrace(TRACE_ID, "SetVariant on NSTimeout failed");
V_I4(&varValue) = pPrnInfo5->TransmissionRetryTimeout;
if (pInst->SetVariant(c_wszRetryTimeout, varValue) == FALSE)
ErrorTrace(TRACE_ID, "SetVariant on RetryTimeout failed");
MyFree(pPrnInfo5);
pPrnInfo5 = NULL;
}
// ** filename + others
// Now call GetProfileString to get the Driver
varValue.Clear();
if (GetProfileString(_T("Devices"), szDeviceID, _T("\0"), szBuffer,
MAX_PATH) > 1)
{
// szBuffer contains a string of two tokens, first the driver,
// second the PathName
// Get the driver
pchToken = _tcstok(szBuffer, _T(","));
if(pchToken != NULL)
{
// Got the Driver Name
bstrPrinterDriver = pchToken;
varValue = pchToken;
// ** set the filename
if (pInst->SetVariant(c_wszFilename, varValue) == FALSE)
ErrorTrace(TRACE_ID, "SetVariant on FileName failed");
// in order to get the file properties, we have to construct
// the full path to the file
bstrPrinterDriver.Append(L".drv");
fDriverFound = getCompletePath(bstrPrinterDriver,
bstrPrinterDriverWithPath);
if (fDriverFound)
{
// GetCIMDataFile Function fetches properties of this file.
hr = GetCIMDataFile(bstrPrinterDriverWithPath, &pFileObj);
if (SUCCEEDED(hr))
{
// ** version
CopyProperty(pFileObj, c_wszVersion,
pInst, c_wszVersion);
// ** filesize
CopyProperty(pFileObj, c_wszFileSize,
pInst, c_wszSize);
// ** date
CopyProperty(pFileObj, c_wszLastModified,
pInst, c_wszDate);
// ** manufacturer
CopyProperty(pFileObj, c_wszManufacturer,
pInst, c_wszManufacturer);
}
}
}
}
}
TraceFunctLeave();
return hr;
}
// ****************************************************************************
HRESULT CPrinter::GetStatus(void)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::GetStatus");
PRINTER_INFO_2 *pPrnInfo = NULL;
HRESULT hr = NOERROR;
VARIANT var;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
DWORD dwStatus;
DWORD dwLocation;
TCHAR szPrinter[1024];
VariantInit(&var);
if (m_pCurrent == NULL || m_pParamOut == NULL)
{
ErrorTrace(TRACE_ID, "Parameter objects not set.");
hr = E_FAIL;
goto done;
}
if (m_pCurrent->GetVariant(c_wszName, var) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to fetch printer name from m_pCurrent");
hr = E_FAIL;
goto done;
}
if (V_VT(&var) != VT_BSTR)
{
hr = VariantChangeType(&var, &var, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x", hr);
goto done;
}
}
_tcscpy(szPrinter, OLE2T(V_BSTR(&var)));
// get the printer info structure
hr = GetPrinterInfo(szPrinter, (LPBYTE *)&pPrnInfo, &hPrinter, 2);
if (FAILED(hr))
goto done;
dwStatus = pPrnInfo->Status;
// if the status is not in the error state, then we need to look at the
// list of print jobs available
if (dwStatus == 0)
{
DWORD dwJobID;
DWORD cbUser;
TCHAR szUser[512];
cbUser = 512;
GetUserName(szUser, &cbUser);
hr = FindJobError(hPrinter, pPrnInfo->cJobs, szUser, &dwStatus,
&dwJobID);
if (FAILED(hr))
goto done;
}
VariantClear(&var);
V_VT(&var) = VT_I4;
V_I4(&var) = dwStatus;
if (m_pParamOut->SetVariant(c_wszRetVal, var) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to set return val object");
hr = E_FAIL;
goto done;
}
done:
VariantClear(&var);
if (pPrnInfo != NULL)
MyFree(pPrnInfo);
if (hPrinter != INVALID_HANDLE_VALUE)
ClosePrinter(hPrinter);
TraceFunctLeave();
return hr;
}
// ****************************************************************************
HRESULT CPrinter::RemovePause(void)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::RemovePause");
PRINTER_INFO_2 *pPrnInfo = NULL;
HRESULT hr = NOERROR;
VARIANT var;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
BOOL fOk;
VariantInit(&var);
if (m_pCurrent == NULL)
{
ErrorTrace(TRACE_ID, "Parameter object not set.");
hr = E_FAIL;
goto done;
}
if (m_pCurrent->GetVariant(c_wszName, var) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to fetch printer name from m_pCurrent");
hr = E_FAIL;
goto done;
}
if (V_VT(&var) != VT_BSTR)
{
hr = VariantChangeType(&var, &var, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x", hr);
goto done;
}
}
hr = GetPrinterInfo(OLE2T(V_BSTR(&var)), (LPBYTE *)&pPrnInfo, &hPrinter,
2);
if (FAILED(hr))
goto done;
if (pPrnInfo->Status == PRINTER_STATUS_PAUSED)
{
fOk = SetPrinter(hPrinter, 0, NULL, PRINTER_CONTROL_RESUME);
if (fOk == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "SetPrinter failed: 0x%08x", hr);
goto done;
}
}
done:
VariantClear(&var);
if (pPrnInfo != NULL)
MyFree(pPrnInfo);
if (hPrinter != INVALID_HANDLE_VALUE)
ClosePrinter(hPrinter);
TraceFunctLeave();
return hr;
}
// ****************************************************************************
HRESULT CPrinter::PrinterProperties(void)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::PrinterProperties");
PRINTER_INFO_2 *pPrnInfo = NULL;
LPDEVMODE pDevMode = NULL;
HRESULT hr = NOERROR;
VARIANT var;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
DWORD cbDevMode;
VariantInit(&var);
if (m_pCurrent == NULL)
{
ErrorTrace(TRACE_ID, "Parameter object not set.");
hr = E_FAIL;
goto done;
}
if (m_pCurrent->GetVariant(c_wszName, var) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to fetch printer name from m_pCurrent");
hr = E_FAIL;
goto done;
}
if (V_VT(&var) != VT_BSTR)
{
hr = VariantChangeType(&var, &var, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x", hr);
goto done;
}
}
hr = GetPrinterInfo(OLE2T(V_BSTR(&var)), (LPBYTE *)pPrnInfo, &hPrinter, 2);
if (FAILED(hr))
goto done;
cbDevMode = DocumentProperties(NULL, hPrinter, OLE2T(V_BSTR(&var)),
NULL, NULL, 0);
pDevMode = (LPDEVMODE)MyAlloc(cbDevMode);
if (pDevMode == NULL)
{
hr = E_OUTOFMEMORY;
ErrorTrace(TRACE_ID, "Out of memory allocating DEVMODE structure");
goto done;
}
// ok, call this for real this time...
if (DocumentProperties(NULL, hPrinter, OLE2T(V_BSTR(&var)),
pDevMode, NULL, DM_PROMPT) == IDOK)
{
// nothing to free here cuz pPrnInfo->pDevMode points into the memory blob
// that pPrnInfo points to...
pPrnInfo->pDevMode = pDevMode;
if (SetPrinter(hPrinter, 2, (LPBYTE)pPrnInfo, 0) == FALSE)
{
hr = E_OUTOFMEMORY;
ErrorTrace(TRACE_ID, "Unable to set new printer info.");
goto done;
}
}
done:
VariantClear(&var);
if (pPrnInfo != NULL)
MyFree(pPrnInfo);
if (pDevMode != NULL)
MyFree(pDevMode);
if (hPrinter != INVALID_HANDLE_VALUE)
ClosePrinter(hPrinter);
TraceFunctLeave();
return hr;
}
// ****************************************************************************
HRESULT CPrinter::SetAsDefault(TCHAR *szOldDefault, DWORD cchOldDefault,
BOOL fSetOldDefault)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::SetAsDefault");
HRESULT hr = NOERROR;
VARIANT var;
DWORD dw;
TCHAR szPrinter[1024], szNewDefault[1024];
BOOL fOk;
VariantInit(&var);
if (m_pCurrent == NULL)
{
ErrorTrace(TRACE_ID, "Parameter object not set.");
hr = E_FAIL;
goto done;
}
// See if the caller wants to know what the old default is or wants to set
// the old default...
if (szOldDefault != NULL)
{
// see if we want to set the default
if (fSetOldDefault)
{
fOk = WriteProfileString(_T("Windows"), _T("Device"), szOldDefault);
if (fOk == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Failed to write old default printer: 0x%08x",
hr);
}
// can goto done here cuz we don't need to do anything else...
goto done;
}
// or maybe we just want to grab is and then set m_pCurrent to be the
// default
else
{
dw = GetProfileString(_T("Windows"), _T("Device"), _T("\0"),
szOldDefault, cchOldDefault);
if (dw <= 1)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Failed to fetch current default: 0x%08x",
hr);
goto done;
}
}
}
// if we're here, then we gotta set the printer pointed to by m_pCurrent as
// the default printer, so fetch the name of the printer we want to be the
// default
if (m_pCurrent->GetVariant(c_wszName, var) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to fetch printer name from m_pCurrent");
hr = E_FAIL;
goto done;
}
if (V_VT(&var) != VT_BSTR)
{
hr = VariantChangeType(&var, &var, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x", hr);
goto done;
}
}
// get the printer info from win.ini
dw = GetProfileString(_T("Devices"), OLE2T(V_BSTR(&var)), _T("\0"),
szPrinter, sizeof(szPrinter) / sizeof(TCHAR));
if (dw <= 1)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Failed to fetch current default: 0x%08x", hr);
goto done;
}
// build a string & slam it back into win.ini
wsprintf(szNewDefault, "%s,%s", OLE2T(V_BSTR(&var)), szPrinter);
fOk = WriteProfileString(_T("Windows"), _T("Device"), szNewDefault);
if (fOk == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Failed to write new default printer: 0x%08x",
hr);
}
// got to notify everyone in existance (well, all the top level windows
// anyway) that we changed the default printer...
SendMessageTimeout(HWND_BROADCAST, WM_WININICHANGE, 0L,
(LPARAM)(LPCTSTR)_T("windows"), SMTO_NORMAL, 1000,
NULL);
done:
VariantClear(&var);
TraceFunctLeave();
return hr;
}
// ****************************************************************************
// *** NOTE: this method doesn't work on WinNT cuz WinMgmt runs as a service
// which has different printer settings / permissions than the user
// account
HRESULT CPrinter::TestPrinter(void)
{
TraceFunctEnter("CPrinter::TestPrinter");
IWebBrowser2 *pwb = NULL;
READYSTATE rs;
VARIANT varFlags, varOpt, varURL;
HRESULT hr = NOERROR;
CLSID clsid;
DWORD dwStart;
TCHAR szDefault[1024];
VariantInit(&varFlags);
VariantInit(&varURL);
VariantInit(&varOpt);
if (m_pParamIn == NULL)
{
ErrorTrace(TRACE_ID, "Parameter object not set.");
hr = E_FAIL;
goto done;
}
if (m_pParamIn->GetVariant(c_wszURL, varURL) == FALSE)
{
ErrorTrace(TRACE_ID, "strURL parameter not present.");
hr = E_FAIL;
goto done;
}
hr = VariantChangeType(&varURL, &varURL, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "unable to convert strURL to string");
goto done;
}
// the URL should be at least 4 characters long in order for it to be a
// valid file path. Need 3 characters for drive path & at least 1 for
// the filename (as in 'd:\a')
if (SysStringLen(V_BSTR(&varURL)) < 4)
{
ErrorTrace(TRACE_ID, "strURL parameter < 4 characters.");
hr = E_INVALIDARG;
goto done;
}
// we obviously need a web browser object, so make one
hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER,
IID_IWebBrowser2, (LPVOID *)&pwb);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "Unable to CoCreate web browser control: 0x%08x", hr);
goto done;
}
// load the URL
V_VT(&varFlags) = VT_I4;
V_I4(&varFlags) = navNoHistory;
V_VT(&varOpt) = VT_ERROR;
V_ERROR(&varOpt) = DISP_E_PARAMNOTFOUND;
hr = pwb->Navigate2(&varURL, &varOpt, &varOpt, &varOpt, &varOpt);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "Unable to Navigate to URL '%ls': 0x%08x",
V_BSTR(&varURL), hr);
goto done;
}
// wait for a maximum of 5 minutes for this URL to come in...
for(dwStart = GetTickCount(); GetTickCount() - dwStart <= 300000;)
{
hr = pwb->get_ReadyState(&rs);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "Unable to get web browser state: 0x%08x", hr);
goto done;
}
if (rs == READYSTATE_COMPLETE)
break;
}
// make sure we didn't timeout...
if (rs != READYSTATE_COMPLETE)
{
ErrorTrace(TRACE_ID, "Timeout waiting for browser to load URL");
hr = E_FAIL;
goto done;
}
// since we aren't prompting the user, we need to temporarily set the
// default printer to be the one we want to test
hr = this->SetAsDefault(szDefault, sizeof(szDefault) / sizeof(TCHAR), FALSE);
if (FAILED(hr))
goto done;
// do the print
hr = pwb->ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, &varOpt, &varOpt);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "Unable to print: 0x%08x", hr);
goto done;
}
// revert back to the original printer
hr = this->SetAsDefault(szDefault, sizeof(szDefault) / sizeof(TCHAR), TRUE);
if (FAILED(hr))
goto done;
done:
VariantClear(&varURL);
if (pwb != NULL)
pwb->Release();
TraceFunctLeave();
return hr;
}
// *****************************************************************************
HRESULT CPrinter::EnableSpooler(void)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::EnableSpooler");
PRINTER_INFO_2 *pPrnInfo = NULL;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
VARIANT varEnable, varName;
HRESULT hr = NOERROR;
VariantInit(&varEnable);
VariantInit(&varName);
// get the parameter
if (m_pParamIn == NULL || m_pCurrent == NULL)
{
ErrorTrace(TRACE_ID, "Parameter object not set.");
hr = E_FAIL;
goto done;
}
if (m_pParamIn->GetVariant(c_wszEnable, varEnable) == FALSE)
{
ErrorTrace(TRACE_ID, "strURL parameter not present.");
hr = E_FAIL;
goto done;
}
if (V_VT(&varEnable) != VT_BOOL)
{
hr = VariantChangeType(&varEnable, &varEnable, 0, VT_BOOL);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "unable to convert fEnable to bool: 0x%08x",
hr);
goto done;
}
}
if (m_pCurrent->GetVariant(c_wszName, varName) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to fetch printer name from m_pCurrent");
hr = E_FAIL;
goto done;
}
if (V_VT(&varName) != VT_BSTR)
{
hr = VariantChangeType(&varName, &varName, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x", hr);
goto done;
}
}
hr = GetPrinterInfo(OLE2T(V_BSTR(&varName)), (LPBYTE *)&pPrnInfo,
&hPrinter, 2);
if (FAILED(hr))
goto done;
if (V_BOOL(&varEnable) == VARIANT_FALSE)
pPrnInfo->Attributes &= ~PRINTER_ATTRIBUTE_DIRECT;
else
pPrnInfo->Attributes |= PRINTER_ATTRIBUTE_DIRECT;
if (SetPrinter(hPrinter, 2, (LPBYTE)pPrnInfo, 0) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "SetPrinter failed: 0x%08x", hr);
goto done;
}
done:
VariantClear(&varName);
VariantClear(&varEnable);
if (pPrnInfo != NULL)
MyFree(pPrnInfo);
if (hPrinter != INVALID_HANDLE_VALUE)
ClosePrinter(hPrinter);
TraceFunctLeave();
return hr;
}
// *****************************************************************************
HRESULT CPrinter::SetTimeouts(void)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::SetTimeouts");
PRINTER_INFO_5 *pPrnInfo5 = NULL;
HRESULT hr = NOERROR;
VARIANT varName, varDNS, varTX;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
VariantInit(&varName);
VariantInit(&varDNS);
VariantInit(&varTX);
// get the parameter
if (m_pParamIn == NULL || m_pCurrent == NULL)
{
ErrorTrace(TRACE_ID, "Parameter object not set.");
hr = E_FAIL;
goto done;
}
// get uiTxTimeout
if (m_pParamIn->GetVariant(c_wszTxTimeoutP, varTX) == FALSE)
{
ErrorTrace(TRACE_ID, "uiTxTimeout parameter not present.");
hr = E_FAIL;
goto done;
}
if (V_VT(&varTX) != VT_I4)
{
hr = VariantChangeType(&varTX, &varTX, 0, VT_I4);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x",
hr);
goto done;
}
}
// get uiDNSTimeout
if (m_pParamIn->GetVariant(c_wszDNSTimeoutP, varDNS) == FALSE)
{
ErrorTrace(TRACE_ID, "uiDNSTimeout parameter not present.");
hr = E_FAIL;
goto done;
}
if (V_VT(&varDNS) != VT_I4)
{
hr = VariantChangeType(&varDNS, &varDNS, 0, VT_I4);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x",
hr);
goto done;
}
}
// get Name
if (m_pCurrent->GetVariant(c_wszName, varName) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to fetch printer name from m_pCurrent");
hr = E_FAIL;
goto done;
}
if (V_VT(&varName) != VT_BSTR)
{
hr = VariantChangeType(&varName, &varName, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x", hr);
goto done;
}
}
hr = GetPrinterInfo(OLE2T(V_BSTR(&varName)), (LPBYTE *)&pPrnInfo5,
&hPrinter, 5);
if (FAILED(hr))
goto done;
pPrnInfo5->TransmissionRetryTimeout = V_I4(&varTX);
pPrnInfo5->DeviceNotSelectedTimeout = V_I4(&varDNS);
if (SetPrinter(hPrinter, 5, (LPBYTE)pPrnInfo5, 0) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace(TRACE_ID, "Unable to set printer info: 0x%08x", hr);
goto done;
}
done:
VariantClear(&varName);
VariantClear(&varDNS);
VariantClear(&varTX);
if (pPrnInfo5 != NULL)
MyFree(pPrnInfo5);
if (hPrinter != INVALID_HANDLE_VALUE)
ClosePrinter(hPrinter);
TraceFunctLeave();
return hr;
}
//////////////////////////////////////////////////////////////////////////////
// exposed methods
// *****************************************************************************
HRESULT CPrinter::EnumerateInstances(MethodContext* pMethodContext, long lFlags)
{
USES_CONVERSION;
TraceFunctEnter("CPrinter::EnumerateInstances");
IEnumWbemClassObject *pEnumInst = NULL;
IWbemClassObjectPtr pObj = NULL;
CComBSTR bstrPrinterQuery;
HRESULT hr = WBEM_S_NO_ERROR;
ULONG ulPrinterRetVal = 0;
// Execute the query to get DeviceID, PortName from the Win32_Printer class
bstrPrinterQuery = L"Select DeviceID, PortName, SpoolEnabled, Status, Attributes FROM win32_printer";
hr = ExecWQLQuery(&pEnumInst, bstrPrinterQuery);
if (FAILED(hr))
goto done;
// Enumerate the instances from pEnumInstance
while(pEnumInst->Next(WBEM_INFINITE, 1, &pObj, &ulPrinterRetVal) == WBEM_S_NO_ERROR)
{
// Create a new instance of PCH_PrinterDriver Class based on the
// passed-in MethodContext
CInstancePtr pInst(CreateNewInstance(pMethodContext), FALSE);
// original code didn't really care if this failed, so neither do I...
hr = GetInstanceData(pObj, pInst);
// All the properties are set. Commit the instance
hr = pInst->Commit();
if(FAILED(hr))
ErrorTrace(TRACE_ID, "Could not commit instance: 0x%08x", hr);
// Ok, so WMI does not follow it's own docs on how GetObject
// works. According to them, we should release this object here. But
// if I try, winmgmt GPFs.
// pObj->Release();
pObj = NULL;
}
done:
if (pEnumInst != NULL)
pEnumInst->Release();
TraceFunctLeave();
return hr;
}
// *****************************************************************************
HRESULT CPrinter::ExecMethod (const CInstance& Instance,
const BSTR bstrMethodName,
CInstance *pInParams, CInstance *pOutParams,
long lFlags)
{
TraceFunctEnter("CPrinter::ExecMethod");
HRESULT hr = NOERROR;
m_pCurrent = (CInstance *)&Instance;
m_pParamIn = pInParams;
m_pParamOut = pOutParams;
m_lFlags = lFlags;
if (_wcsicmp(bstrMethodName, L"SetAsDefault") == 0)
hr = this->SetAsDefault();
else if (_wcsicmp(bstrMethodName, L"PrinterProperties") == 0)
hr = this->PrinterProperties();
else if (_wcsicmp(bstrMethodName, L"RemovePause") == 0)
hr = this->RemovePause();
else if (_wcsicmp(bstrMethodName, L"TestPrinter") == 0)
hr = this->TestPrinter();
else if (_wcsicmp(bstrMethodName, L"ErrorStatus") == 0)
hr = this->GetStatus();
else if (_wcsicmp(bstrMethodName, L"EnableSpooler") == 0)
hr = this->EnableSpooler();
else if (_wcsicmp(bstrMethodName, L"SetTimeouts") == 0)
hr = this->SetTimeouts();
else
hr = WBEM_E_INVALID_METHOD;
if (FAILED(hr))
goto done;
done:
m_pCurrent = NULL;
m_pParamIn = NULL;
m_pParamOut = NULL;
m_lFlags = 0;
TraceFunctLeave();
return hr;
}
// *****************************************************************************
HRESULT CPrinter::GetObject(CInstance* pInstance, long lFlags)
{
TraceFunctEnter("CPrinter::GetObject");
IWbemClassObjectPtr pObj = NULL;
CComBSTR bstrPath;
HRESULT hr = NOERROR;
VARIANT var;
WCHAR wszBuffer[1024], *pwszPrn, *pwszBuf;
DWORD i;
BSTR bstrPrn;
VariantInit(&var);
if (pInstance == NULL)
{
hr = E_INVALIDARG;
goto done;
}
// get the name of the printer
if (pInstance->GetVariant(c_wszName, var) == FALSE)
{
ErrorTrace(TRACE_ID, "Unable to fetch printer name");
hr = E_FAIL;
goto done;
}
if (V_VT(&var) != VT_BSTR)
{
hr = VariantChangeType(&var, &var, 0, VT_BSTR);
if (FAILED(hr))
{
ErrorTrace(TRACE_ID, "VariantChangeType failed: 0x%08x", hr);
goto done;
}
}
// WMI!! It expects me to turn a printer with a name \\server\share
// into \\\\server\\share. (double '\'s)
bstrPrn = V_BSTR(&var);
if ((bstrPrn[0] != L'\\' && bstrPrn[1] != L'\\') ||
(bstrPrn[0] == L'\\' && bstrPrn[1] == L'\\' && bstrPrn[2] == L'\\' &&
bstrPrn[3] == L'\\'))
{
wcscpy(wszBuffer, bstrPrn);
}
else
{
// ok, here's the annoying part...
wcscpy(wszBuffer, L"\\\\\\\\");
pwszBuf = wszBuffer + 4;
pwszPrn = bstrPrn + 2;
// actually, we only need to scan to the first '\' cuz we've already
// taken care of the 1st two & this needs to fit into '\\server\share'
while (pwszPrn != L'\0')
{
if (*pwszPrn == L'\\')
{
*pwszBuf++ = L'\\';
break;
}
*pwszBuf++ = *pwszPrn++;
}
wcscpy(pwszBuf, pwszPrn);
}
// build the path to the object
bstrPath = L"\\\\.\\root\\cimv2:Win32_Printer.DeviceID=\"";
bstrPath.Append(wszBuffer);
bstrPath.Append("\"");
// fetch it
hr = GetCIMObj(bstrPath, &pObj, lFlags);
if (FAILED(hr))
goto done;
// populate the CInstance object
hr = GetInstanceData(pObj, pInstance);
if (FAILED(hr))
goto done;
// All the properties are set. Commit the instance
hr = pInstance->Commit();
if(FAILED(hr))
ErrorTrace(TRACE_ID, "Could not commit instance: 0x%08x", hr);
done:
VariantClear(&var);
// Ok, so WMI does not follow it's own docs on how GetObject
// works. According to them, we should release this object here. But
// if I try, winmgmt GPFs.
// if (pObj != NULL)
// pObj->Release();
TraceFunctLeave();
return hr;
}
// *****************************************************************************
HRESULT CPrinter::ExecQuery(MethodContext *pMethodContext,
CFrameworkQuery& Query, long lFlags)
{
return WBEM_E_PROVIDER_NOT_CAPABLE;
}
// *****************************************************************************
HRESULT CPrinter::PutInstance(const CInstance& Instance, long lFlags)
{
return WBEM_E_PROVIDER_NOT_CAPABLE;
}
// *****************************************************************************
HRESULT CPrinter::DeleteInstance(const CInstance& Instance, long lFlags)
{
return WBEM_E_PROVIDER_NOT_CAPABLE;
}