/*++ Copyright (c) 1997 Microsoft Corporation Abstract: This module provides functionality for publishing printers Author: Steve Wilson (NT) November 1997 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include "pubprn.hxx" #include "varconv.hxx" #include "property.hxx" #include "dsutil.hxx" #include "client.h" #define PPM_FACTOR 48 BOOL PublishPrinterW( HWND hwnd, PCWSTR pszUNCName, PCWSTR pszDN, PCWSTR pszCN, PWSTR *ppszDN, DWORD dwAction ) { PRINTER_DEFAULTS Defaults; HANDLE hPrinter = NULL; HANDLE hServer = NULL; PWSTR pszServerName = NULL; PWSTR pszPrinterName = NULL; PPRINTER_INFO_2 pInfo2 = NULL; DWORD dwRet = ERROR_SUCCESS; DWORD dwType; DWORD dwMajorVersion; DWORD dwDsPresent; DWORD cbNeeded; DWORD dwLength; HRESULT hr; WCHAR szDNSMachineName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; WCHAR szFullUNCName[MAX_UNC_PRINTER_NAME]; WCHAR szShortServerName[MAX_PATH+1]; PWSTR pszFullUNCName; PWSTR pszShortServerName; PWSTR pszFullServerName; if (InCSRProcess()) { SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } hr = CoInitialize(NULL); if (hr != S_OK && hr != S_FALSE) { SetLastError((DWORD)((HRESULT_FACILITY(hr) == FACILITY_WIN32) ? HRESULT_CODE(hr) : hr)); return FALSE; } if (ppszDN) *ppszDN = NULL; // Get server name if (dwRet = UNC2Server(pszUNCName, &pszServerName)) goto error; if(!OpenPrinter(pszServerName, &hServer, NULL)) { dwMajorVersion = 0; } else { dwRet = GetPrinterData( hServer, SPLREG_MAJOR_VERSION, &dwType, (PBYTE) &dwMajorVersion, sizeof dwMajorVersion, &cbNeeded); if (dwRet != ERROR_SUCCESS) { dwMajorVersion = 0; dwRet = ERROR_SUCCESS; // ignore errors and assume lowest version } if (dwMajorVersion >= WIN2000_SPOOLER_VERSION) { hr = MachineIsInMyForest(pszServerName); if (FAILED(hr)) { dwRet = HRESULT_CODE(hr); goto error; } else if(HRESULT_CODE(hr) == 1) { // Machine is in my forest and is NT5+ dwRet = ERROR_INVALID_LEVEL; goto error; } else { // Downgrade the version for NT5+ printers published in a non-DS domain dwMajorVersion = WIN2000_SPOOLER_VERSION; } } } Defaults.pDatatype = NULL; Defaults.pDevMode = NULL; Defaults.DesiredAccess = PRINTER_ACCESS_USE; if (!OpenPrinter((PWSTR) pszUNCName, &hPrinter, &Defaults)) { dwRet = GetLastError(); goto error; } hr = GetPrinterInfo2(hPrinter, &pInfo2); if (FAILED(hr)) { dwRet = HRESULT_CODE(hr); goto error; } if (dwRet = UNC2Printer(pInfo2->pPrinterName, &pszPrinterName)) goto error; if( dwMajorVersion >= WIN2000_SPOOLER_VERSION){ if(dwRet = GetPrinterData( hServer, SPLREG_DNS_MACHINE_NAME, &dwType, (PBYTE) szDNSMachineName, (INTERNET_MAX_HOST_NAME_LENGTH + 1) * sizeof(WCHAR), &cbNeeded) != ERROR_SUCCESS ) { goto error; } wsprintf( szFullUNCName, L"\\\\%s\\%s", szDNSMachineName, pszPrinterName ); dwLength = MAX_PATH + 1; if (!DnsHostnameToComputerName(pszServerName, szShortServerName, &dwLength)) { dwRet = GetLastError(); goto error; } pszFullUNCName = szFullUNCName; pszFullServerName = szDNSMachineName; pszShortServerName = szShortServerName+2; } else { pszFullUNCName = (PWSTR)pszUNCName; pszFullServerName = pszServerName+2; pszShortServerName = pszServerName+2; } // Verify PrintQueue doesn't already exist if (dwAction != PUBLISHPRINTER_IGNORE_DUPLICATES) { if(dwRet = PrintQueueExists(hwnd, hPrinter, pszFullUNCName, dwAction, (PWSTR) pszDN, (PWSTR *) ppszDN)) goto error; } if (dwRet = PublishDownlevelPrinter(hPrinter, (PWSTR) pszDN, (PWSTR) pszCN, pszFullServerName, pszShortServerName, pszFullUNCName, pszPrinterName, dwMajorVersion, ppszDN)) goto error; error: if (hPrinter != NULL) ClosePrinter(hPrinter); if (hServer != NULL) ClosePrinter(hServer); if (pszServerName) FreeSplMem(pszServerName); if (pszPrinterName) FreeSplMem(pszPrinterName); FreeSplMem(pInfo2); if (dwRet != ERROR_SUCCESS) { SetLastError(dwRet); return FALSE; } CoUninitialize(); return TRUE; } DWORD PublishNT5Printer( HANDLE hPrinter, PWSTR pszDN, PWSTR pszCN, PWSTR *ppszObjectDN ) { PRINTER_INFO_7 Info7; DWORD dwRet = ERROR_SUCCESS; BYTE Data[(100 + sizeof(PRINTER_INFO_7))*sizeof(WCHAR)]; // GUIDs don't have well-defined size PPRINTER_INFO_7 pInfo7 = NULL; DWORD cbNeeded; // Disable NT5+ publishing because it is inconsistent with SetPrinter. // Also, NT5 duplicate printer must be deleted via SetPrinter, not via DS. If it is // deleted via DS then subsequent SetPrinter to publish will take a long time since // it discovers GUID no longer exists and spins background thread to delete & republish. // This, combined with replication delays, causes PublishNT5Printer to fail because getting // the ppszObjectDN because the object may not be published yet. return ERROR_INVALID_LEVEL; Info7.dwAction = DSPRINT_PUBLISH; if (!SetPrinter(hPrinter, 7, (PBYTE) &Info7, 0)) { dwRet = GetLastError(); goto error; } if (!GetPrinter(hPrinter, 7, (PBYTE) Data, sizeof(Data), &cbNeeded)) { if ((dwRet = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) goto error; if (!(pInfo7 = (PPRINTER_INFO_7) AllocSplMem(cbNeeded))) goto error; if (!GetPrinter(hPrinter, 7, (PBYTE) pInfo7, cbNeeded, &cbNeeded)) { dwRet = GetLastError(); goto error; } } else { pInfo7 = (PPRINTER_INFO_7) Data; } if (!(dwRet = MovePrintQueue(pInfo7->pszObjectGUID, pszDN, pszCN))) goto error; if (dwRet = GetADsPathFromGUID(pInfo7->pszObjectGUID, ppszObjectDN)) goto error; error: if (pInfo7 && pInfo7 != (PPRINTER_INFO_7) Data) FreeSplMem(pInfo7); if (dwRet != ERROR_SUCCESS) { FreeGlobalStr(*ppszObjectDN); } return dwRet; } DWORD PublishDownlevelPrinter( HANDLE hPrinter, PWSTR pszDN, PWSTR pszCN, PWSTR pszServerName, PWSTR pszShortServerName, PWSTR pszUNCName, PWSTR pszPrinterName, DWORD dwVersion, PWSTR *ppszObjectDN ) { HRESULT hr = S_OK; DWORD dwRet = ERROR_SUCCESS; IADs *pPrintQueue = NULL; IADsContainer *pADsContainer = NULL; IDispatch *pDispatch = NULL; PWSTR pszCommonName = pszCN; BSTR bstrADsPath = NULL; PWSTR pszDNWithDC = NULL; if (ppszObjectDN) *ppszObjectDN = NULL; // If pszCN is not supplied, generate default common name if (!pszCommonName || !*pszCommonName) { dwRet = GetCommonName(hPrinter, &pszCommonName); if (dwRet != ERROR_SUCCESS) { hr = dw2hr(dwRet); BAIL_ON_FAILURE(hr); } } // Stick DC in DN if (!(pszDNWithDC = GetDNWithServer(pszDN))) { pszDNWithDC = pszDN; } // Get container hr = ADsGetObject(pszDNWithDC, IID_IADsContainer, (void **) &pADsContainer); BAIL_ON_FAILURE(hr); // Create printqueue hr = pADsContainer->Create(SPLDS_PRINTER_CLASS, pszCommonName, &pDispatch); BAIL_ON_FAILURE(hr); hr = pDispatch->QueryInterface(IID_IADs, (void **) &pPrintQueue); BAIL_ON_FAILURE(hr); // Set properties hr = SetProperties( hPrinter, pszServerName, pszShortServerName, pszUNCName, pszPrinterName, dwVersion, pPrintQueue); BAIL_ON_FAILURE(hr); // Get ADsPath to printQueue if (ppszObjectDN) { hr = pPrintQueue->get_ADsPath(&bstrADsPath); BAIL_ON_FAILURE(hr); if (!(*ppszObjectDN = AllocGlobalStr(bstrADsPath))) { dwRet = GetLastError(); hr = dw2hr(dwRet); BAIL_ON_FAILURE(hr); } } error: if (pszDNWithDC != pszDN) FreeSplMem(pszDNWithDC); if (bstrADsPath) SysFreeString(bstrADsPath); if (pszCommonName != pszCN) FreeSplMem(pszCommonName); if (pADsContainer) pADsContainer->Release(); if (pDispatch) pDispatch->Release(); if (pPrintQueue) pPrintQueue->Release(); if (FAILED(hr) && ppszObjectDN && *ppszObjectDN) FreeGlobalStr(*ppszObjectDN); return hr2dw(hr); } HRESULT SetProperties( HANDLE hPrinter, PWSTR pszServerName, PWSTR pszShortServerName, PWSTR pszUNCName, PWSTR pszPrinterName, DWORD dwVersion, IADs *pPrintQueue ) { HRESULT hr; hr = SetMandatoryProperties(pszServerName, pszShortServerName, pszUNCName, pszPrinterName, dwVersion, pPrintQueue); BAIL_ON_FAILURE(hr); SetSpoolerProperties(hPrinter, pPrintQueue, dwVersion); SetDriverProperties(hPrinter, pPrintQueue); error: return hr; } HRESULT SetMandatoryProperties( PWSTR pszServerName, PWSTR pszShortServerName, PWSTR pszUNCName, PWSTR pszPrinterName, DWORD dwVersion, IADs *pPrintQueue ) { HRESULT hr; // ServerName hr = put_BSTR_Property(pPrintQueue, SPLDS_SERVER_NAME, pszServerName); BAIL_ON_FAILURE(hr); // ShortServerName hr = put_BSTR_Property(pPrintQueue, SPLDS_SHORT_SERVER_NAME, pszShortServerName); BAIL_ON_FAILURE(hr); // UNC Name hr = put_BSTR_Property(pPrintQueue, SPLDS_UNC_NAME, pszUNCName); BAIL_ON_FAILURE(hr); // PrinterName hr = put_BSTR_Property(pPrintQueue, SPLDS_PRINTER_NAME, pszPrinterName); BAIL_ON_FAILURE(hr); // versionNumber hr = put_DWORD_Property(pPrintQueue, SPLDS_VERSION_NUMBER, &dwVersion); BAIL_ON_FAILURE(hr); hr = pPrintQueue->SetInfo(); if (FAILED(hr)) pPrintQueue->GetInfo(); error: return hr; } HRESULT SetSpoolerProperties( HANDLE hPrinter, IADs *pPrintQueue, DWORD dwVersion ) { HRESULT hr = S_OK; PPRINTER_INFO_2 pInfo2 = NULL; DWORD cbNeeded; BYTE Byte; PWSTR psz; // Get PRINTER_INFO_2 properties if (!GetPrinter(hPrinter, 2, (PBYTE) pInfo2, 0, &cbNeeded)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { hr = dw2hr(GetLastError()); goto error; } if (!(pInfo2 = (PPRINTER_INFO_2) AllocSplMem(cbNeeded))) { hr = dw2hr(GetLastError()); goto error; } if (!GetPrinter(hPrinter, 2, (PBYTE) pInfo2, cbNeeded, &cbNeeded)) { hr = dw2hr(GetLastError()); goto error; } } // Description hr = PublishDsData( pPrintQueue, SPLDS_DESCRIPTION, REG_SZ, (PBYTE) pInfo2->pComment); // Driver-Name hr = PublishDsData( pPrintQueue, SPLDS_DRIVER_NAME, REG_SZ, (PBYTE) pInfo2->pDriverName); // Location hr = PublishDsData( pPrintQueue, SPLDS_LOCATION, REG_SZ, (PBYTE) pInfo2->pLocation); // portName (Port1,Port2,Port3) if (pInfo2->pPortName) { PWSTR pszPortName; // copy comma delimited strings to Multi-sz format pszPortName = DelimString2MultiSz(pInfo2->pPortName, L','); if (pszPortName) { hr = PublishDsData( pPrintQueue, SPLDS_PORT_NAME, REG_MULTI_SZ, (PBYTE) pszPortName); FreeSplMem(pszPortName); } } // startTime hr = PublishDsData( pPrintQueue, SPLDS_PRINT_START_TIME, REG_DWORD, (PBYTE) &pInfo2->StartTime); // endTime hr = PublishDsData( pPrintQueue, SPLDS_PRINT_END_TIME, REG_DWORD, (PBYTE) &pInfo2->UntilTime); // keepPrintedJobs Byte = pInfo2->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS ? 1 : 0; hr = PublishDsData( pPrintQueue, SPLDS_PRINT_KEEP_PRINTED_JOBS, REG_BINARY, (PBYTE) &Byte ); // printSeparatorFile hr = PublishDsData( pPrintQueue, SPLDS_PRINT_SEPARATOR_FILE, REG_SZ, (PBYTE) pInfo2->pSepFile); // printShareName hr = PublishDsData( pPrintQueue, SPLDS_PRINT_SHARE_NAME, REG_SZ, (PBYTE) pInfo2->pShareName); // printSpooling if (pInfo2->Attributes & PRINTER_ATTRIBUTE_DIRECT) { psz = L"PrintDirect"; } else if (pInfo2->Attributes & PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST) { psz = L"PrintAfterSpooled"; } else { psz = L"PrintWhileSpooling"; } hr = PublishDsData( pPrintQueue, SPLDS_PRINT_SPOOLING, REG_SZ, (PBYTE) psz); // priority hr = PublishDsData( pPrintQueue, SPLDS_PRIORITY, REG_DWORD, (PBYTE) &pInfo2->Priority); // // Non-Info2 properties // URL - downlevel machines don't support http printers, so don't publish useless url // if (dwVersion >= WIN2000_SPOOLER_VERSION) { DWORD dwRet, dwType; PWSTR pszUrl = NULL; // // Get the url from the print server // dwRet = GetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_URL, &dwType, (PBYTE) pszUrl, 0, &cbNeeded); if (dwRet == ERROR_MORE_DATA) { if ((pszUrl = (PWSTR) AllocSplMem(cbNeeded))) { dwRet = GetPrinterDataEx( hPrinter, SPLDS_SPOOLER_KEY, SPLDS_URL, &dwType, (PBYTE) pszUrl, cbNeeded, &cbNeeded); if (dwRet == ERROR_SUCCESS && dwType == REG_SZ) { hr = PublishDsData( pPrintQueue, SPLDS_URL, REG_SZ, (PBYTE) pszUrl); } FreeSplMem(pszUrl); } } } error: FreeSplMem(pInfo2); return hr; } HRESULT SetDriverProperties( HANDLE hPrinter, IADs *pPrintQueue ) { DWORD i, cbBytes, dwCount; LPWSTR pStr; DWORD dwResult; LPWSTR pOutput = NULL, pTemp = NULL, pTemp1 = NULL; DWORD cOutputBytes, cTempBytes; POINTS point; WCHAR pBuf[100]; BOOL bInSplSem = TRUE; DWORD dwTemp, dwPrintRate, dwPrintRateUnit, dwPrintPPM; HRESULT hr = S_OK; PPRINTER_INFO_2 pInfo2 = NULL; PWSTR pszUNCName; // Get UNCName hr = GetPrinterInfo2(hPrinter, &pInfo2); BAIL_ON_FAILURE(hr); if (!pInfo2) { hr = dw2hr(GetLastError()); goto error; } pszUNCName = pInfo2->pPrinterName; // *** DeviceCapability properties *** pOutput = (PWSTR) AllocSplMem(cOutputBytes = 200); if (!pOutput) { hr = dw2hr(GetLastError()); goto error; } pTemp = (PWSTR) AllocSplMem(cTempBytes = 200); if (!pTemp) { hr = dw2hr(GetLastError()); goto error; } // printBinNames DevCapMultiSz( pszUNCName, pPrintQueue, DC_BINNAMES, 24, SPLDS_PRINT_BIN_NAMES); // printCollate (awaiting DC_COLLATE) dwResult = DeviceCapabilities( pszUNCName, NULL, DC_COLLATE, NULL, NULL); if (dwResult != GDI_ERROR) { hr = PublishDsData( pPrintQueue, SPLDS_PRINT_COLLATE, REG_BINARY, (PBYTE) &dwResult); } // printColor dwResult = DeviceCapabilities( pszUNCName, NULL, DC_COLORDEVICE, NULL, NULL); if (dwResult == GDI_ERROR) { // Try alternative method dwResult = ThisIsAColorPrinter(pszUNCName); } hr = PublishDsData( pPrintQueue, SPLDS_PRINT_COLOR, REG_BINARY, (PBYTE) &dwResult); // printDuplexSupported dwResult = DeviceCapabilities( pszUNCName, NULL, DC_DUPLEX, NULL, NULL); if (dwResult != GDI_ERROR) { hr = PublishDsData( pPrintQueue, SPLDS_PRINT_DUPLEX_SUPPORTED, REG_BINARY, (PBYTE) &dwResult); } // printStaplingSupported dwResult = DeviceCapabilities( pszUNCName, NULL, DC_STAPLE, NULL, NULL); if (dwResult != GDI_ERROR) { hr = PublishDsData( pPrintQueue, SPLDS_PRINT_STAPLING_SUPPORTED, REG_BINARY, (PBYTE) &dwResult); } // printMaxXExtent & printMaxYExtent dwResult = DeviceCapabilities( pszUNCName, NULL, DC_MAXEXTENT, NULL, NULL); if (dwResult != GDI_ERROR) { *((DWORD *) &point) = dwResult; dwTemp = (DWORD) point.x; hr = PublishDsData( pPrintQueue, SPLDS_PRINT_MAX_X_EXTENT, REG_DWORD, (PBYTE) &dwTemp); dwTemp = (DWORD) point.y; hr = PublishDsData( pPrintQueue, SPLDS_PRINT_MAX_Y_EXTENT, REG_DWORD, (PBYTE) &dwTemp); } // printMinXExtent & printMinYExtent dwResult = DeviceCapabilities( pszUNCName, NULL, DC_MINEXTENT, NULL, NULL); if (dwResult != GDI_ERROR) { *((DWORD *) &point) = dwResult; dwTemp = (DWORD) point.x; hr = PublishDsData( pPrintQueue, SPLDS_PRINT_MIN_X_EXTENT, REG_DWORD, (PBYTE) &dwTemp); dwTemp = (DWORD) point.y; hr = PublishDsData( pPrintQueue, SPLDS_PRINT_MIN_Y_EXTENT, REG_DWORD, (PBYTE) &dwTemp); } // printMediaSupported DevCapMultiSz( pszUNCName, pPrintQueue, DC_PAPERNAMES, 64, SPLDS_PRINT_MEDIA_SUPPORTED); // printMediaReady DevCapMultiSz( pszUNCName, pPrintQueue, DC_MEDIAREADY, 64, SPLDS_PRINT_MEDIA_READY); // printNumberUp dwResult = DeviceCapabilities( pszUNCName, NULL, DC_NUP, NULL, NULL); if (dwResult != GDI_ERROR) { // DS NUp is boolean dwResult = !!dwResult; hr = PublishDsData( pPrintQueue, SPLDS_PRINT_DUPLEX_SUPPORTED, REG_DWORD, (PBYTE) &dwResult); } // printMemory dwResult = DeviceCapabilities( pszUNCName, NULL, DC_PRINTERMEM, NULL, NULL); if (dwResult != GDI_ERROR) { hr = PublishDsData( pPrintQueue, SPLDS_PRINT_MEMORY, REG_DWORD, (PBYTE) &dwResult); } // printOrientationsSupported dwResult = DeviceCapabilities( pszUNCName, NULL, DC_ORIENTATION, NULL, NULL); if (dwResult != GDI_ERROR) { if (dwResult == 90 || dwResult == 270) { wcscpy(pBuf, L"PORTRAIT"); wcscpy(pStr = pBuf + wcslen(pBuf) + 1, L"LANDSCAPE"); } else { wcscpy(pStr = pBuf, L"PORTRAIT"); } pStr += wcslen(pStr) + 1; *pStr++ = L'\0'; hr = PublishDsData( pPrintQueue, SPLDS_PRINT_ORIENTATIONS_SUPPORTED, REG_MULTI_SZ, (PBYTE) pBuf); } // printMaxResolutionSupported dwResult = DeviceCapabilities( pszUNCName, NULL, DC_ENUMRESOLUTIONS, NULL, NULL); if (dwResult != GDI_ERROR) { if (cOutputBytes < dwResult*2*sizeof(DWORD)) { if(!(pTemp1 = (PWSTR) ReallocSplMem(pOutput, 0, cOutputBytes = dwResult*2*sizeof(DWORD)))) goto error; pOutput = pTemp1; } dwResult = DeviceCapabilities( pszUNCName, NULL, DC_ENUMRESOLUTIONS, pOutput, NULL); if (dwResult == GDI_ERROR) goto error; // Find the maximum resolution: we have dwResult*2 resolutions to check for(i = dwTemp = 0 ; i < dwResult*2 ; ++i) { if (((DWORD *) pOutput)[i] > dwTemp) dwTemp = ((DWORD *) pOutput)[i]; } hr = PublishDsData( pPrintQueue, SPLDS_PRINT_MAX_RESOLUTION_SUPPORTED, REG_DWORD, (PBYTE) &dwTemp); } // printLanguage DevCapMultiSz( pszUNCName, pPrintQueue, DC_PERSONALITY, 32, SPLDS_PRINT_LANGUAGE); // printRate // NOTE: If PrintRate is 0, no value is published dwResult = DeviceCapabilities( pszUNCName, NULL, DC_PRINTRATE, NULL, NULL); dwPrintRate = dwResult ? dwResult : GDI_ERROR; if (dwPrintRate != GDI_ERROR) { hr = PublishDsData( pPrintQueue, SPLDS_PRINT_RATE, REG_DWORD, (PBYTE) &dwPrintRate); } // printRateUnit dwResult = DeviceCapabilities( pszUNCName, NULL, DC_PRINTRATEUNIT, NULL, NULL); dwPrintRateUnit = dwResult; if (dwPrintRateUnit != GDI_ERROR) { switch (dwPrintRateUnit) { case PRINTRATEUNIT_PPM: pStr = L"PagesPerMinute"; break; case PRINTRATEUNIT_CPS: pStr = L"CharactersPerSecond"; break; case PRINTRATEUNIT_LPM: pStr = L"LinesPerMinute"; break; case PRINTRATEUNIT_IPM: pStr = L"InchesPerMinute"; break; default: pStr = L""; break; } hr = PublishDsData( pPrintQueue, SPLDS_PRINT_RATE_UNIT, REG_SZ, (PBYTE) pStr); } // printPagesPerMinute // DevCap returns 0 if there is no entry in GPD dwResult = DeviceCapabilities( pszUNCName, NULL, DC_PRINTRATEPPM, NULL, NULL); if (dwResult == GDI_ERROR) dwResult = 0; dwPrintPPM = dwResult; // If dwPrintPPM == 0, then calculate PPM from PrintRate if (dwPrintPPM == 0) { if (dwPrintRate == GDI_ERROR) { dwPrintPPM = GDI_ERROR; } else { switch (dwPrintRateUnit) { case PRINTRATEUNIT_PPM: dwPrintPPM = dwPrintRate; break; case PRINTRATEUNIT_CPS: case PRINTRATEUNIT_LPM: dwPrintPPM = dwPrintRate/PPM_FACTOR; if (dwPrintPPM == 0) dwPrintPPM = 1; // min PPM is 1 break; default: dwPrintPPM = GDI_ERROR; break; } } } if (dwPrintPPM != GDI_ERROR) { hr = PublishDsData( pPrintQueue, SPLDS_PRINT_PAGES_PER_MINUTE, REG_DWORD, (PBYTE) &dwPrintPPM); } // printDriverVersion dwResult = DeviceCapabilities( pszUNCName, NULL, DC_VERSION, NULL, NULL); if (dwResult != GDI_ERROR) { hr = PublishDsData( pPrintQueue, SPLDS_DRIVER_VERSION, REG_DWORD, (PBYTE) &dwResult); } error: FreeSplMem(pInfo2); if (pOutput) FreeSplMem(pOutput); if (pTemp) FreeSplMem(pTemp); return hr; } HRESULT PublishDsData( IADs *pADs, PWSTR pValue, DWORD dwType, PBYTE pData ) { HRESULT hr; BOOL bCreated = FALSE; switch (dwType) { case REG_SZ: hr = put_BSTR_Property(pADs, pValue, (LPWSTR) pData); break; case REG_MULTI_SZ: hr = put_MULTISZ_Property(pADs, pValue, (LPWSTR) pData); break; case REG_DWORD: hr = put_DWORD_Property(pADs, pValue, (DWORD *) pData); break; case REG_BINARY: hr = put_BOOL_Property(pADs, pValue, (BOOL *) pData); break; default: hr = dw2hr(ERROR_INVALID_PARAMETER); } BAIL_ON_FAILURE(hr); hr = pADs->SetInfo(); if (FAILED(hr)) pADs->GetInfo(); error: return hr; }