|
|
/*++
Copyright (c) 1990-1994 Microsoft Corporation
Module Name:
local.c
Abstract:
This module provides all the public exported APIs relating to Printer and Job management for the Local Print Providor
Author:
Dave Snipp (DaveSn) 15-Mar-1991
Revision History:
16-Jun-1992 JohnRo net print vs. UNICODE. July 1994 MattFe Caching
--*/ #include <winerror.h>
#include <windows.h>
#include <winspool.h>
#include <lm.h>
#include <w32types.h>
#include <local.h>
#include <splcom.h> // DBGMSG
#include <wininet.h>
#include <offsets.h>
#include <string.h>
char szPMRaw[]="PM_Q_RAW";
WCHAR *szAdmin = L"ADMIN$";
extern HANDLE hNetApi; extern NET_API_STATUS (*pfnNetServerGetInfo)(); extern NET_API_STATUS (*pfnNetApiBufferFree)();
HMODULE hSpoolssDll = NULL; FARPROC pfnSpoolssEnumPorts = NULL;
DWORD GetPortSize( PWINIPORT pIniPort, DWORD Level ) { DWORD cb; WCHAR szMonitor[MAX_PATH+1], szPort[MAX_PATH+1];
switch (Level) {
case 1:
cb=sizeof(PORT_INFO_1) + wcslen(pIniPort->pName)*sizeof(WCHAR) + sizeof(WCHAR); break;
case 2: LoadString(hInst, IDS_MONITOR_NAME, szMonitor, sizeof(szMonitor)/sizeof(szMonitor[0])-1); LoadString(hInst, IDS_PORT_NAME, szPort, sizeof(szPort)/sizeof(szPort[0])-1); cb = wcslen(pIniPort->pName) + 1 + wcslen(szMonitor) + 1 + wcslen(szPort) + 1; cb *= sizeof(WCHAR); cb += sizeof(PORT_INFO_2); break;
default: cb = 0; break; }
return cb; }
LPBYTE CopyIniPortToPort( PWINIPORT pIniPort, DWORD Level, LPBYTE pPortInfo, LPBYTE pEnd ) { LPWSTR *SourceStrings, *pSourceStrings; DWORD *pOffsets; WCHAR szMonitor[MAX_PATH+1], szPort[MAX_PATH+1]; DWORD Count; LPPORT_INFO_2 pPort2 = (LPPORT_INFO_2) pPortInfo;
switch (Level) {
case 1: pOffsets = PortInfo1Strings; break;
case 2: pOffsets = PortInfo2Strings; break;
default: DBGMSG(DBG_ERROR, ("CopyIniPortToPort: invalid level %d", Level)); return pEnd; }
for ( Count = 0 ; pOffsets[Count] != -1 ; ++Count ) { }
SourceStrings = pSourceStrings = AllocSplMem(Count * sizeof(LPWSTR));
if ( !SourceStrings ) { DBGMSG(DBG_WARNING, ("CopyIniPortToPort: Failed to alloc port source strings.\n")); return NULL; }
switch (Level) {
case 1:
*pSourceStrings++=pIniPort->pName; break;
case 2: *pSourceStrings++=pIniPort->pName; LoadString(hInst, IDS_MONITOR_NAME, szMonitor, sizeof(szMonitor)/sizeof(szMonitor[0])-1); LoadString(hInst, IDS_PORT_NAME, szPort, sizeof(szPort)/sizeof(szPort[0])-1); *pSourceStrings++ = szMonitor; *pSourceStrings++ = szPort; pPort2->fPortType = PORT_TYPE_WRITE; pPort2->Reserved = 0; break;
default: return pEnd; DBGMSG(DBG_ERROR, ("CopyIniPortToPort: invalid level %d", Level)); }
pEnd = PackStrings(SourceStrings, pPortInfo, pOffsets, pEnd); FreeSplMem(SourceStrings); return pEnd; }
/* PortExists
* * Calls EnumPorts to check whether the port name already exists. * This asks every monitor, rather than just this one. * The function will return TRUE if the specified port is in the list. * If an error occurs, the return is FALSE and the variable pointed * to by pError contains the return from GetLastError(). * The caller must therefore always check that *pError == NO_ERROR. */ BOOL PortExists( LPWSTR pName, LPWSTR pPortName, PDWORD pError ) { DWORD cbNeeded; DWORD cReturned; DWORD cbPorts; LPPORT_INFO_1 pPorts; DWORD i; BOOL Found = TRUE;
*pError = NO_ERROR;
if (!hSpoolssDll) {
hSpoolssDll = LoadLibrary(L"SPOOLSS.DLL");
if (hSpoolssDll) { pfnSpoolssEnumPorts = GetProcAddress(hSpoolssDll, "EnumPortsW"); if (!pfnSpoolssEnumPorts) {
*pError = GetLastError(); FreeLibrary(hSpoolssDll); hSpoolssDll = NULL; }
} else {
*pError = GetLastError(); } }
if (!pfnSpoolssEnumPorts) return FALSE;
if (!(*pfnSpoolssEnumPorts)(pName, 1, NULL, 0, &cbNeeded, &cReturned)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { cbPorts = cbNeeded;
pPorts = AllocSplMem(cbPorts);
if (pPorts) { if ((*pfnSpoolssEnumPorts)(pName, 1, (LPBYTE)pPorts, cbPorts, &cbNeeded, &cReturned)) { Found = FALSE;
for (i = 0; i < cReturned; i++) { if (!lstrcmpi(pPorts[i].pName, pPortName)) Found = TRUE; } } }
FreeSplMem(pPorts); } }
else Found = FALSE;
return Found; }
BOOL LMOpenPrinter( LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTS pDefault ) { PWINIPORT pIniPort; PWSPOOL pSpool; DWORD cb; PUSE_INFO_0 pUseInfo; LPWSTR pShare; WCHAR PrinterName[MAX_UNC_PRINTER_NAME]; DWORD cbNeeded; DWORD rc; BYTE Buffer[4]; DWORD Error = NO_ERROR; DWORD dwEntry = 0xffffffff; PSERVER_INFO_101 pserver_info_101 = NULL;
/* If we already have an INI port entry by this name, don't worry
* about hitting the network. This ensures that we don't try to * make a network call when we're not impersonating - like on * bootup. */ if (!(pIniPort = FindPort(pPrinterName, pIniFirstPort))) {
if (!NetUseGetInfo(NULL, pPrinterName, 0, (LPBYTE *)&pUseInfo)) pPrinterName = AllocSplStr(pUseInfo->ui0_remote);
NetApiBufferFree( (LPVOID) pUseInfo ); }
if ( !pPrinterName || *pPrinterName != L'\\' || *(pPrinterName+1) != L'\\' || wcslen(pPrinterName) + 1 > MAX_UNC_PRINTER_NAME ) {
SetLastError(ERROR_INVALID_NAME); return FALSE; }
wcscpy(PrinterName, pPrinterName); pShare=wcschr(PrinterName+2, L'\\');
if ( !pShare ) {
SetLastError(ERROR_INVALID_NAME); return FALSE; }
*pShare++=0;
if (!pIniPort) {
/* Verify that this guy actually exists.
* Call it with a zero-length buffer, * and see if the error is that the buffer isn't big enough. * If we make Buffer = NULL, it fails with * ERROR_INVALID_PARAMETER, which is pretty abysmal, * so we must pass a Buffer address. * (Actually, it seems to accept any non-NULL value, * regardless of whether it's a valid address.) */
EnterSplSem(); dwEntry = FindEntryinLMCache(PrinterName, pShare); LeaveSplSem();
if (dwEntry == -1) {
DBGMSG(DBG_TRACE, ("We haven't cached this entry so we have to hit the net\n"));
rc = RxPrintQGetInfo(PrinterName, /* e.g. \\msprint07 */ pShare, /* e.g. l07corpa */ 0, /* Level 0 */ Buffer, /* Dummy - won't get filled in */ 0, /* Length of buffer */ &cbNeeded); /* How much we need - we'll ignore */
DBGMSG(DBG_INFO, ("LMOpenPrinter!RxPrintQGetInfo returned %d\n", rc));
if (rc == ERROR_ACCESS_DENIED) {
/* The print share exists; we just don't have access to it.
*/ SetLastError(ERROR_ACCESS_DENIED); return FALSE; }
if (!((rc == ERROR_MORE_DATA) ||(rc == NERR_BufTooSmall) ||(rc == ERROR_INSUFFICIENT_BUFFER))) { SetLastError(ERROR_INVALID_NAME); return FALSE; }
//
// Be sure that we are connecting to a downlevel server.
// If the server is Windows NT Machine, then fail the call:
// downlevel NT connections won't work properly because
// "\\Server\Printer Name" will get past RxPrintQGetInfo, but
// we can't do CreateFile on it (we need to user the share
// name). Downlevel connects also lose admin functionality.
//
if (pfnNetServerGetInfo) {
rc = pfnNetServerGetInfo(PrinterName, 101, &pserver_info_101);
//
// Advanced Server for Unix (ASU) by AT&T reports that
// they are TYPE_NT even though they don't support the
// rpc interface. They also set TYPE_XENIX_SERVER.
// Since the need lm connections, allow them when
// TYPE_XENIX_SERVER is specified.
//
// Also make this change for SERVER_VMS and SERVER_OSF.
// These changes are for AT&T also.
//
// Note: this will also allow accidental downlevel
// connections to any servers that set TYPE_XENIX_SERVER,
// TYPE_SERVER_VMS, or TYPE_SERVER_OSF.
//
if (!rc && (pserver_info_101->sv101_type & SV_TYPE_NT) && !(pserver_info_101->sv101_type & ( SV_TYPE_XENIX_SERVER | SV_TYPE_SERVER_VMS | SV_TYPE_SERVER_OSF))) {
DBGMSG(DBG_WARNING, ("NetServerGetInfo indicates %ws is a WinNT\n", PrinterName)); pfnNetApiBufferFree((LPVOID)pserver_info_101);
SetLastError(ERROR_INVALID_NAME); return FALSE; }
//
// Now free the buffer
//
if (pserver_info_101) { pfnNetApiBufferFree((LPVOID)pserver_info_101); } }
//
// Add entry to the cache
//
EnterSplSem(); AddEntrytoLMCache(PrinterName, pShare); LeaveSplSem(); } }
/* Make sure we can write to the print share.
* This will fail if there's an invalid password. */ /* hTest = CreateFile(pPrinterName, GENERIC_WRITE, 0, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hTest == INVALID_HANDLE_VALUE) {
DBGMSG(DBG_WARNING, ("Can't write to %ws: Error %d\n", pPrinterName, GetLastError())); return FALSE; }
CloseHandle(hTest); */
/* Make sure there's a port of this name so that
* EnumPorts will return it: */ if (!PortExists(NULL, pPrinterName, &Error) && (Error == NO_ERROR)) { if (CreatePortEntry(pPrinterName, &pIniFirstPort)) { CreateRegistryEntry(pPrinterName); } }
if (Error != NO_ERROR) return FALSE;
cb = sizeof(WSPOOL);
EnterSplSem(); pSpool = AllocWSpool(); LeaveSplSem();
if ( pSpool != NULL ) {
pSpool->pServer = AllocSplStr(PrinterName); pSpool->pShare = AllocSplStr(pShare); pSpool->Status = 0; pSpool->Type = LM_HANDLE;
} else {
DBGMSG(DBG_TRACE,("Error: LMOpenPrinter to return ERROR_NOT_ENOUGH_MEMORY\n")); SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE; }
*phPrinter = (HANDLE)pSpool;
return TRUE; }
BOOL LMSetPrinter( HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD Command ) { PWSPOOL pSpool = (PWSPOOL)hPrinter;
API_RET_TYPE uReturnCode; DWORD dwParmError; USE_INFO_1 UseInfo1; PUSE_INFO_1 pUseInfo1 = &UseInfo1; WCHAR szRemoteShare[MAX_PATH]; DWORD dwRet;
if (!pSpool || pSpool->signature != WSJ_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE); return FALSE; }
if( (dwRet = StrNCatBuff(szRemoteShare, COUNTOF(szRemoteShare), pSpool->pServer, L"\\", szAdmin, NULL )) != ERROR_SUCCESS ) {
SetLastError(dwRet); return FALSE; }
pUseInfo1->ui1_local = NULL; pUseInfo1->ui1_remote = szRemoteShare; pUseInfo1->ui1_password = NULL; pUseInfo1->ui1_asg_type = 0; dwParmError = 0;
switch (Command) {
case 0: break;
case PRINTER_CONTROL_PURGE: uReturnCode = RxPrintQPurge(pSpool->pServer, pSpool->pShare); if (uReturnCode) {
uReturnCode = NetUseAdd(NULL, 1, (LPBYTE)pUseInfo1, &dwParmError); if (uReturnCode == ERROR_ACCESS_DENIED) { SetLastError(ERROR_ACCESS_DENIED); return(FALSE);
} else {
uReturnCode = RxPrintQPurge(pSpool->pServer, pSpool->pShare); if (uReturnCode == ERROR_ACCESS_DENIED) { NetUseDel(NULL, pUseInfo1->ui1_remote, USE_FORCE); SetLastError(ERROR_ACCESS_DENIED); return(FALSE); } NetUseDel(NULL, pUseInfo1->ui1_remote, USE_FORCE); } } break;
case PRINTER_CONTROL_RESUME: uReturnCode = RxPrintQContinue(pSpool->pServer, pSpool->pShare); if (uReturnCode) {
uReturnCode = NetUseAdd(NULL, 1, (LPBYTE)pUseInfo1, &dwParmError); if (uReturnCode == ERROR_ACCESS_DENIED) { SetLastError(ERROR_ACCESS_DENIED); return(FALSE);
} else { uReturnCode = RxPrintQContinue(pSpool->pServer, pSpool->pShare); if (uReturnCode == ERROR_ACCESS_DENIED) { NetUseDel(NULL, pUseInfo1->ui1_remote, USE_FORCE); SetLastError(ERROR_ACCESS_DENIED); return(FALSE); } NetUseDel(NULL, pUseInfo1->ui1_remote, USE_FORCE); } } break;
case PRINTER_CONTROL_PAUSE: uReturnCode = RxPrintQPause(pSpool->pServer, pSpool->pShare); if (uReturnCode) {
uReturnCode = NetUseAdd(NULL, 1, (LPBYTE)pUseInfo1, &dwParmError); if (uReturnCode == ERROR_ACCESS_DENIED) { SetLastError(ERROR_ACCESS_DENIED); return(FALSE);
} else { uReturnCode = RxPrintQPause(pSpool->pServer, pSpool->pShare); if (uReturnCode) { NetUseDel(NULL, pUseInfo1->ui1_remote, USE_FORCE); SetLastError(ERROR_ACCESS_DENIED); return(FALSE); } NetUseDel(NULL, pUseInfo1->ui1_remote, USE_FORCE); }
} break;
default: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; break; }
//
// SetPrinter successful - so pulse here if event set,
// or reply to spooler.
//
LMSetSpoolChange(pSpool);
return TRUE; }
#define Nullstrlen(psz) ((psz) ? wcslen(psz)*sizeof(WCHAR)+sizeof(WCHAR) : 0)
DWORD GetPrqInfo3Size( PWSPOOL pSpool, PRQINFO3 *pPrqInfo3, DWORD Level ) { DWORD cb;
switch (Level) {
case 1:
//
// 3 extra chars
// (2 NULL terminators, 1 for '\' (server/share separator)
//
cb=sizeof(PRINTER_INFO_1) + wcslen(pSpool->pServer)*sizeof(WCHAR)*2 + wcslen(pSpool->pShare)*sizeof(WCHAR) + sizeof(WCHAR)*3 + Nullstrlen(pPrqInfo3->pszComment); break;
case 2:
cb = sizeof(PRINTER_INFO_2) + wcslen(pSpool->pServer)*sizeof(WCHAR) + sizeof(WCHAR) + wcslen(pSpool->pShare)*sizeof(WCHAR) + sizeof(WCHAR) + wcslen(pSpool->pServer)*sizeof(WCHAR) + sizeof(WCHAR) + wcslen(pSpool->pShare)*sizeof(WCHAR) + sizeof(WCHAR) + Nullstrlen(pPrqInfo3->pszPrinters) + Nullstrlen(pPrqInfo3->pszDriverName) + Nullstrlen(pPrqInfo3->pszComment) + Nullstrlen(pPrqInfo3->pszSepFile) + Nullstrlen(pPrqInfo3->pszPrProc) + wcslen(L"RAW")*sizeof(WCHAR) + sizeof(WCHAR) + Nullstrlen(pPrqInfo3->pszParms); break;
default: cb = 0; break; }
return cb; }
// This can be radically tidied up
// We should probably use the stack for the
// array of string pointers rather than dynamically allocating it !
LPBYTE CopyPrqInfo3ToPrinter( PWSPOOL pSpool, PRQINFO3 *pPrqInfo3, DWORD Level, LPBYTE pPrinterInfo, LPBYTE pEnd ) { LPWSTR *pSourceStrings, *SourceStrings; PPRINTER_INFO_2 pPrinter2 = (PPRINTER_INFO_2)pPrinterInfo; PPRINTER_INFO_1 pPrinter1 = (PPRINTER_INFO_1)pPrinterInfo; DWORD i; DWORD *pOffsets; DWORD RetVal = ERROR_SUCCESS; WCHAR szFileName[MAX_PATH];
switch (Level) {
case 1: pOffsets = PrinterInfo1Strings; break;
case 2: pOffsets = PrinterInfo2Strings; break;
default: return pEnd; }
for (i=0; pOffsets[i] != -1; i++) { }
SourceStrings = pSourceStrings = AllocSplMem(i * sizeof(LPWSTR));
if (!SourceStrings) return NULL;
switch (Level) {
case 1: *pSourceStrings++=pSpool->pServer;
RetVal = StrNCatBuff( szFileName, COUNTOF(szFileName), pSpool->pServer, L"\\", pSpool->pShare, NULL ); if ( RetVal != ERROR_SUCCESS ) { break; }
*pSourceStrings++=szFileName; *pSourceStrings++=pPrqInfo3->pszComment;
pEnd = PackStrings(SourceStrings, pPrinterInfo, pOffsets, pEnd);
pPrinter1->Flags = PRINTER_ENUM_REMOTE | PRINTER_ENUM_NAME; break;
case 2:
RetVal = StrNCatBuff( szFileName, COUNTOF(szFileName), pSpool->pServer, L"\\", pSpool->pShare, NULL ); if ( RetVal != ERROR_SUCCESS ) { break; }
*pSourceStrings++=pSpool->pServer;
*pSourceStrings++=szFileName;
*pSourceStrings++=pSpool->pShare; *pSourceStrings++=pPrqInfo3->pszPrinters; *pSourceStrings++=pPrqInfo3->pszDriverName ? pPrqInfo3->pszDriverName : L""; *pSourceStrings++=pPrqInfo3->pszComment; *pSourceStrings++=NULL; *pSourceStrings++=pPrqInfo3->pszSepFile; *pSourceStrings++=pPrqInfo3->pszPrProc; *pSourceStrings++=L"RAW"; *pSourceStrings++=pPrqInfo3->pszParms;
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter2, pOffsets, pEnd);
pPrinter2->pDevMode=0; pPrinter2->Attributes=PRINTER_ATTRIBUTE_QUEUED; pPrinter2->Priority=pPrqInfo3->uPriority; pPrinter2->DefaultPriority=pPrqInfo3->uPriority; pPrinter2->StartTime=pPrqInfo3->uStartTime; pPrinter2->UntilTime=pPrqInfo3->uUntilTime;
pPrinter2->Status=0;
if (pPrqInfo3->fsStatus & PRQ3_PAUSED) pPrinter2->Status|=PRINTER_STATUS_PAUSED;
if (pPrqInfo3->fsStatus & PRQ3_PENDING) pPrinter2->Status|=PRINTER_STATUS_PENDING_DELETION;
pPrinter2->cJobs=pPrqInfo3->cJobs; pPrinter2->AveragePPM=0; break;
default: return pEnd; }
FreeSplMem(SourceStrings);
if ( RetVal == ERROR_SUCCESS ) { return pEnd; } else { return NULL; } }
BOOL LMGetPrinter( HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded ) { PWSPOOL pSpool = (PWSPOOL)hPrinter; PRQINFO3 *pPrqInfo3; PRQINFO3 PrqInfo3; PRQINFO *pPrqInfo=NULL; DWORD cb = 0x400; DWORD rc; DWORD cbNeeded; LPBYTE pInfo = NULL; BOOL bWFW = FALSE;
if (Level >= 7) { SetLastError(ERROR_INVALID_LEVEL); return FALSE; }
if (!pSpool || pSpool->signature != WSJ_SIGNATURE) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; }
pPrqInfo3 = AllocSplMem(cb);
if ( !pPrqInfo3 ) goto Cleanup;
if ( rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare, 3, (PBYTE)pPrqInfo3, cb, &cbNeeded)) {
if (rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall) {
pPrqInfo3=ReallocSplMem(pPrqInfo3, 0, cbNeeded);
if ( !pPrqInfo3 ) goto Cleanup;
cb=cbNeeded;
if (rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare, 3, (PBYTE)pPrqInfo3, cb, &cbNeeded)) {
SetLastError(rc); goto Cleanup; }
} else if (rc == ERROR_INVALID_LEVEL) {
// Must be WFW
if (rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare, 1, (PBYTE)pPrqInfo3, cb, &cbNeeded)) {
if (rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall) {
pPrqInfo3 = ReallocSplMem(pPrqInfo3, 0, cbNeeded);
if ( !pPrqInfo3 ) goto Cleanup;
cb=cbNeeded;
if (rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare, 1, (PBYTE)pPrqInfo3, cb, &cbNeeded)) {
SetLastError(rc); goto Cleanup; }
} else {
SetLastError(rc); goto Cleanup; } }
pPrqInfo = (PRQINFO *)pPrqInfo3;
PrqInfo3.pszName = pPrqInfo->szName; PrqInfo3.uPriority = pPrqInfo->uPriority; PrqInfo3.uStartTime = pPrqInfo->uStartTime; PrqInfo3.uUntilTime = pPrqInfo->uUntilTime; PrqInfo3.pad1 = 0; PrqInfo3.pszSepFile = pPrqInfo->pszSepFile; PrqInfo3.pszPrProc = pPrqInfo->pszPrProc; PrqInfo3.pszParms = pPrqInfo->pszDestinations; PrqInfo3.pszComment = pPrqInfo->pszComment; PrqInfo3.fsStatus = pPrqInfo->fsStatus; PrqInfo3.cJobs = pPrqInfo->cJobs; PrqInfo3.pszPrinters = pPrqInfo->pszDestinations; PrqInfo3.pszDriverName = L""; PrqInfo3.pDriverData = NULL;
bWFW = TRUE;
} else {
SetLastError(rc); goto Cleanup; } }
cbNeeded=GetPrqInfo3Size(pSpool, bWFW ? &PrqInfo3 : pPrqInfo3, Level);
*pcbNeeded=cbNeeded;
if (cbNeeded > cbBuf) {
SetLastError(ERROR_INSUFFICIENT_BUFFER); goto Cleanup; }
pInfo = CopyPrqInfo3ToPrinter(pSpool, bWFW ? &PrqInfo3 : pPrqInfo3, Level, pPrinter, (LPBYTE)pPrinter+cbBuf);
Cleanup: if (pPrqInfo3) FreeSplMem(pPrqInfo3);
return pInfo != NULL; }
BOOL LMEnumPorts( LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) { BOOL rc=TRUE; DWORD cb; PWINIPORT pIniPort; LPBYTE pEnd;
switch (Level) {
case 1: break;
case 2: break;
default: SetLastError(ERROR_INVALID_LEVEL); return FALSE; }
EnterSplSem();
cb=0;
pIniPort = pIniFirstPort;
while (pIniPort) { cb+=GetPortSize(pIniPort, Level); pIniPort=pIniPort->pNext; }
*pcbNeeded=cb;
if (cb <= cbBuf) {
pEnd=pPorts+cbBuf; *pcReturned=0;
pIniPort = pIniFirstPort; while (pIniPort) { pEnd = CopyIniPortToPort(pIniPort, Level, pPorts, pEnd); switch (Level) { case 1: pPorts+=sizeof(PORT_INFO_1); break;
case 2: pPorts+=sizeof(PORT_INFO_2); break; } pIniPort=pIniPort->pNext; (*pcReturned)++; }
} else {
*pcReturned = 0;
rc = FALSE;
SetLastError(ERROR_INSUFFICIENT_BUFFER); }
LeaveSplSem();
return rc; }
|