/****************************************************************** 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; }