Annotation of ntddk/src/print/localmon/localmon.c, revision 1.1

1.1     ! root        1: #include <windows.h>
        !             2: #include <winspool.h>
        !             3: #include <winsplp.h>
        !             4: #include <stdio.h>
        !             5: #include <string.h>
        !             6: #include <stdlib.h>
        !             7: #include <wchar.h>
        !             8: #include "spltypes.h"
        !             9: #include "local.h"
        !            10: #include "dialogs.h"
        !            11: 
        !            12: 
        !            13: /* Definitions for MonitorThread:
        !            14:  */
        !            15: #define TRANSMISSION_DATA_SIZE  100
        !            16: 
        !            17: typedef struct _TRANSMISSION
        !            18: {
        !            19:     HANDLE       hPipe;
        !            20:     WCHAR        Data[TRANSMISSION_DATA_SIZE];
        !            21:     LPOVERLAPPED pOverlapped;
        !            22:     INT          PipeInstance;
        !            23:     HANDLE       hPrinter;
        !            24:     DWORD        JobId;
        !            25:     HANDLE       hFile;
        !            26:     PINIPORT     pIniPort;
        !            27: }
        !            28: TRANSMISSION, *PTRANSMISSION;
        !            29: 
        !            30: extern WCHAR szWindows[];
        !            31: extern WCHAR szINIKey_TransmissionRetryTimeout[];
        !            32: WCHAR   *Network = L"Net:";
        !            33: 
        !            34: BOOL
        !            35: CreateMonitorThread(
        !            36:    PINIPORT pIniPort
        !            37: );
        !            38: 
        !            39: VOID
        !            40: CompleteRead(
        !            41:     DWORD Error,
        !            42:     DWORD ByteCount,
        !            43:     LPOVERLAPPED pOverlapped
        !            44: );
        !            45: 
        !            46: PSECURITY_DESCRIPTOR
        !            47: CreateNamedPipeSecurityDescriptor(
        !            48:     VOID
        !            49: );
        !            50: 
        !            51: BOOL
        !            52: BuildPrintObjectProtection(
        !            53:     IN ULONG AceCount,
        !            54:     IN PSID *AceSid,
        !            55:     IN ACCESS_MASK *AceMask,
        !            56:     IN BYTE *InheritFlags,
        !            57:     IN PSID WorldSid,
        !            58:     IN PGENERIC_MAPPING GenericMap,
        !            59:     OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
        !            60: );
        !            61: 
        !            62: BOOL
        !            63: PortExists(
        !            64:     LPWSTR pName,
        !            65:     LPWSTR pPortName,
        !            66:     PDWORD pError
        !            67: );
        !            68: 
        !            69: #define offsetof(type, identifier) (DWORD)(&(((type)0)->identifier))
        !            70: 
        !            71: #if DBG
        !            72: DWORD GLOBAL_DEBUG_FLAGS = DBG_ERROR | DBG_WARNING;
        !            73: #endif
        !            74: 
        !            75: HANDLE hInst;
        !            76: 
        !            77: DWORD PortInfo1Offsets[]={offsetof(LPPORT_INFO_1, pName),
        !            78:                              (DWORD)-1};
        !            79: 
        !            80: WCHAR szPorts[]   = L"ports";
        !            81: WCHAR szPortsEx[] = L"portsex"; /* Extra ports values */
        !            82: WCHAR szFILE[]    = L"FILE:";
        !            83: WCHAR szCOM[]     = L"COM";
        !            84: WCHAR szLPT[]     = L"LPT";
        !            85: 
        !            86: /* These globals are needed so that AddPort can call
        !            87:  * SPOOLSS!EnumPorts to see whether the port to be added
        !            88:  * already exists.
        !            89:  * They will be initialized the first time AddPort is called.
        !            90:  */
        !            91: HMODULE hSpoolssDll = NULL;
        !            92: FARPROC pfnSpoolssEnumPorts = NULL;
        !            93: 
        !            94: #define IS_FILE_PORT(pName) \
        !            95:     !wcsicmp( pName, szFILE )
        !            96: 
        !            97: #define IS_COM_PORT(pName) \
        !            98:     !wcsnicmp( pName, szCOM, 3 )
        !            99: 
        !           100: #define IS_LPT_PORT(pName) \
        !           101:     !wcsnicmp( pName, szLPT, 3 )
        !           102: 
        !           103: #define IS_NOT_LOCAL_PORT(pName, pLocalMonitorName) \
        !           104:     wcsicmp( pName, pLocalMonitorName )
        !           105: 
        !           106: 
        !           107: LPWSTR
        !           108: GetPortName(
        !           109:     HWND hWnd
        !           110: );
        !           111: VOID
        !           112: ConfigCOMPort(
        !           113:     HWND hWnd
        !           114: );
        !           115: BOOL
        !           116: ConfigLPTPort(
        !           117:     HWND hWnd
        !           118: );
        !           119: 
        !           120: 
        !           121: PINIPORT    pIniFirstPort;
        !           122: CRITICAL_SECTION    SpoolerSection;
        !           123: 
        !           124: VOID
        !           125: RemoveColon(
        !           126:     LPWSTR  pName
        !           127: )
        !           128: {
        !           129:     DWORD   Length;
        !           130: 
        !           131:     // Remove that damm trailing colon
        !           132: 
        !           133:     Length = wcslen(pName);
        !           134: 
        !           135:     if (pName[Length-1] == L':')
        !           136:         pName[Length-1] = 0;
        !           137: 
        !           138: }
        !           139: 
        !           140: BOOL
        !           141: LibMain(
        !           142:     HANDLE hModule,
        !           143:     DWORD dwReason,
        !           144:     LPVOID lpRes
        !           145: )
        !           146: {
        !           147:     if (dwReason != DLL_PROCESS_ATTACH)
        !           148:         return TRUE;
        !           149: 
        !           150:     hInst = hModule;
        !           151: 
        !           152:     return TRUE;
        !           153: 
        !           154:     UNREFERENCED_PARAMETER( lpRes );
        !           155: }
        !           156: 
        !           157: 
        !           158: PINIPORT
        !           159: CreatePortEntry(
        !           160:     LPWSTR   pPortName
        !           161: )
        !           162: {
        !           163:     DWORD       cb;
        !           164:     PINIPORT    pIniPort, pPort;
        !           165: 
        !           166:     if (!lstrcmpi(pPortName, Network))
        !           167:         return FALSE;
        !           168: 
        !           169:     cb = sizeof(INIPORT) + wcslen(pPortName)*sizeof(WCHAR) + sizeof(WCHAR);
        !           170: 
        !           171:     EnterSplSem( );
        !           172: 
        !           173:     pIniPort=AllocSplMem(cb);
        !           174: 
        !           175:     LeaveSplSem( );
        !           176: 
        !           177:     if( pIniPort )
        !           178:     {
        !           179:         pIniPort->pName = wcscpy((LPWSTR)(pIniPort+1), pPortName);
        !           180:         pIniPort->cb = cb;
        !           181:         pIniPort->pNext = 0;
        !           182:         pIniPort->signature = IPO_SIGNATURE;
        !           183: 
        !           184:         if (pPort = pIniFirstPort) {
        !           185: 
        !           186:             while (pPort->pNext)
        !           187:                 pPort = pPort->pNext;
        !           188: 
        !           189:             pPort->pNext = pIniPort;
        !           190: 
        !           191:         } else
        !           192: 
        !           193:             pIniFirstPort = pIniPort;
        !           194:     }
        !           195: 
        !           196:     return pIniPort;
        !           197: }
        !           198: 
        !           199: 
        !           200: BOOL
        !           201: DeletePortEntry(
        !           202:     LPWSTR   pPortName
        !           203: )
        !           204: {
        !           205:     DWORD       cb;
        !           206:     PINIPORT    pPort, pPrevPort;
        !           207: 
        !           208:     cb = sizeof(INIPORT) + wcslen(pPortName)*sizeof(WCHAR) + sizeof(WCHAR);
        !           209: 
        !           210:     pPort = pIniFirstPort;
        !           211:     while (pPort && lstrcmpi(pPort->pName, pPortName)) {
        !           212:         pPrevPort = pPort;
        !           213:         pPort = pPort->pNext;
        !           214:     }
        !           215: 
        !           216:     if (pPort) {
        !           217:         if (pPort == pIniFirstPort) {
        !           218:             pIniFirstPort = pPort->pNext;
        !           219:         } else {
        !           220:             pPrevPort->pNext = pPort->pNext;
        !           221:         }
        !           222:         FreeSplMem (pPort, cb);
        !           223: 
        !           224:         return TRUE;
        !           225:     }
        !           226:     else
        !           227:         return FALSE;
        !           228: }
        !           229: 
        !           230: 
        !           231: BOOL
        !           232: InitializeMonitor(
        !           233:     LPWSTR  pRegistryRoot
        !           234: )
        !           235: {
        !           236:     LPWSTR   pPorts, pFirstPort;
        !           237:     DWORD   cbAlloc=4096;
        !           238: 
        !           239:     InitializeCriticalSection(&SpoolerSection);
        !           240: 
        !           241:    EnterSplSem();
        !           242: 
        !           243:     if (!(pPorts=AllocSplMem(cbAlloc))) {
        !           244: 
        !           245:         DBGMSG(DBG_ERROR, ("Failed to alloc %d bytes for ports.", cbAlloc));
        !           246:         return FALSE;
        !           247:     }
        !           248: 
        !           249:     GetProfileString(szPorts, NULL, L"", pPorts, cbAlloc);
        !           250: 
        !           251:     pFirstPort=pPorts;  // Remember the beginning so we can free it later
        !           252: 
        !           253:     // We now have all the ports
        !           254: 
        !           255:     while (*pPorts) {
        !           256: 
        !           257:         CreatePortEntry(pPorts);
        !           258: 
        !           259:         pPorts+=wcslen(pPorts)+1;
        !           260:     }
        !           261: 
        !           262:     FreeSplMem(pFirstPort, cbAlloc);
        !           263: 
        !           264:    LeaveSplSem();
        !           265: 
        !           266:     return TRUE;
        !           267: }
        !           268: 
        !           269: DWORD
        !           270: GetPortSize(
        !           271:     PINIPORT pIniPort,
        !           272:     DWORD   Level
        !           273: )
        !           274: {
        !           275:     DWORD   cb;
        !           276: 
        !           277:     switch (Level) {
        !           278: 
        !           279:     case 1:
        !           280: 
        !           281:         cb=sizeof(PORT_INFO_1) +
        !           282:            wcslen(pIniPort->pName)*sizeof(WCHAR) + sizeof(WCHAR);
        !           283:         break;
        !           284: 
        !           285:     default:
        !           286:         cb = 0;
        !           287:         break;
        !           288:     }
        !           289: 
        !           290:     return cb;
        !           291: }
        !           292: 
        !           293: // We are being a bit naughty here as we are not sure exactly how much
        !           294: // memory to allocate for the source strings. We will just assume that
        !           295: // PORT_INFO_2 is the biggest structure around for the moment.
        !           296: 
        !           297: LPBYTE
        !           298: CopyIniPortToPort(
        !           299:     PINIPORT pIniPort,
        !           300:     DWORD   Level,
        !           301:     LPBYTE  pPortInfo,
        !           302:     LPBYTE   pEnd
        !           303: )
        !           304: {
        !           305:     LPWSTR   SourceStrings[sizeof(PORT_INFO_1)/sizeof(LPWSTR)];
        !           306:     LPWSTR   *pSourceStrings=SourceStrings;
        !           307:     PPORT_INFO_1 pPort1 = (PPORT_INFO_1)pPortInfo;
        !           308:     DWORD   *pOffsets;
        !           309: 
        !           310:     switch (Level) {
        !           311: 
        !           312:     case 1:
        !           313:         pOffsets = PortInfo1Offsets;
        !           314:         break;
        !           315: 
        !           316:     default:
        !           317:         return pEnd;
        !           318:     }
        !           319: 
        !           320:     switch (Level) {
        !           321: 
        !           322:     case 1:
        !           323:         *pSourceStrings++=pIniPort->pName;
        !           324: 
        !           325:         pEnd = PackStrings(SourceStrings, pPortInfo, pOffsets, pEnd);
        !           326: 
        !           327:         break;
        !           328: 
        !           329:     default:
        !           330:         return pEnd;
        !           331:     }
        !           332: 
        !           333:     return pEnd;
        !           334: }
        !           335: 
        !           336: BOOL
        !           337: EnumPorts(
        !           338:     LPWSTR   pName,
        !           339:     DWORD   Level,
        !           340:     LPBYTE  pPorts,
        !           341:     DWORD   cbBuf,
        !           342:     LPDWORD pcbNeeded,
        !           343:     LPDWORD pcReturned
        !           344: )
        !           345: {
        !           346:     PINIPORT pIniPort;
        !           347:     DWORD   cb;
        !           348:     LPBYTE  pEnd;
        !           349:     DWORD   LastError=0;
        !           350: 
        !           351:    EnterSplSem();
        !           352: 
        !           353:     cb=0;
        !           354: 
        !           355:     pIniPort=pIniFirstPort;
        !           356: 
        !           357:     while (pIniPort) {
        !           358:         cb+=GetPortSize(pIniPort, Level);
        !           359:         pIniPort=pIniPort->pNext;
        !           360:     }
        !           361: 
        !           362:     *pcbNeeded=cb;
        !           363: 
        !           364:     if (cb <= cbBuf) {
        !           365: 
        !           366:         pEnd=pPorts+cbBuf;
        !           367:         *pcReturned=0;
        !           368: 
        !           369:         pIniPort=pIniFirstPort;
        !           370:         while (pIniPort) {
        !           371:             pEnd = CopyIniPortToPort(pIniPort, Level, pPorts, pEnd);
        !           372:             switch (Level) {
        !           373:             case 1:
        !           374:                 pPorts+=sizeof(PORT_INFO_1);
        !           375:                 break;
        !           376:             }
        !           377:             pIniPort=pIniPort->pNext;
        !           378:             (*pcReturned)++;
        !           379:         }
        !           380: 
        !           381:     } else
        !           382: 
        !           383:         LastError = ERROR_INSUFFICIENT_BUFFER;
        !           384: 
        !           385:    LeaveSplSem();
        !           386: 
        !           387:     if (LastError) {
        !           388: 
        !           389:         SetLastError(LastError);
        !           390:         return FALSE;
        !           391: 
        !           392:     } else
        !           393: 
        !           394:         return TRUE;
        !           395: }
        !           396: 
        !           397: #define NEXTVAL(pch)                    \
        !           398:     while( *pch && ( *pch != L',' ) )    \
        !           399:         pch++;                          \
        !           400:     if( *pch )                          \
        !           401:         pch++
        !           402: 
        !           403: 
        !           404: BOOL
        !           405: GetIniCommValues(
        !           406:     LPWSTR          pName,
        !           407:     LPDCB          pdcb,
        !           408:     LPCOMMTIMEOUTS pcto
        !           409: )
        !           410: {
        !           411:     WCHAR IniEntry[20];
        !           412: 
        !           413:     *IniEntry = L'\0';
        !           414: 
        !           415:     GetProfileString( szPorts, pName, L"", IniEntry, sizeof IniEntry );
        !           416: 
        !           417:     BuildCommDCB(IniEntry, pdcb);
        !           418: 
        !           419:     pcto->WriteTotalTimeoutConstant = GetProfileInt(szWindows,
        !           420:                                             szINIKey_TransmissionRetryTimeout,
        !           421:                                             45 );
        !           422:     pcto->WriteTotalTimeoutConstant*=1000;
        !           423:     return TRUE;
        !           424: }
        !           425: 
        !           426: 
        !           427: BOOL
        !           428: OpenPort(
        !           429:     LPWSTR   pName,
        !           430:     PHANDLE pHandle
        !           431: )
        !           432: {
        !           433:     PINIPORT     pIniPort;
        !           434: 
        !           435:    EnterSplSem();
        !           436:     pIniPort = FindPort(pName);
        !           437: 
        !           438:     if (pIniPort) {
        !           439: 
        !           440:         *pHandle = pIniPort;
        !           441: 
        !           442:         CreateMonitorThread(pIniPort);
        !           443:        LeaveSplSem();
        !           444: 
        !           445:         return TRUE;
        !           446: 
        !           447:     } else {
        !           448: 
        !           449:        DBGMSG(DBG_ERROR, ("localmon!OpenPort %s : Failed\n", pName));
        !           450: 
        !           451:        LeaveSplSem();
        !           452:         return FALSE;
        !           453:     }
        !           454: }
        !           455: 
        !           456: 
        !           457: BOOL
        !           458: StartDocPort(
        !           459:     HANDLE  hPort,
        !           460:     LPWSTR  pPrinterName,
        !           461:     DWORD   JobId,
        !           462:     DWORD   Level,
        !           463:     LPBYTE  pDocInfo
        !           464: )
        !           465: {
        !           466:     PINIPORT    pIniPort = (PINIPORT)hPort;
        !           467:     LPWSTR       pPortName;
        !           468:     DCB          dcb;
        !           469:     COMMTIMEOUTS cto;
        !           470:     WCHAR       TempDosDeviceName[MAX_PATH];
        !           471:     HANDLE      hToken;
        !           472: 
        !           473:     UNREFERENCED_PARAMETER( Level );
        !           474:     UNREFERENCED_PARAMETER( pDocInfo );
        !           475: 
        !           476:     DBGMSG(DBG_TRACE, ("StartDocPort(%08x, %ws, %d, %d, %08x)\n",
        !           477:                        hPort, pPrinterName, JobId, Level, pDocInfo));
        !           478: 
        !           479:    EnterSplSem();
        !           480:     pIniPort->pPrinterName = AllocSplStr(pPrinterName);
        !           481:    LeaveSplSem();
        !           482: 
        !           483:     if (pIniPort->pPrinterName) {
        !           484: 
        !           485:         if (OpenPrinter(pPrinterName, &pIniPort->hPrinter, NULL)) {
        !           486: 
        !           487:             pIniPort->JobId = JobId;
        !           488: 
        !           489:             pPortName = pIniPort->pName;
        !           490: 
        !           491:             if (!IS_FILE_PORT (pPortName)) {
        !           492: 
        !           493:                 LPWSTR  pName;
        !           494: 
        !           495:                 if (pIniPort->Status & PP_MONITORRUNNING) {
        !           496: 
        !           497:                     WCHAR   DeviceNames[MAX_PATH];
        !           498:                     WCHAR   DosDeviceName[MAX_PATH];
        !           499:                     WCHAR  *pDeviceNames=DeviceNames;
        !           500: 
        !           501:                     wcscpy(DosDeviceName, pIniPort->pName);
        !           502:                     RemoveColon(DosDeviceName);
        !           503: 
        !           504:                     hToken = RevertToPrinterSelf();
        !           505: 
        !           506:                     QueryDosDevice(DosDeviceName,
        !           507:                                    DeviceNames,
        !           508:                                    sizeof(DeviceNames));
        !           509: 
        !           510:                     if (!lstrcmpi(pDeviceNames, pIniPort->pNewDeviceName)) {
        !           511: 
        !           512:                         pDeviceNames+=wcslen(pDeviceNames)+1;
        !           513:                     }
        !           514: 
        !           515:                     wcscpy(TempDosDeviceName, L"NONSPOOLED_");
        !           516:                     wcscat(TempDosDeviceName, pIniPort->pName);
        !           517:                     RemoveColon(TempDosDeviceName);
        !           518: 
        !           519:                     DefineDosDevice(DDD_RAW_TARGET_PATH, TempDosDeviceName, pDeviceNames);
        !           520: 
        !           521:                     ImpersonatePrinterClient(hToken);
        !           522: 
        !           523:                     wcscpy(TempDosDeviceName, L"\\\\.\\NONSPOOLED_");
        !           524:                     wcscat(TempDosDeviceName, pIniPort->pName);
        !           525:                     RemoveColon(TempDosDeviceName);
        !           526: 
        !           527:                     pName = TempDosDeviceName;
        !           528: 
        !           529:                 } else
        !           530:                     pName = pIniPort->pName;
        !           531: 
        !           532:                 pIniPort->hFile = CreateFile(pName, GENERIC_WRITE,
        !           533:                                             FILE_SHARE_READ, NULL, OPEN_ALWAYS,
        !           534:                                             FILE_ATTRIBUTE_NORMAL |
        !           535:                                             FILE_FLAG_SEQUENTIAL_SCAN, NULL);
        !           536: 
        !           537:                 if (pIniPort->hFile == INVALID_HANDLE_VALUE) {
        !           538: 
        !           539:                     if (pIniPort->Status & PP_MONITORRUNNING) {
        !           540: 
        !           541:                         wcscpy(TempDosDeviceName, L"NONSPOOLED_");
        !           542:                         wcscat(TempDosDeviceName, pIniPort->pName);
        !           543:                         RemoveColon(TempDosDeviceName);
        !           544: 
        !           545:                         DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
        !           546:                     }
        !           547:                    EnterSplSem();
        !           548:                     FreeSplStr(pIniPort->pPrinterName);
        !           549:                    LeaveSplSem();
        !           550:                     return FALSE;
        !           551:                 }
        !           552: 
        !           553:                 SetEndOfFile(pIniPort->hFile);
        !           554: 
        !           555:                 if (IS_COM_PORT (pPortName)) {
        !           556: 
        !           557:                     if (GetCommState (pIniPort->hFile, &dcb)) {
        !           558: 
        !           559:                         GetCommTimeouts(pIniPort->hFile, &cto);
        !           560:                         GetIniCommValues (pPortName, &dcb, &cto);
        !           561:                         SetCommState (pIniPort->hFile, &dcb);
        !           562:                         SetCommTimeouts(pIniPort->hFile, &cto);
        !           563: 
        !           564:                     } else {
        !           565: 
        !           566:                         DBGMSG( DBG_ERROR, ("ERROR: Failed to set Comm State") );
        !           567:                     }
        !           568:                 }
        !           569: 
        !           570:                 else if (IS_LPT_PORT (pPortName)) {
        !           571: 
        !           572:                     if (GetCommTimeouts(pIniPort->hFile, &cto)) {
        !           573:                         cto.WriteTotalTimeoutConstant = GetProfileInt(szWindows,
        !           574:                                                 szINIKey_TransmissionRetryTimeout,
        !           575:                                                 45 );
        !           576:                         cto.WriteTotalTimeoutConstant*=1000;
        !           577:                         SetCommTimeouts(pIniPort->hFile, &cto);
        !           578: 
        !           579:                     } else {
        !           580: 
        !           581:                         DBGMSG( DBG_ERROR, ("ERROR: Failed to set Comm State") );
        !           582:                     }
        !           583:                 }
        !           584: 
        !           585:             } else {
        !           586: 
        !           587:                 HANDLE  hToken;
        !           588:                 int    rc;
        !           589:                 HANDLE hFile;
        !           590: 
        !           591:                 hToken = RevertToPrinterSelf();
        !           592: 
        !           593: 
        !           594:                 rc = DialogBoxParam( hInst,
        !           595:                                     MAKEINTRESOURCE( DLG_PRINTTOFILE ),
        !           596:                                     NULL, (DLGPROC)PrintToFileDlg,
        !           597:                                     (LPARAM)&hFile );
        !           598: 
        !           599:                 ImpersonatePrinterClient(hToken);
        !           600: 
        !           601:                 if( rc == -1 ) {
        !           602: 
        !           603:                    EnterSplSem();
        !           604:                     FreeSplStr(pIniPort->pPrinterName);
        !           605:                    LeaveSplSem();
        !           606:                     return FALSE;
        !           607: 
        !           608:                 } else if( rc == 0 ) {
        !           609: 
        !           610:                    EnterSplSem();
        !           611:                     FreeSplStr(pIniPort->pPrinterName);
        !           612:                    LeaveSplSem();
        !           613:                     SetLastError(ERROR_PRINT_CANCELLED);
        !           614:                     return FALSE;
        !           615: 
        !           616:                 } else {
        !           617: 
        !           618:                     pIniPort->hFile = hFile;
        !           619:                 }
        !           620: 
        !           621:                 SetEndOfFile(pIniPort->hFile);
        !           622:             }
        !           623: 
        !           624:         } else {
        !           625:               EnterSplSem( );
        !           626:               FreeSplStr(pIniPort->pPrinterName);
        !           627:               LeaveSplSem( );
        !           628:         }
        !           629:     }
        !           630: 
        !           631:     if (pIniPort->hFile == INVALID_HANDLE_VALUE) {
        !           632:        DBGMSG(DBG_ERROR, ("StartDocPort FAILED %x\n", GetLastError()));
        !           633:         if (pIniPort->pPrinterName) {
        !           634:            EnterSplSem();
        !           635:             FreeSplStr(pIniPort->pPrinterName);
        !           636:            LeaveSplSem();
        !           637:         }
        !           638:         return FALSE;
        !           639:     } else {
        !           640:         return TRUE;
        !           641:     }
        !           642: }
        !           643: 
        !           644: BOOL
        !           645: ReadPort(
        !           646:     HANDLE hPort,
        !           647:     LPBYTE pBuffer,
        !           648:     DWORD  cbBuf,
        !           649:     LPDWORD pcbRead
        !           650: )
        !           651: {
        !           652:     PINIPORT    pIniPort = (PINIPORT)hPort;
        !           653:     BOOL    rc;
        !           654: 
        !           655:     DBGMSG(DBG_TRACE, ("ReadPort(%08x, %08x, %d)\n", hPort, pBuffer, cbBuf));
        !           656: 
        !           657:     rc = ReadFile(pIniPort->hFile, pBuffer, cbBuf, pcbRead, NULL);
        !           658: 
        !           659:     DBGMSG(DBG_TRACE, ("ReadPort returns %d; %d bytes read\n", rc, *pcbRead));
        !           660: 
        !           661:     return rc;
        !           662: }
        !           663: 
        !           664: BOOL
        !           665: WritePort(
        !           666:     HANDLE  hPort,
        !           667:     LPBYTE  pBuffer,
        !           668:     DWORD   cbBuf,
        !           669:     LPDWORD pcbWritten
        !           670: )
        !           671: {
        !           672:     PINIPORT    pIniPort = (PINIPORT)hPort;
        !           673:     BOOL    rc;
        !           674: 
        !           675:     DBGMSG(DBG_TRACE, ("WritePort(%08x, %08x, %d)\n", hPort, pBuffer, cbBuf));
        !           676: 
        !           677:     rc = WriteFile(pIniPort->hFile, pBuffer, cbBuf, pcbWritten, NULL);
        !           678: 
        !           679:     DBGMSG(DBG_TRACE, ("WritePort returns %d; %d bytes written\n", rc, *pcbWritten));
        !           680: 
        !           681:     return rc;
        !           682: }
        !           683: 
        !           684: BOOL
        !           685: EndDocPort(
        !           686:    HANDLE   hPort
        !           687: )
        !           688: {
        !           689:     PINIPORT    pIniPort = (PINIPORT)hPort;
        !           690:     WCHAR       TempDosDeviceName[MAX_PATH];
        !           691: 
        !           692:     DBGMSG(DBG_TRACE, ("EndDocPort(%08x)\n", hPort));
        !           693: 
        !           694:     CloseHandle(pIniPort->hFile);
        !           695: 
        !           696:     SetJob(pIniPort->hPrinter, pIniPort->JobId, 0, NULL, JOB_CONTROL_CANCEL);
        !           697: 
        !           698:     ClosePrinter(pIniPort->hPrinter);
        !           699: 
        !           700:    EnterSplSem();
        !           701: 
        !           702:     if (pIniPort->Status & PP_MONITORRUNNING) {
        !           703: 
        !           704:         wcscpy(TempDosDeviceName, L"NONSPOOLED_");
        !           705:         wcscat(TempDosDeviceName, pIniPort->pName);
        !           706:         RemoveColon(TempDosDeviceName);
        !           707: 
        !           708:         DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
        !           709:     }
        !           710: 
        !           711:     FreeSplStr(pIniPort->pPrinterName);
        !           712: 
        !           713:    LeaveSplSem();
        !           714: 
        !           715:     return TRUE;
        !           716: }
        !           717: 
        !           718: BOOL
        !           719: ClosePort(
        !           720:     HANDLE  hPort
        !           721: )
        !           722: {
        !           723:     PINIPORT pIniPort = (PINIPORT)hPort;
        !           724: 
        !           725:     // Now destroy the monitor
        !           726: 
        !           727:     if (pIniPort->Status & PP_MONITORRUNNING) {
        !           728:         pIniPort->Status &= ~PP_MONITORRUNNING;
        !           729:         pIniPort->Status &= ~PP_RUNMONITOR;
        !           730:         SetEvent(pIniPort->Semaphore);
        !           731:     }
        !           732: 
        !           733:     return TRUE;
        !           734: }
        !           735: 
        !           736: BOOL
        !           737: DeletePort(
        !           738:     LPWSTR   pName,
        !           739:     HWND    hWnd,
        !           740:     LPWSTR   pPortName
        !           741: )
        !           742: {
        !           743:     BOOL rc;
        !           744: 
        !           745:     if( !hWnd )
        !           746:         hWnd == GetDesktopWindow( );
        !           747: 
        !           748:     EnterSplSem();
        !           749: 
        !           750:     if (rc = DeletePortEntry( pPortName ))
        !           751:         WriteProfileString(szPorts, pPortName, NULL);
        !           752: 
        !           753:     LeaveSplSem();
        !           754: 
        !           755:     return rc;
        !           756: 
        !           757:     UNREFERENCED_PARAMETER( pName );
        !           758: }
        !           759: 
        !           760: BOOL
        !           761: AddPort(
        !           762:     LPWSTR   pName,
        !           763:     HWND    hWnd,
        !           764:     LPWSTR   pMonitorName
        !           765: )
        !           766: {
        !           767:     LPWSTR pPortName;
        !           768:     DWORD  ThreadId;
        !           769:     DWORD  WindowThreadId;
        !           770:     BOOL   rc = TRUE;
        !           771:     DWORD  Error;
        !           772:     BOOL   DoAddPort = TRUE;
        !           773:     WCHAR  szLocalMonitor[MAX_PATH+1];
        !           774: 
        !           775: 
        !           776:     LoadString(hInst, IDS_LOCALMONITOR, szLocalMonitor, MAX_PATH);
        !           777:     if  (IS_NOT_LOCAL_PORT( pMonitorName, szLocalMonitor)) {
        !           778: 
        !           779:         // If pMonitorName != "Local Port", we have an
        !           780:         // invalid Monitor name
        !           781:         SetLastError(ERROR_INVALID_PARAMETER);
        !           782:         return(FALSE);
        !           783:     }
        !           784: 
        !           785:     ThreadId = GetCurrentThreadId( );
        !           786:     WindowThreadId = GetWindowThreadProcessId(hWnd, NULL);
        !           787: 
        !           788:     if (!AttachThreadInput(ThreadId, WindowThreadId, TRUE))
        !           789:         DBGMSG(DBG_WARNING, ("AttachThreadInput failed: Error %d\n", GetLastError()));
        !           790: 
        !           791:     /* Get the user to enter a port name:
        !           792:      */
        !           793:     pPortName = GetPortName( hWnd );
        !           794: 
        !           795:     if( pPortName )
        !           796:     {
        !           797:         if( PortExists( pName, pPortName, &Error ) )
        !           798:         {
        !           799:             Message( hWnd, MSG_ERROR, IDS_LOCALMONITOR,
        !           800:                      IDS_PORTALREADYEXISTS_S, pPortName );
        !           801: 
        !           802:             DoAddPort = FALSE;
        !           803: 
        !           804:             /* In this case, the error was handled, and the user was
        !           805:              * notified with a message box, so we return true to ensure
        !           806:              * that the caller doesn't have to do so too.
        !           807:              */
        !           808:             rc = TRUE;
        !           809:         }
        !           810: 
        !           811:         else if( Error != NO_ERROR )
        !           812:         {
        !           813:             DBGMSG(DBG_ERROR, ("Error %d occurred checking whether port exists\n",
        !           814:                                Error));
        !           815:             DoAddPort = FALSE;
        !           816:             rc = FALSE;
        !           817:         }
        !           818: 
        !           819:         if( DoAddPort )
        !           820:         {
        !           821:             /* Now, let's see if it's a COM port:
        !           822:              */
        !           823:             if( IS_COM_PORT( pPortName ) )
        !           824: 
        !           825:                 ConfigCOMPort( hWnd );
        !           826: 
        !           827:             else
        !           828:             /* No, well maybe it's an LPT?
        !           829:              */
        !           830:             if( IS_LPT_PORT( pPortName ) )
        !           831: 
        !           832:                 ConfigLPTPort( hWnd );
        !           833: 
        !           834: 
        !           835:             if( CreatePortEntry( pPortName ) ) {
        !           836:                 if (!WriteProfileString(szPorts, pPortName, L"")) {
        !           837:                     DeletePortEntry(pPortName);
        !           838:                     rc = FALSE;
        !           839:                 }
        !           840:             }
        !           841:             else
        !           842:                 rc = FALSE;
        !           843:         }
        !           844: 
        !           845:     }
        !           846: 
        !           847:     AttachThreadInput(WindowThreadId, ThreadId, FALSE);
        !           848: 
        !           849:     return rc;
        !           850: }
        !           851: 
        !           852: 
        !           853: /* PortExists
        !           854:  *
        !           855:  * Calls EnumPorts to check whether the port name already exists.
        !           856:  * This asks every monitor, rather than just this one.
        !           857:  * The function will return TRUE if the specified port is in the list.
        !           858:  * If an error occurs, the return is FALSE and the variable pointed
        !           859:  * to by pError contains the return from GetLastError().
        !           860:  * The caller must therefore always check that *pError == NO_ERROR.
        !           861:  */
        !           862: BOOL
        !           863: PortExists(
        !           864:     LPWSTR pName,
        !           865:     LPWSTR pPortName,
        !           866:     PDWORD pError
        !           867: )
        !           868: {
        !           869:     DWORD cbNeeded;
        !           870:     DWORD cReturned;
        !           871:     DWORD cbPorts;
        !           872:     LPPORT_INFO_1 pPorts;
        !           873:     DWORD i;
        !           874:     BOOL  Found = TRUE;
        !           875: 
        !           876:     *pError = NO_ERROR;
        !           877: 
        !           878:     if (!hSpoolssDll) {
        !           879: 
        !           880:         hSpoolssDll = LoadLibrary(L"SPOOLSS.DLL");
        !           881: 
        !           882:         if (hSpoolssDll) {
        !           883:             pfnSpoolssEnumPorts = GetProcAddress(hSpoolssDll,
        !           884:                                                  "EnumPortsW");
        !           885:             if (!pfnSpoolssEnumPorts) {
        !           886: 
        !           887:                 *pError = GetLastError();
        !           888:                 FreeLibrary(hSpoolssDll);
        !           889:                 hSpoolssDll = NULL;
        !           890:             }
        !           891: 
        !           892:         } else {
        !           893: 
        !           894:             *pError = GetLastError();
        !           895:         }
        !           896:     }
        !           897: 
        !           898:     if (!pfnSpoolssEnumPorts)
        !           899:         return FALSE;
        !           900: 
        !           901: 
        !           902:     if (!(*pfnSpoolssEnumPorts)(pName, 1, NULL, 0, &cbNeeded, &cReturned))
        !           903:     {
        !           904:         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        !           905:         {
        !           906:             cbPorts = cbNeeded;
        !           907: 
        !           908:             EnterSplSem();
        !           909:             pPorts = AllocSplMem(cbPorts);
        !           910:             LeaveSplSem();
        !           911: 
        !           912:             if (pPorts)
        !           913:             {
        !           914:                 if ((*pfnSpoolssEnumPorts)(pName, 1, (LPBYTE)pPorts, cbPorts,
        !           915:                                            &cbNeeded, &cReturned))
        !           916:                 {
        !           917:                     Found = FALSE;
        !           918: 
        !           919:                     for (i = 0; i < cReturned; i++)
        !           920:                     {
        !           921:                         if (!lstrcmpi(pPorts[i].pName, pPortName))
        !           922:                             Found = TRUE;
        !           923:                     }
        !           924:                 }
        !           925:             }
        !           926: 
        !           927:             EnterSplSem();
        !           928:             FreeSplMem(pPorts, cbPorts);
        !           929:             LeaveSplSem();
        !           930:         }
        !           931:     }
        !           932: 
        !           933:     else
        !           934:         Found = FALSE;
        !           935: 
        !           936: 
        !           937:     return Found;
        !           938: }
        !           939: 
        !           940: 
        !           941: 
        !           942: /* ConfigurePort
        !           943:  *
        !           944:  */
        !           945: BOOL
        !           946: ConfigurePort(
        !           947:     LPWSTR   pName,
        !           948:     HWND  hWnd,
        !           949:     LPWSTR pPortName
        !           950: )
        !           951: {
        !           952:     DWORD  ThreadId;
        !           953:     DWORD  WindowThreadId;
        !           954: 
        !           955:     ThreadId = GetCurrentThreadId( );
        !           956:     WindowThreadId = GetWindowThreadProcessId(hWnd, NULL);
        !           957: 
        !           958:     if (!AttachThreadInput(ThreadId, WindowThreadId, TRUE))
        !           959:         DBGMSG(DBG_WARNING, ("AttachThreadInput failed: Error %d\n", GetLastError()));
        !           960: 
        !           961:     if( IS_COM_PORT( pPortName ) )
        !           962: 
        !           963:         ConfigCOMPort( hWnd );
        !           964: 
        !           965:     else
        !           966:     /* No, well maybe it's an LPT?
        !           967:      */
        !           968:     if( IS_LPT_PORT( pPortName ) )
        !           969: 
        !           970:         ConfigLPTPort( hWnd );
        !           971: 
        !           972:     else
        !           973:         Message( hWnd, MSG_INFORMATION, IDS_LOCALMONITOR,
        !           974:                  IDS_NOTHING_TO_CONFIGURE );
        !           975: 
        !           976:     AttachThreadInput(WindowThreadId, ThreadId, FALSE);
        !           977: 
        !           978:     return TRUE;
        !           979: }
        !           980: 
        !           981: 
        !           982: /* GetPortName
        !           983:  *
        !           984:  * Puts up a dialog containing a free entry field.
        !           985:  * The dialog allocates a string for the name, if a selection is made.
        !           986:  */
        !           987: LPWSTR
        !           988: GetPortName(
        !           989:     HWND hWnd
        !           990: )
        !           991: {
        !           992:     LPWSTR pPortName;
        !           993: 
        !           994:     if( DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_PORTNAME), hWnd,
        !           995:                         (DLGPROC)PortNameDlg, (LPARAM)&pPortName ) != IDOK )
        !           996:         pPortName = NULL;
        !           997: 
        !           998:     return pPortName;
        !           999: }
        !          1000: 
        !          1001: /* From Control Panel's control.h:
        !          1002:  */
        !          1003: #define CHILD_PORTS 4
        !          1004: 
        !          1005: /* From cpl.h:
        !          1006:  */
        !          1007: #define CPL_INIT        1
        !          1008: #define CPL_DBLCLK      5
        !          1009: #define CPL_EXIT        7
        !          1010: 
        !          1011: /* Hack:
        !          1012:  */
        !          1013: #define CHILD_PORTS_HELPID  2
        !          1014: 
        !          1015: /* ConfigCOMPort
        !          1016:  *
        !          1017:  * Calls the Control Panel Ports applet
        !          1018:  * to permit user to set Baud rate etc.
        !          1019:  */
        !          1020: typedef void (WINAPI *CFGPROC)(HWND, ULONG, ULONG, ULONG);
        !          1021: 
        !          1022: 
        !          1023: VOID
        !          1024: ConfigCOMPort(
        !          1025:     HWND hWnd
        !          1026: )
        !          1027: {
        !          1028:     HANDLE  hLibrary;
        !          1029:     CFGPROC pfnCplApplet;
        !          1030: 
        !          1031:     if( hLibrary = LoadLibrary( L"MAIN.CPL" ) ) {
        !          1032: 
        !          1033:         if( pfnCplApplet = (CFGPROC)GetProcAddress( hLibrary, "CPlApplet" ) ) {
        !          1034:             (*pfnCplApplet)( hWnd, CPL_INIT, 0, 0 );
        !          1035:             (*pfnCplApplet)( hWnd, CPL_DBLCLK, CHILD_PORTS_HELPID, CHILD_PORTS );
        !          1036:             (*pfnCplApplet)( hWnd, CPL_EXIT, 0, 0 );
        !          1037:         }
        !          1038: 
        !          1039:         FreeLibrary( hLibrary );
        !          1040:     }
        !          1041: }
        !          1042: 
        !          1043: 
        !          1044: /* ConfigLPTPort
        !          1045:  *
        !          1046:  * Calls a dialog box which prompts the user to enter timeout and retry
        !          1047:  * values for the port concerned.
        !          1048:  * The dialog writes the information to the registry (win.ini for now).
        !          1049:  */
        !          1050: BOOL
        !          1051: ConfigLPTPort(
        !          1052:     HWND hWnd
        !          1053: )
        !          1054: {
        !          1055:     DialogBox( hInst, MAKEINTRESOURCE( DLG_CONFIGURE_LPT ),
        !          1056:                hWnd, (DLGPROC)ConfigureLPTPortDlg );
        !          1057: 
        !          1058:     return TRUE;
        !          1059: }
        !          1060: 
        !          1061: VOID
        !          1062: MonitorThread(
        !          1063:     PINIPORT  pIniPort
        !          1064: );
        !          1065: 
        !          1066: BOOL
        !          1067: CreateMonitorThread(
        !          1068:    PINIPORT pIniPort
        !          1069: )
        !          1070: {
        !          1071:     if (!(pIniPort->Status & PP_THREADRUNNING)) {
        !          1072: 
        !          1073:         pIniPort->Status |= PP_RUNTHREAD;
        !          1074: 
        !          1075:         pIniPort->Semaphore=CreateEvent(NULL, FALSE, FALSE, NULL);
        !          1076: 
        !          1077:         pIniPort->AccessSem=CreateSemaphore(NULL, 1, 256,NULL);
        !          1078: 
        !          1079:         pIniPort->ThreadHandle = CreateThread(NULL, 16*1024,
        !          1080:                                  (LPTHREAD_START_ROUTINE)MonitorThread,
        !          1081:                                  pIniPort,
        !          1082:                                  0, &pIniPort->ThreadId);
        !          1083: 
        !          1084:         pIniPort->Status |= PP_THREADRUNNING;
        !          1085: 
        !          1086:     } else {
        !          1087: 
        !          1088:         DBGMSG(DBG_ERROR, ("localmon!MonitorThread already running %x\n",
        !          1089:                            pIniPort->Status));
        !          1090:     }
        !          1091: 
        !          1092:     return TRUE;
        !          1093: }
        !          1094: 
        !          1095: #define NUMBER_OF_PIPE_INSTANCES 10
        !          1096: 
        !          1097: VOID
        !          1098: MonitorThread(
        !          1099:     PINIPORT  pIniPort
        !          1100: )
        !          1101: {
        !          1102:     WCHAR   NewNtDeviceName[MAX_PATH];
        !          1103:     WCHAR   OldNtDeviceName[MAX_PATH];
        !          1104:     WCHAR   DosDeviceName[MAX_PATH];
        !          1105:     WCHAR   szPipeName[MAX_PATH];
        !          1106:     HANDLE  hPipe[NUMBER_OF_PIPE_INSTANCES];
        !          1107:     HANDLE  hEvent[NUMBER_OF_PIPE_INSTANCES+1];  // One extra event for semaphore
        !          1108:     DWORD   WaitResult;
        !          1109:     DWORD   i;
        !          1110:     DWORD   Error;
        !          1111:     OVERLAPPED  OverLapped;
        !          1112:     OVERLAPPED  Overlapped[NUMBER_OF_PIPE_INSTANCES];
        !          1113:     LPOVERLAPPED    pOverlapped;
        !          1114:     PTRANSMISSION   pTransmission;
        !          1115:     SECURITY_ATTRIBUTES SecurityAttributes;
        !          1116: 
        !          1117:    EnterSplSem();
        !          1118: 
        !          1119:     wcscpy(DosDeviceName, pIniPort->pName);
        !          1120:     RemoveColon(DosDeviceName);
        !          1121: 
        !          1122:     if (!QueryDosDevice(DosDeviceName, OldNtDeviceName,
        !          1123:                        sizeof(OldNtDeviceName)/sizeof(OldNtDeviceName[0]))) {
        !          1124:         LeaveSplSem();
        !          1125:         return;
        !          1126:     }
        !          1127: 
        !          1128:     SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
        !          1129: 
        !          1130:     SecurityAttributes.lpSecurityDescriptor = CreateNamedPipeSecurityDescriptor();
        !          1131: 
        !          1132:     SecurityAttributes.bInheritHandle = FALSE;
        !          1133: 
        !          1134:     wcscpy(NewNtDeviceName, L"\\Device\\NamedPipe\\Spooler\\");
        !          1135:     wcscat(NewNtDeviceName, pIniPort->pName);
        !          1136:     RemoveColon(NewNtDeviceName);
        !          1137: 
        !          1138:     DefineDosDevice(DDD_RAW_TARGET_PATH, DosDeviceName, NewNtDeviceName);
        !          1139: 
        !          1140:     pIniPort->pNewDeviceName = AllocSplStr(NewNtDeviceName);
        !          1141: 
        !          1142:    LeaveSplSem();
        !          1143: 
        !          1144:     wsprintf(szPipeName, L"\\\\.\\Pipe\\Spooler\\%ws", pIniPort->pName);
        !          1145:     RemoveColon(szPipeName);
        !          1146: 
        !          1147:     pIniPort->Status |= PP_MONITORRUNNING;
        !          1148: 
        !          1149:     memset(&OverLapped, 0, sizeof(OVERLAPPED));
        !          1150: 
        !          1151:     /* Put the semaphore event in the extra member of the event array:
        !          1152:      */
        !          1153:     hEvent[NUMBER_OF_PIPE_INSTANCES] = pIniPort->Semaphore;
        !          1154: 
        !          1155:     /* Create several instances of a named pipe, create an event for each,
        !          1156:      * and connect to wait for a client:
        !          1157:      */
        !          1158:     for( i = 0; i < NUMBER_OF_PIPE_INSTANCES; i++ )
        !          1159:     {
        !          1160:         hPipe[i] = CreateNamedPipe(szPipeName, PIPE_ACCESS_DUPLEX |
        !          1161:                                                            FILE_FLAG_OVERLAPPED,
        !          1162:                                                PIPE_WAIT | PIPE_READMODE_BYTE |
        !          1163:                                                            PIPE_TYPE_BYTE,
        !          1164:                                                PIPE_UNLIMITED_INSTANCES,
        !          1165:                                                4096,
        !          1166:                                                64*1024,   // 64k
        !          1167:                                                0,
        !          1168:                                                &SecurityAttributes);
        !          1169:         if( hPipe[i] == (HANDLE)-1 )
        !          1170:         {
        !          1171:             DBGMSG( DBG_ERROR, ( "CreateNamedPipe of %s failed. Error %d\n",
        !          1172:                                  szPipeName, GetLastError( ) ) );
        !          1173:             pIniPort->Status &= ~PP_THREADRUNNING;
        !          1174:             return;
        !          1175:         }
        !          1176: 
        !          1177:         hEvent[i] = Overlapped[i].hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
        !          1178: 
        !          1179:         if( !hEvent[i] )
        !          1180:         {
        !          1181:             DBGMSG( DBG_ERROR, ( "CreateEvent failed. Error %d\n", GetLastError( ) ) );
        !          1182:             pIniPort->Status &= ~PP_THREADRUNNING;
        !          1183:             return;
        !          1184:         }
        !          1185: 
        !          1186:         if( !ConnectNamedPipe( hPipe[i], &Overlapped[i] ) )
        !          1187:         {
        !          1188:             Error = GetLastError( );
        !          1189: 
        !          1190:             if( Error == ERROR_IO_PENDING )
        !          1191:             {
        !          1192:                 DBGMSG( DBG_INFO, ( "ConnectNamedPipe %d, IO pending\n", i ) );
        !          1193:             }
        !          1194:             else
        !          1195:             {
        !          1196:                 DBGMSG( DBG_ERROR, ( "ConnectNamedPipe failed. Error %d\n", GetLastError( ) ) );
        !          1197:                 pIniPort->Status &= ~PP_THREADRUNNING;
        !          1198:                 return;
        !          1199:             }
        !          1200:         }
        !          1201:     }
        !          1202: 
        !          1203: 
        !          1204:     while( TRUE )
        !          1205:     {
        !          1206:         DBGMSG( DBG_INFO, ( "Waiting to connect...\n" ) );
        !          1207: 
        !          1208:         /* WaitForMultipleObjectsEx should return the index of the pipe instance
        !          1209:          * that is in signalled state.  Any other value except WAIT_IO_COMPLETION
        !          1210:          * is an error:
        !          1211:          */
        !          1212:         while ((WaitResult = WaitForMultipleObjectsEx( NUMBER_OF_PIPE_INSTANCES + 1,
        !          1213:                                                hEvent,
        !          1214:                                                FALSE, // wait for any
        !          1215:                                                INFINITE,
        !          1216:                                                TRUE )) == WAIT_IO_COMPLETION)
        !          1217:             /* do nothing */ ;
        !          1218: 
        !          1219:         DBGMSG( DBG_INFO, ( "\nWaitForMultipleObjectsEx returned %d\n", WaitResult ) );
        !          1220: 
        !          1221:         if( ( WaitResult >= NUMBER_OF_PIPE_INSTANCES )
        !          1222:           &&( WaitResult != WAIT_IO_COMPLETION ) )
        !          1223:         {
        !          1224:             DBGMSG( DBG_INFO, ( "WaitForMultipleObjects returned %d; Last error = %d\n",
        !          1225:                     WaitResult, GetLastError( ) ) );
        !          1226:             EnterSplSem();
        !          1227: 
        !          1228: //           if (pIniPort->pPreviousNtDeviceName) {
        !          1229: 
        !          1230:                  wcscpy(DosDeviceName, pIniPort->pName);
        !          1231:                  RemoveColon(DosDeviceName);
        !          1232: 
        !          1233:                  wcscpy(NewNtDeviceName, L"\\Device\\NamedPipe\\Spooler\\");
        !          1234:                  wcscat(NewNtDeviceName, pIniPort->pName);
        !          1235:                  RemoveColon(NewNtDeviceName);
        !          1236: 
        !          1237:                  DefineDosDevice(DDD_REMOVE_DEFINITION |
        !          1238:                                          DDD_EXACT_MATCH_ON_REMOVE |
        !          1239:                                          DDD_RAW_TARGET_PATH,
        !          1240:                                  DosDeviceName,
        !          1241:                                  NewNtDeviceName);
        !          1242: //           }
        !          1243: 
        !          1244:              for( i = 0; i < NUMBER_OF_PIPE_INSTANCES; i++ ) {
        !          1245: 
        !          1246:                 if( !CloseHandle( hPipe[i] ) ) {
        !          1247:                     DBGMSG( DBG_ERROR, ( "localspl: CloseHandle failed %d %d\n", hPipe[i], GetLastError( ) ) );
        !          1248:                 }
        !          1249: 
        !          1250:                 if( !CloseHandle( hEvent[i] ) ) {
        !          1251:                     DBGMSG( DBG_ERROR, ( "localspl: CloseHandle failed %d %d\n", hPipe[i], GetLastError( ) ) );
        !          1252:                 }
        !          1253:              }
        !          1254: 
        !          1255:              pIniPort->Status &= ~PP_THREADRUNNING;
        !          1256: 
        !          1257:             LeaveSplSem();
        !          1258:             SplOutSem();
        !          1259:              return;
        !          1260:         }
        !          1261: 
        !          1262:         i = WaitResult;
        !          1263: 
        !          1264: 
        !          1265:         /* Set up the transmission structure with the handles etc. needed by
        !          1266:          * the completion callback routine:
        !          1267:          */
        !          1268:         pOverlapped = (LPOVERLAPPED)LocalAlloc( LMEM_ZEROINIT, sizeof( OVERLAPPED ) );
        !          1269:         pTransmission = (PTRANSMISSION)LocalAlloc( LMEM_ZEROINIT, sizeof( TRANSMISSION ) );
        !          1270: 
        !          1271:         if( pOverlapped && pTransmission )
        !          1272:         {
        !          1273:             /* hEvent isn't used in Win32.
        !          1274:              * We can use it for our own data:
        !          1275:              */
        !          1276:             pTransmission->hPipe = hPipe[i];
        !          1277:             pOverlapped->hEvent = (HANDLE)pTransmission;
        !          1278:             pTransmission->pOverlapped = &Overlapped[i];
        !          1279:             pTransmission->PipeInstance = i;
        !          1280:             pTransmission->hPrinter = NULL;
        !          1281:             pTransmission->hFile = NULL;    // CompleteRead will initialise this
        !          1282:             pTransmission->pIniPort = pIniPort;
        !          1283: 
        !          1284:             if( !ReadFileEx( hPipe[i], pTransmission->Data, TRANSMISSION_DATA_SIZE,
        !          1285:                              pOverlapped, CompleteRead ) )
        !          1286:             {
        !          1287:                 DBGMSG( DBG_ERROR, ( "ReadFileEx[%d] Failed with %d\n", i, GetLastError( ) ) );
        !          1288: 
        !          1289:                 if (GetLastError() == ERROR_BROKEN_PIPE)
        !          1290:                 {
        !          1291:                     /* If the pipe has broken, we must disconnect
        !          1292:                      * then reconnect for further transmissions
        !          1293:                      * to succeed:
        !          1294:                      */
        !          1295:                     DisconnectNamedPipe( hPipe[i] );
        !          1296: 
        !          1297:                     if( !ConnectNamedPipe( hPipe[i], &Overlapped[i] ) )
        !          1298:                     {
        !          1299:                         Error = GetLastError( );
        !          1300: 
        !          1301:                         if( Error == ERROR_IO_PENDING )
        !          1302:                         {
        !          1303:                             DBGMSG( DBG_INFO, ( "re-ConnectNamedPipe %d, IO pending\n",
        !          1304:                                     pTransmission->PipeInstance ) );
        !          1305:                         }
        !          1306:                         else
        !          1307:                         {
        !          1308:                             DBGMSG( DBG_ERROR, ( "re-ConnectNamedPipe %d failed. Error %d\n",
        !          1309:                                     pTransmission->PipeInstance, GetLastError( ) ) );
        !          1310:                         }
        !          1311:                     }
        !          1312:                 }
        !          1313: 
        !          1314:                 LocalFree( pTransmission );
        !          1315:                 LocalFree( pOverlapped );
        !          1316: 
        !          1317:             }
        !          1318:             else
        !          1319:             {
        !          1320:                 DBGMSG( DBG_INFO, ( "ReadFileEx succeeded" ) );
        !          1321: 
        !          1322:             }
        !          1323:         }
        !          1324:     }
        !          1325: }
        !          1326: 
        !          1327: 
        !          1328: 
        !          1329: #define ADDJOB_INFO_SIZE         ( sizeof( ADDJOB_INFO_1 ) + MAX_PATH )
        !          1330: 
        !          1331: VOID CompleteRead( DWORD Error, DWORD ByteCount, LPOVERLAPPED pOverlapped )
        !          1332: {
        !          1333:     PTRANSMISSION pTransmission;
        !          1334:     DWORD         BytesWritten;
        !          1335:     BOOL          ReadResult;
        !          1336:     DWORD         cbNeeded;
        !          1337:     HANDLE        hFile;
        !          1338:     WCHAR         szDefaultPrinter[MAX_PATH];
        !          1339: 
        !          1340:     pTransmission = (PTRANSMISSION)pOverlapped->hEvent;
        !          1341: 
        !          1342:     DBGMSG( DBG_INFO, ( "CompleteRead: Pipe Instance %d; Error = %d; ByteCount = %d\n",
        !          1343:              pTransmission->PipeInstance, Error, ByteCount ) );
        !          1344: 
        !          1345:     if (!ImpersonateNamedPipeClient(pTransmission->hPipe))
        !          1346:         DBGMSG( DBG_ERROR, ("ImpersonateNamedPipeClient failed %d\n",
        !          1347:                 GetLastError()));
        !          1348: 
        !          1349:     if( pTransmission->hFile == NULL )
        !          1350:     {
        !          1351:         PADDJOB_INFO_1 pAddJobInfo;
        !          1352:         BYTE           AddJobInfo[ADDJOB_INFO_SIZE];
        !          1353: 
        !          1354:         pAddJobInfo = (PADDJOB_INFO_1)AddJobInfo;
        !          1355: 
        !          1356:         OpenProfileUserMapping();
        !          1357: 
        !          1358:         GetProfileString(L"windows", L"device", L"", szDefaultPrinter,
        !          1359:                          sizeof(szDefaultPrinter)/sizeof(szDefaultPrinter[0]));
        !          1360: 
        !          1361:         CloseProfileUserMapping();
        !          1362: 
        !          1363:         wcstok(szDefaultPrinter, L",");
        !          1364: 
        !          1365:         /* Open the port (???) !!!
        !          1366:          */
        !          1367:         if( !OpenPrinter( szDefaultPrinter, &pTransmission->hPrinter, NULL ) )
        !          1368:         {
        !          1369:             DBGMSG( DBG_ERROR, ( "OpenPrinter failed: Error %d", GetLastError( ) ) );
        !          1370:             return;
        !          1371:         }
        !          1372: 
        !          1373:         memset( &AddJobInfo, '\0', ADDJOB_INFO_SIZE );
        !          1374: 
        !          1375:         if( AddJob( pTransmission->hPrinter, 1, AddJobInfo, ADDJOB_INFO_SIZE, &cbNeeded ) )
        !          1376:         {
        !          1377:             DBGMSG( DBG_INFO, ( "AddJob: JobId = %d\n", pAddJobInfo->JobId ) );
        !          1378: 
        !          1379:             hFile = CreateFile( pAddJobInfo->Path,
        !          1380:                                 GENERIC_WRITE,
        !          1381:                                 FILE_SHARE_READ,
        !          1382:                                 NULL,             // PSECURITY_DESCRIPTOR
        !          1383:                                 CREATE_ALWAYS,
        !          1384:                                 FILE_FLAG_SEQUENTIAL_SCAN,
        !          1385:                                 NULL );
        !          1386: 
        !          1387:             if( hFile != INVALID_HANDLE_VALUE )
        !          1388:             {
        !          1389:                 DBGMSG( DBG_INFO, ( "File created\n" ) );
        !          1390: 
        !          1391:                 pTransmission->JobId    = pAddJobInfo->JobId;
        !          1392:                 pTransmission->hFile    = hFile;
        !          1393: 
        !          1394:             }
        !          1395:             else
        !          1396:             {
        !          1397:                 DBGMSG( DBG_ERROR, ( "CreateFile %s failed: Error %d\n",
        !          1398:                         pAddJobInfo->Path, GetLastError( ) ) );
        !          1399:             }
        !          1400:         }
        !          1401:         else
        !          1402:         {
        !          1403:             DBGMSG( DBG_ERROR, ( "AddJob failed: Error %d\n", GetLastError( ) ) );
        !          1404:         }
        !          1405:     }
        !          1406: 
        !          1407:     if( Error == NO_ERROR )
        !          1408:     {
        !          1409:         if( !WriteFile( pTransmission->hFile,
        !          1410:                         pTransmission->Data,
        !          1411:                         ByteCount,
        !          1412:                         &BytesWritten,
        !          1413:                         NULL ) )
        !          1414:         {
        !          1415:             DBGMSG( DBG_ERROR, ( "WriteFile failed: Error %d\n", GetLastError( ) ) );
        !          1416:         }
        !          1417: 
        !          1418:         memset( pTransmission->Data, '\0', TRANSMISSION_DATA_SIZE );
        !          1419: 
        !          1420:         ReadResult = ReadFileEx( pTransmission->hPipe, pTransmission->Data,
        !          1421:                                  TRANSMISSION_DATA_SIZE, pOverlapped, CompleteRead );
        !          1422: 
        !          1423:         if( ReadResult == FALSE )
        !          1424:         {
        !          1425:             Error = GetLastError( );
        !          1426:             DBGMSG( DBG_ERROR, ( "ReadFileEx failed: Error %d\n", Error ) );
        !          1427:         }
        !          1428:     }
        !          1429: 
        !          1430:     if( Error != NO_ERROR )
        !          1431:     {
        !          1432:         DisconnectNamedPipe( pTransmission->hPipe );
        !          1433: 
        !          1434:         if( !ConnectNamedPipe( pTransmission->hPipe, pTransmission->pOverlapped ) )
        !          1435:         {
        !          1436:             Error = GetLastError( );
        !          1437: 
        !          1438:             if( Error == ERROR_IO_PENDING )
        !          1439:             {
        !          1440:                 DBGMSG( DBG_INFO, ( "re-ConnectNamedPipe %d, IO pending\n",
        !          1441:                         pTransmission->PipeInstance ) );
        !          1442:             }
        !          1443:             else
        !          1444:             {
        !          1445:                 DBGMSG( DBG_ERROR, ("re-ConnectNamedPipe %d failed. Error %d\n",
        !          1446:                         pTransmission->PipeInstance, GetLastError( ) ) );
        !          1447:             }
        !          1448:         }
        !          1449: 
        !          1450:         if( !CloseHandle( pTransmission->hFile ) )
        !          1451:         {
        !          1452:             DBGMSG( DBG_ERROR, ( "CloseHandle failed: Error %d\n", GetLastError( ) ) );
        !          1453:         }
        !          1454: 
        !          1455:         if( !ScheduleJob( pTransmission->hPrinter, pTransmission->JobId ) )
        !          1456:         {
        !          1457:             DBGMSG( DBG_ERROR, ( "ScheduleJob failed: Error %d\n", GetLastError( ) ) );
        !          1458:         }
        !          1459: 
        !          1460:         ClosePrinter(pTransmission->hPrinter);
        !          1461: 
        !          1462:         LocalFree( pTransmission );
        !          1463:         LocalFree( pOverlapped );
        !          1464: 
        !          1465:         DBGMSG( DBG_INFO, ( "\nEnd of transmission\n" ) );
        !          1466:     }
        !          1467: 
        !          1468: /*  !!! HACK HACK HACK swapping in and out of impersonation doesn't work
        !          1469:     if (!RevertToSelf())
        !          1470:         DBGMSG( DBG_ERROR, ( "RevertToSelf failed: Error %d\n", GetLastError( ) ) );
        !          1471: */
        !          1472: }
        !          1473: 
        !          1474: 
        !          1475: 
        !          1476: 
        !          1477: /* CreateNamedPipeSecurityDescriptor
        !          1478:  *
        !          1479:  * Creates a security descriptor giving everyone access
        !          1480:  *
        !          1481:  * Arguments: None
        !          1482:  *
        !          1483:  * Return: The security descriptor returned by BuildPrintObjectProtection.
        !          1484:  *
        !          1485:  */
        !          1486: #define MAX_ACE 2
        !          1487: #define DBGCHK( Condition, ErrorInfo ) \
        !          1488:     if( Condition ) DBGMSG( DBG_ERROR, ErrorInfo )
        !          1489: 
        !          1490: PSECURITY_DESCRIPTOR
        !          1491: CreateNamedPipeSecurityDescriptor(
        !          1492:     VOID
        !          1493: )
        !          1494: {
        !          1495:     PSID AceSid[MAX_ACE];          // Don't expect more than MAX_ACE ACEs in any of these.
        !          1496:     ACCESS_MASK AceMask[MAX_ACE];  // Access masks corresponding to Sids
        !          1497:     BYTE InheritFlags[MAX_ACE];  //
        !          1498:     ULONG AceCount;
        !          1499:     SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
        !          1500:     PSID WorldSid;
        !          1501:     PSECURITY_DESCRIPTOR ServerSD;
        !          1502:     BOOL OK;
        !          1503: 
        !          1504:     //
        !          1505:     // Printer SD
        !          1506:     //
        !          1507: 
        !          1508:     AceCount = 0;
        !          1509: 
        !          1510:     /* World SID */
        !          1511: 
        !          1512:     OK = AllocateAndInitializeSid( &WorldSidAuthority, 1,
        !          1513:                                    SECURITY_WORLD_RID,
        !          1514:                                    0, 0, 0, 0, 0, 0, 0,
        !          1515:                                    &WorldSid );
        !          1516: 
        !          1517:     DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
        !          1518: 
        !          1519:     AceSid[AceCount]           = WorldSid;
        !          1520:     AceMask[AceCount]          = GENERIC_ALL;
        !          1521:     InheritFlags[AceCount]     = 0;
        !          1522:     AceCount++;
        !          1523: 
        !          1524:     OK = BuildPrintObjectProtection( AceCount,
        !          1525:                                      AceSid,
        !          1526:                                      AceMask,
        !          1527:                                      InheritFlags,
        !          1528:                                      WorldSid,
        !          1529:                                      NULL,
        !          1530:                                      &ServerSD );
        !          1531: 
        !          1532:     FreeSid( WorldSid );
        !          1533: 
        !          1534:     return ServerSD;
        !          1535: }
        !          1536: 
        !          1537: 
        !          1538: BOOL
        !          1539: BuildPrintObjectProtection(
        !          1540:     IN ULONG AceCount,
        !          1541:     IN PSID *AceSid,
        !          1542:     IN ACCESS_MASK *AceMask,
        !          1543:     IN BYTE *InheritFlags,
        !          1544:     IN PSID WorldSid,
        !          1545:     IN PGENERIC_MAPPING GenericMap,
        !          1546:     OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
        !          1547:     )
        !          1548: 
        !          1549: /*++
        !          1550: 
        !          1551: 
        !          1552: Routine Description:
        !          1553: 
        !          1554:     This routine builds a self-relative security descriptor ready
        !          1555:     to be applied to one of the print manager objects.
        !          1556: 
        !          1557:     If so indicated, a pointer to the last RID of the SID in the last
        !          1558:     ACE of the DACL is returned and a flag set indicating that the RID
        !          1559:     must be replaced before the security descriptor is applied to an object.
        !          1560:     This is to support USER object protection, which must grant some
        !          1561:     access to the user represented by the object.
        !          1562: 
        !          1563:     The owner and group of each security descriptor will be set
        !          1564:     to:
        !          1565: 
        !          1566:                     Owner:  Administrators Alias
        !          1567:                     Group:  Administrators Alias
        !          1568: 
        !          1569: 
        !          1570:     The SACL of each of these objects will be set to:
        !          1571: 
        !          1572: 
        !          1573:                     Audit
        !          1574:                     Success | Fail
        !          1575:                     WORLD
        !          1576:                     (Write | Delete | WriteDacl | AccessSystemSecurity)
        !          1577: 
        !          1578: 
        !          1579: 
        !          1580: Arguments:
        !          1581: 
        !          1582:     AceCount - The number of ACEs to be included in the DACL.
        !          1583: 
        !          1584:     AceSid - Points to an array of SIDs to be granted access by the DACL.
        !          1585:         If the target SAM object is a User object, then the last entry
        !          1586:         in this array is expected to be the SID of an account within the
        !          1587:         domain with the last RID not yet set.  The RID will be set during
        !          1588:         actual account creation.
        !          1589: 
        !          1590:     AceMask - Points to an array of accesses to be granted by the DACL.
        !          1591:         The n'th entry of this array corresponds to the n'th entry of
        !          1592:         the AceSid array.  These masks should not include any generic
        !          1593:         access types.
        !          1594: 
        !          1595:     InheritOnly - Inidicates whether each ace is inherit only or not.
        !          1596: 
        !          1597:     GenericMap - Points to a generic mapping for the target object type.
        !          1598: 
        !          1599: 
        !          1600:     UserObject - Indicates whether the target SAM object is a User object
        !          1601:         or not.  If TRUE (it is a User object), then the resultant
        !          1602:         protection will be set up indicating Rid replacement is necessary.
        !          1603: 
        !          1604:     Result - Receives a pointer to the resultant protection information.
        !          1605:         All access masks in ACLs in the result are mapped to standard and
        !          1606:         specific accesses.
        !          1607: 
        !          1608: 
        !          1609: Return Value:
        !          1610: 
        !          1611:     TBS.
        !          1612: 
        !          1613: --*/
        !          1614: {
        !          1615: 
        !          1616: 
        !          1617: 
        !          1618:     SECURITY_DESCRIPTOR     Absolute;
        !          1619:     PSECURITY_DESCRIPTOR    Relative;
        !          1620:     PACL                    TmpAcl;
        !          1621:     PACCESS_ALLOWED_ACE     TmpAce;
        !          1622:     ULONG                   SDLength;
        !          1623:     ULONG                   DaclLength;
        !          1624:     ULONG                   i;
        !          1625:     BOOL                    OK;
        !          1626: 
        !          1627:     //
        !          1628:     // The approach is to set up an absolute security descriptor that
        !          1629:     // looks like what we want and then copy it to make a self-relative
        !          1630:     // security descriptor.
        !          1631:     //
        !          1632: 
        !          1633:     OK = InitializeSecurityDescriptor( &Absolute,
        !          1634:                                        SECURITY_DESCRIPTOR_REVISION1 );
        !          1635: 
        !          1636:     DBGCHK( !OK, ( "Failed to initialize security descriptor.  Error %d", GetLastError() ) );
        !          1637: 
        !          1638:     //
        !          1639:     // Owner
        !          1640:     //
        !          1641:                                                 // No owner -- OK??
        !          1642: 
        !          1643:     OK = SetSecurityDescriptorOwner( &Absolute, NULL, FALSE );
        !          1644: 
        !          1645:     DBGCHK( !OK, ( "Failed to set security descriptor owner.  Error %d", GetLastError() ) );
        !          1646: 
        !          1647: 
        !          1648:     //
        !          1649:     // Group
        !          1650:     //
        !          1651: 
        !          1652:     OK = SetSecurityDescriptorGroup( &Absolute, WorldSid, FALSE );
        !          1653: 
        !          1654:     DBGCHK( !OK, ( "Failed to set security descriptor group.  Error %d", GetLastError() ) );
        !          1655: 
        !          1656: 
        !          1657: 
        !          1658: 
        !          1659:     //
        !          1660:     // Discretionary ACL
        !          1661:     //
        !          1662:     //      Calculate its length,
        !          1663:     //      Allocate it,
        !          1664:     //      Initialize it,
        !          1665:     //      Add each ACE
        !          1666:     //      Set ACE as InheritOnly if necessary
        !          1667:     //      Add it to the security descriptor
        !          1668:     //
        !          1669: 
        !          1670:     DaclLength = (ULONG)sizeof(ACL);
        !          1671:     for (i=0; i<AceCount; i++) {
        !          1672: 
        !          1673:         DaclLength += GetLengthSid( AceSid[i] ) +
        !          1674:                       (ULONG)sizeof(ACCESS_ALLOWED_ACE) -
        !          1675:                       (ULONG)sizeof(ULONG);  //Subtract out SidStart field length
        !          1676:     }
        !          1677: 
        !          1678:     EnterSplSem( );
        !          1679:     TmpAcl = AllocSplMem( DaclLength );
        !          1680:     LeaveSplSem( );
        !          1681: 
        !          1682:     DBGCHK( !TmpAcl, ( "Out of heap space: Can't allocate ACL." ) );
        !          1683: 
        !          1684:     OK = InitializeAcl( TmpAcl, DaclLength, ACL_REVISION2 );
        !          1685: 
        !          1686:     DBGCHK( !OK, ( "Failed to set initialize ACL.  Error %d", GetLastError() ) );
        !          1687: 
        !          1688:     for (i=0; i<AceCount; i++)
        !          1689:     {
        !          1690:         OK = AddAccessAllowedAce ( TmpAcl, ACL_REVISION2, AceMask[i], AceSid[i] );
        !          1691: 
        !          1692:         DBGCHK( !OK, ( "Failed to add access-allowed ACE.  Error %d", GetLastError() ) );
        !          1693: 
        !          1694:         if (InheritFlags[i] != 0)
        !          1695:         {
        !          1696:             OK = GetAce( TmpAcl, i, (LPVOID *)&TmpAce );
        !          1697:             DBGCHK( !OK, ( "Failed to get ACE.  Error %d", GetLastError() ) );
        !          1698: 
        !          1699:             TmpAce->Header.AceFlags = InheritFlags[i];
        !          1700:         }
        !          1701:     }
        !          1702: 
        !          1703:     OK = SetSecurityDescriptorDacl (&Absolute, TRUE, TmpAcl, FALSE );
        !          1704:     DBGCHK( !OK, ( "Failed to set security descriptor DACL.  Error %d", GetLastError() ) );
        !          1705: 
        !          1706: 
        !          1707: 
        !          1708:     //
        !          1709:     // Convert the Security Descriptor to Self-Relative
        !          1710:     //
        !          1711:     //      Get the length needed
        !          1712:     //      Allocate that much memory
        !          1713:     //      Copy it
        !          1714:     //      Free the generated absolute ACLs
        !          1715:     //
        !          1716: 
        !          1717:     SDLength = GetSecurityDescriptorLength( &Absolute );
        !          1718: 
        !          1719:     EnterSplSem( );
        !          1720:     Relative = AllocSplMem( SDLength );
        !          1721:     LeaveSplSem( );
        !          1722: 
        !          1723:     DBGCHK( !Relative, ( "Out of heap space: Can't allocate security descriptor" ) );
        !          1724: 
        !          1725:     OK = MakeSelfRelativeSD(&Absolute, Relative, &SDLength );
        !          1726: 
        !          1727:     DBGCHK( !OK, ( "Failed to create self-relative security descriptor DACL.  Error %d", GetLastError() ) );
        !          1728: 
        !          1729:     EnterSplSem( );
        !          1730:     FreeSplMem( Absolute.Dacl, DaclLength );
        !          1731:     LeaveSplSem( );
        !          1732: 
        !          1733:     *ppSecurityDescriptor = Relative;
        !          1734: 
        !          1735:     return( OK );
        !          1736: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.