Annotation of mstools/samples/namepipe/npserver/server32.c, revision 1.1

1.1     ! root        1: /******************************************************************************\
        !             2: *       This is a part of the Microsoft Source Code Samples. 
        !             3: *       Copyright (C) 1993 Microsoft Corporation.
        !             4: *       All rights reserved. 
        !             5: *       This source code is only intended as a supplement to 
        !             6: *       Microsoft Development Tools and/or WinHelp documentation.
        !             7: *       See these sources for detailed information regarding the 
        !             8: *       Microsoft samples programs.
        !             9: \******************************************************************************/
        !            10: 
        !            11: /*************************************************************************\
        !            12: *  PROGRAM: server32.c
        !            13: *
        !            14: *  PURPOSE:
        !            15: *
        !            16: *     To demonstrate the use of named pipes and the overlapped structure.
        !            17: *     This code serves as the server side of the named pipe instances.
        !            18: *     For more details on an overview of this codes designs or use, see
        !            19: *     the README file.  For details on the implementation, see the comments
        !            20: *     in this code.
        !            21: *
        !            22: *
        !            23: \*************************************************************************/
        !            24: 
        !            25: #define  STRICT
        !            26: #include <windows.h>
        !            27: #include "server32.h"
        !            28: 
        !            29: #include <string.h>
        !            30: #include <stdio.h>
        !            31: #include <stdlib.h>
        !            32: 
        !            33:                                        // clients[] is a global array of
        !            34:                                        // structures used to keep track
        !            35:                                        // of the multiple instances of
        !            36:                                        // the server side of the named
        !            37:                                        // pipe.  As a client connects
        !            38:                                        // to a given instance, a new
        !            39:                                        // server thread is created and
        !            40:                                        // added to the array.
        !            41: WRTHANDLE clients[MAX_PIPE_INSTANCES];
        !            42: DWORD     clientCount = 0;             // Global count of connected clients.
        !            43: 
        !            44: HWND   hWnd;
        !            45: HANDLE hInst;
        !            46: 
        !            47: 
        !            48: /*************************************************************************\
        !            49: *
        !            50: *  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
        !            51: *
        !            52: *  PURPOSE: calls initialization function, processes message loop
        !            53: *
        !            54: *  COMMENTS:
        !            55: *
        !            56: \*************************************************************************/
        !            57: 
        !            58: int APIENTRY WinMain (HINSTANCE hInstance,
        !            59:                       HINSTANCE hPrevInstance,
        !            60:                       LPSTR  lpCmdLine,
        !            61:                       int    nCmdShow)
        !            62: 
        !            63: 
        !            64: {
        !            65: 
        !            66:   MSG  msg;
        !            67:   WNDCLASS wc;
        !            68: 
        !            69: 
        !            70:   UNREFERENCED_PARAMETER( lpCmdLine );
        !            71:   UNREFERENCED_PARAMETER( hPrevInstance );
        !            72: 
        !            73:   hInst = hInstance;
        !            74: 
        !            75:   wc.style = 0;
        !            76:   wc.lpfnWndProc = (WNDPROC)MainWndProc;
        !            77:   wc.cbClsExtra = 0;
        !            78:   wc.cbWndExtra = 0;
        !            79:   wc.hInstance = hInstance;
        !            80:   wc.hIcon = LoadIcon (hInstance, "npserver");
        !            81:   wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        !            82:   wc.hbrBackground = GetStockObject (WHITE_BRUSH);
        !            83:   wc.lpszMenuName = "PipeMenu";
        !            84:   wc.lpszClassName = "PipeWClass";
        !            85: 
        !            86:   RegisterClass(&wc);
        !            87: 
        !            88:   hWnd = CreateWindow ("PipeWClass",
        !            89:                        "Server32 Named Pipe Sample",
        !            90:                        WS_OVERLAPPEDWINDOW,
        !            91:                        CW_USEDEFAULT,
        !            92:                        CW_USEDEFAULT,
        !            93:                        CW_USEDEFAULT,
        !            94:                        CW_USEDEFAULT,
        !            95:                        NULL,
        !            96:                        NULL,
        !            97:                        hInstance,
        !            98:                        NULL);
        !            99: 
        !           100: 
        !           101:   ShowWindow (hWnd, nCmdShow);
        !           102: 
        !           103:   while (GetMessage (&msg, NULL, 0, 0))
        !           104:     DispatchMessage (&msg);
        !           105: 
        !           106:   return (msg.wParam);
        !           107: 
        !           108: }
        !           109: 
        !           110: /*************************************************************************\
        !           111: *
        !           112: *  FUNCTION:  MainWndProc (HWND, UINT, WPARAM, LPARAM)
        !           113: *
        !           114: *  PURPOSE:   To process messages.  To launch client and server threads
        !           115: *
        !           116: \*************************************************************************/
        !           117: 
        !           118: LONG CALLBACK MainWndProc (HWND   hwnd,
        !           119:                            UINT   message,
        !           120:                            WPARAM wParam,
        !           121:                            LPARAM lParam)
        !           122: {
        !           123: 
        !           124:   LONG        lpServerThreadID;
        !           125:   PAINTSTRUCT paintStruct;
        !           126:   HDC         hDC;
        !           127: 
        !           128:   switch (message)
        !           129:       {
        !           130:         case WM_PAINT:
        !           131:            // DrawBranch is used to paint the spools and text to the window.
        !           132:            hDC = BeginPaint (hwnd, &paintStruct);
        !           133:            DrawBranch (hDC);
        !           134:            EndPaint (hwnd, &paintStruct);
        !           135:            return(0);
        !           136: 
        !           137:         case WM_CREATE :
        !           138:            // Create the first instance of a server side of the pipe.
        !           139:            CreateThread ((LPSECURITY_ATTRIBUTES)NULL,       // No security.
        !           140:                          (DWORD)0,                          // Same stack size.
        !           141:                          (LPTHREAD_START_ROUTINE)ServerProc,// Thread procedure.
        !           142:                          (LPVOID)&hwnd,                     // Parameter.
        !           143:                          (DWORD)0,                          // Start immediatly.
        !           144:                          (LPDWORD)&lpServerThreadID);       // Thread ID.
        !           145:            return (0);
        !           146: 
        !           147:         case WM_DESTROY :
        !           148:            PostQuitMessage (0);
        !           149:            return (0);
        !           150:        }
        !           151:     return DefWindowProc (hwnd, message, wParam, lParam);
        !           152: }
        !           153: 
        !           154: 
        !           155: 
        !           156: /*************************************************************************\
        !           157: *
        !           158: *  PROCEDURE: ServerProc (HWND *hWnd)
        !           159: *
        !           160: *  PURPOSE:
        !           161: *
        !           162: *    A thread procedure, which creates an instance of the server side of
        !           163: *    the named pipe, and then blocks waiting for a client to connect.
        !           164: *    Once the client connects, a global array is updated with the specific
        !           165: *    clients information, and this procedure is called again
        !           166: *    to launch another waiting server thread.  After launching the new
        !           167: *    thread, this thread begins to loop, reading the named pipe.  When
        !           168: *    a message comes from it's client, it uses TellAll() to broadcast
        !           169: *    the message to the other clients in the array.
        !           170: *
        !           171: *  CALLED BY:
        !           172: *
        !           173: *    ServerProc();
        !           174: *    WinMain();
        !           175: *
        !           176: *  CALLS TO:
        !           177: *
        !           178: *    TellAll();
        !           179: *    ServerProc().
        !           180: *
        !           181: *  COMMENTS:
        !           182: *
        !           183: *    Clients is a global array which hold information on each client
        !           184: *    connected to the named pipe.  This procedure recieves a buffer.
        !           185: *    It then steps through this global array, and for each client it
        !           186: *    writes the buffer.
        !           187: *
        !           188: \*************************************************************************/
        !           189: 
        !           190: VOID ServerProc(HWND *hWnd)
        !           191:  {
        !           192:    HANDLE hPipe;                       // Pipe handle.
        !           193:    CHAR   inBuf[IN_BUF_SIZE] = "";     // Input buffer for pipe.
        !           194:    DWORD  ServerThreadID;              // Used for CreateThread().
        !           195: 
        !           196:    CHAR   errorBuf[LINE_LEN] = "";     // Used for error messages.
        !           197:    DWORD  bytesRead;                   // Used in ReadFile().
        !           198:    DWORD  retCode;                     // Used to trap return codes.
        !           199:    DWORD  clientIndex;                 // Index into global array, for this
        !           200:                                        // instances client.
        !           201:    DWORD  lastError;                   // Traps returns from GetLastError().
        !           202:    BOOL   ExitLoop = FALSE;            // Boolean Flag to exit loop.
        !           203: 
        !           204:    OVERLAPPED OverLapWrt;              // Overlapped structure for writing.
        !           205:    HANDLE     hEventWrt;               // Event handle for overlapped write.
        !           206: 
        !           207:    OVERLAPPED OverLapRd;               // Overlapped structure for reading.
        !           208:    HANDLE     hEventRd;                // Event handle for overlapped reads.
        !           209:    DWORD        bytesTransRd;          // Bytes transferred by overlapped.
        !           210:    PSECURITY_DESCRIPTOR    pSD;
        !           211:    SECURITY_ATTRIBUTES     sa;
        !           212: 
        !           213:                                        // create a security NULL security
        !           214:                                        // descriptor, one that allows anyone
        !           215:                                        // to write to the pipe... WARNING
        !           216:                                        // entering NULL as the last attribute
        !           217:                                        // of the CreateNamedPipe() will
        !           218:                                        // indicate that you wish all
        !           219:                                        // clients connecting to it to have
        !           220:                                        // all of the same security attributes
        !           221:                                        // as the user that started the
        !           222:                                        // pipe server.
        !           223: 
        !           224:    pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
        !           225:                SECURITY_DESCRIPTOR_MIN_LENGTH);
        !           226: 
        !           227:    if (pSD == NULL)
        !           228:      {
        !           229:      MessageBox (*hWnd, "Error in LocalAlloc for pSD",
        !           230:                  "Debug: ServerProc()", MB_OK);
        !           231:      return;
        !           232:      }
        !           233: 
        !           234:    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
        !           235:      {
        !           236:      wsprintf (errorBuf, "Error: InitializeSecurityDescriptor() %d",
        !           237:                GetLastError());
        !           238:      MessageBox (*hWnd, errorBuf, "Debug: ServerProc()", MB_OK);
        !           239:        LocalFree((HLOCAL)pSD);
        !           240:        return;
        !           241:      }
        !           242: 
        !           243:                                        // add a NULL disc. ACL to the
        !           244:                                        // security descriptor.
        !           245: 
        !           246:    if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE))
        !           247:      {
        !           248:      wsprintf (errorBuf, "Error: SetSecurityDescriptorDacl() %d",
        !           249:                 GetLastError());
        !           250:      MessageBox (*hWnd, errorBuf, "Debug: ServerProc()", MB_OK);
        !           251:      LocalFree((HLOCAL)pSD);
        !           252:      return;
        !           253:      }
        !           254: 
        !           255:    sa.nLength = sizeof(sa);
        !           256:    sa.lpSecurityDescriptor = pSD;
        !           257:    sa.bInheritHandle = TRUE;
        !           258: 
        !           259:                                        // Create a local named pipe with
        !           260:                                        // the name '\\.\PIPE\test'.  The
        !           261:                                        // '.' signifies local pipe.
        !           262:    hPipe = CreateNamedPipe ("\\\\.\\PIPE\\test", // Pipe name = 'test'.
        !           263:                PIPE_ACCESS_DUPLEX                // 2 way pipe.
        !           264:                | FILE_FLAG_OVERLAPPED,           // Use overlapped structure.
        !           265:                PIPE_WAIT                         // Wait on messages.
        !           266:                | PIPE_READMODE_MESSAGE           // Specify message mode pipe.
        !           267:                | PIPE_TYPE_MESSAGE,
        !           268:                MAX_PIPE_INSTANCES,               // Maximum instance limit.
        !           269:                OUT_BUF_SIZE,                     // Buffer sizes.
        !           270:                IN_BUF_SIZE,
        !           271:                TIME_OUT,                         // Specify time out.
        !           272:                &sa);                             // Security attributes.
        !           273: 
        !           274:                                        // Check Errors.
        !           275:     if ((DWORD)hPipe == 0xFFFFFFFF)
        !           276:      {
        !           277: 
        !           278:      retCode = GetLastError();         // Report any error, it should always
        !           279:      wsprintf (errorBuf,               // succeed.
        !           280:                "Error return code from CreateNamedPipe = %li.",
        !           281:                 retCode);
        !           282:      MessageBox (*hWnd, errorBuf, "Debug Window",
        !           283:                  MB_ICONINFORMATION | MB_OK | MB_APPLMODAL);
        !           284:      };
        !           285: 
        !           286:                                        // Block until a client connects.
        !           287:    ConnectNamedPipe(hPipe, NULL);
        !           288: 
        !           289:                                        // Create and init overlap for writing.
        !           290:    hEventWrt = CreateEvent (NULL, TRUE, FALSE, NULL);
        !           291:    memset (&OverLapWrt, 0, sizeof(OVERLAPPED));
        !           292:    OverLapWrt.hEvent = hEventWrt;
        !           293: 
        !           294:                                        // Set the clientIndex, then increment
        !           295:                                        // the count.  Fill in the structure
        !           296:                                        // for this client in the array.
        !           297:    clientIndex = clientCount++;
        !           298:    clients[clientIndex].hPipe   = hPipe;
        !           299:    clients[clientIndex].live    = TRUE;
        !           300:    clients[clientIndex].overLap = OverLapWrt;
        !           301:    clients[clientIndex].hEvent  = hEventWrt;
        !           302: 
        !           303:                                        // Create and init overlap for reading.
        !           304:    hEventRd = CreateEvent(NULL,TRUE,FALSE,NULL);
        !           305:    memset (&OverLapRd, 0, sizeof(OVERLAPPED));
        !           306:    OverLapRd.hEvent = hEventRd;
        !           307: 
        !           308:                                        // Read from the client, the first
        !           309:                                        // first message should always be
        !           310:                                        // the clients user name.
        !           311:    retCode = ReadFile (hPipe, inBuf, PLEASE_READ, &bytesRead, &OverLapRd);
        !           312: 
        !           313:    if (!retCode)
        !           314:     lastError = GetLastError();
        !           315: 
        !           316:    if (lastError == ERROR_IO_PENDING)  // Wait on read if need be.
        !           317:      WaitForSingleObject (hEventRd, (DWORD)-1);
        !           318: 
        !           319:                                        // Put client's name in the array.
        !           320:    strcpy (clients[clientIndex].Name, inBuf);
        !           321: 
        !           322:                                        // Create a thread which will make
        !           323:                                        // another server instance of the
        !           324:                                        // named pipe.
        !           325:    CreateThread ((LPSECURITY_ATTRIBUTES)NULL,        // No security attributes.
        !           326:                  (DWORD)0,                           // Use same stack size.
        !           327:                  (LPTHREAD_START_ROUTINE)ServerProc, // Thread procedure.
        !           328:                  (LPVOID)hWnd,                       // Parameter to pass.
        !           329:                  (DWORD)0,                           // Run immediately.
        !           330:                  (LPDWORD)&ServerThreadID);          // Thread identifier.
        !           331: 
        !           332:    TellAll("");                        // Forces a paint, draws a red spool
        !           333:                                        // and name for this client.
        !           334: 
        !           335:                                        // Do loop which basically reads from
        !           336:                                        // this specific client, and then
        !           337:                                        // uses TellAll() to broadcast the
        !           338:                                        // message to all the connected
        !           339:                                        // clients.
        !           340:    do{
        !           341:                                        // Read the pipe.
        !           342:       retCode = ReadFile (hPipe, inBuf, PLEASE_READ, &bytesRead, &OverLapRd);
        !           343: 
        !           344:                                        // Check for three kinds of errors:
        !           345:                                        // If Error = IO_PENDING, wait til
        !           346:                                        // the event handle signals success,
        !           347:                                        // If BROKEN_PIPE, exit the do loop.
        !           348:                                        // Any other error, flag it to the
        !           349:                                        // user and exit the do loop.
        !           350:       if (!retCode)
        !           351:         {
        !           352:         lastError = GetLastError();
        !           353: 
        !           354:         switch (lastError)
        !           355:           {
        !           356:                                        // IO_PENDING, wait on the event.
        !           357:            case ERROR_IO_PENDING:
        !           358:              WaitForSingleObject (hEventRd, (DWORD)-1);
        !           359:              break;
        !           360:                                        // Pipe is broken, exit the loop.
        !           361:            case ERROR_BROKEN_PIPE:
        !           362:              ExitLoop = TRUE;
        !           363:              break;
        !           364:                                        // Something else is wrong, exit the
        !           365:                                        // the loop after telling the user.
        !           366:            default:
        !           367:              wsprintf (errorBuf, "ReadFile Error in ServerProc()= %d",
        !           368:              lastError);
        !           369:              MessageBox (*hWnd, errorBuf, "Debug Information", MB_OK);
        !           370:              ExitLoop = TRUE;
        !           371:              break;
        !           372:           }
        !           373:         }
        !           374: 
        !           375:       if (!ExitLoop)
        !           376:         {
        !           377:         GetOverlappedResult (hPipe, &OverLapRd, &bytesTransRd, FALSE);
        !           378: 
        !           379:                                        // Use TellAll to broadcast the message.
        !           380:         if (bytesTransRd)
        !           381:           TellAll(inBuf);
        !           382:         else
        !           383:           TellAll("");
        !           384:         }
        !           385: 
        !           386:    }while(!ExitLoop);
        !           387: 
        !           388:    clients[clientIndex].live = FALSE;  // Turns spool gray.
        !           389:    CloseHandle (hPipe);                // Close handles.
        !           390:    CloseHandle (hEventRd);
        !           391:    CloseHandle (hEventWrt);
        !           392:    DisconnectNamedPipe (hPipe);        // Close pipe instance.
        !           393:    ExitThread(0);                      // Clean up and die.
        !           394:   }
        !           395: 
        !           396: 
        !           397: /*************************************************************************\
        !           398: *
        !           399: *  PROCEDURE: TellAll (CHAR *buffer)
        !           400: *
        !           401: *  PURPOSE:
        !           402: *
        !           403: *    To write the buffer (input parameter) to all of the clients listed
        !           404: *    in the global array "clients".
        !           405: *
        !           406: *  CALLED BY:
        !           407: *
        !           408: *    ServerProc();
        !           409: *
        !           410: *  COMMENTS:
        !           411: *
        !           412: *    Clients is a global array which hold information on each client
        !           413: *    connected to the named pipe.  This procedure recieves a buffer.
        !           414: *    It then steps through this global array, and for each client it
        !           415: *    writes the buffer.
        !           416: *
        !           417: \*************************************************************************/
        !           418: 
        !           419: VOID TellAll( CHAR *buffer )
        !           420:   {
        !           421:     DWORD i;                           // Index through array.
        !           422:     DWORD bytesWritten;                // Used in WriteFile().
        !           423:     DWORD retCode;                     // Traps return codes.
        !           424:     CHAR  Buf[LINE_LEN];               // Message Buffer.
        !           425:     DWORD lastError;                   // Traps returns from GetLastError().
        !           426: 
        !           427:     for(i=0; i < clientCount; i++)     // For all clients in the array.
        !           428:       {
        !           429:                                        // If client isn't alive, don't waste
        !           430:                                        // time writing to it.
        !           431:       if (clients[i].live)
        !           432:         {
        !           433:         retCode = WriteFile (clients[i].hPipe, buffer, strlen(buffer),
        !           434:                              &bytesWritten, &clients[i].overLap);
        !           435: 
        !           436:                                        // Check 3 kinds of errors: IO_PENDING,
        !           437:                                        // NO_DATA, or other.  Wait on event
        !           438:                                        // handle if IO_PENDING, else, if it's
        !           439:                                        // anything other than NO_DATA (pipe
        !           440:                                        // client disconnected), flag the user.
        !           441:                                        // In any case, if it's not IO_PENDING,
        !           442:                                        // clients[i].live = FALSE, spool turns
        !           443:                                        // gray.
        !           444:         if (!retCode)
        !           445:           {
        !           446:           lastError = GetLastError();
        !           447: 
        !           448:                                        // IO_PENDING, wait on event handle.
        !           449:           if (lastError == ERROR_IO_PENDING)
        !           450:             {
        !           451:             WaitForSingleObject (clients[i].hEvent, (DWORD)-1);
        !           452:             }
        !           453:           else
        !           454:             {
        !           455:                                        // If not NO_DATA, flag user.
        !           456:             if (lastError != ERROR_NO_DATA)
        !           457:               {
        !           458:               wsprintf (Buf, "%s = %d", buffer, GetLastError());
        !           459:               MessageBox(hWnd, Buf, "Debug,TellAll:", MB_OK);
        !           460:               }
        !           461:             clients[i].live = FALSE;
        !           462:             }
        !           463:           }
        !           464:         } //if client.live
        !           465:       } // for loop
        !           466:                                        // Paint window with new information.
        !           467:     InvalidateRect(hWnd, NULL, TRUE);
        !           468:   }
        !           469: 
        !           470: 
        !           471: 
        !           472: /*************************************************************************\
        !           473: *
        !           474: *  PROCEDURE: DrawBranch (HDC hDC)
        !           475: *
        !           476: *  PURPOSE:
        !           477: *
        !           478: *    To draw one of four bitmaps for each client, depending upon the clients
        !           479: *    status (alive = red spool, dead or disconnected = gray), and location in
        !           480: *    the array.  It also draws the clients user name beside the spool.
        !           481: *    This procedure is executed when the WM_PAINT message is trapped.
        !           482: *
        !           483: *  CALLED BY:
        !           484: *
        !           485: *    WinMain();
        !           486: *
        !           487: *  COMMENTS:
        !           488: *
        !           489: \*************************************************************************/
        !           490: VOID DrawBranch(HDC hDC)
        !           491: {
        !           492:                                        // Spool bitmaps.
        !           493:   HBITMAP hEndLive, hEndDead, hMidLive, hMidDead, hBitMap;
        !           494: 
        !           495:   HDC hDCMem;
        !           496:   int X, Y;
        !           497:   BITMAP bm;
        !           498:   POINT ptSize, ptOrg;
        !           499:   DWORD index;
        !           500: 
        !           501:                                        // Load bitmaps: two red (live),
        !           502:                                        // two dead (gray).  End = end
        !           503:                                        // of tree (last client to connect),
        !           504:                                        // mid means in the middle somewhere.
        !           505:        hEndLive = LoadBitmap (hInst, "EndLive");
        !           506:        hEndDead = LoadBitmap (hInst, "EndDead");
        !           507: 
        !           508:        hMidLive = LoadBitmap (hInst, "MidLive");
        !           509:        hMidDead = LoadBitmap (hInst, "MidDead");
        !           510: 
        !           511:                                        // For each client, determine if
        !           512:                                        // if alive or not, and position;
        !           513:                                        // then blt appropriate map and
        !           514:                                        // clients name.
        !           515:     for (index = 0; index < clientCount; index++)
        !           516:       {
        !           517: 
        !           518:       if (index < clientCount - 1)     // ClientCount - 1 = last (end) client.
        !           519:        {
        !           520:         if(clients[index].live)        // If live = red, else = gray.
        !           521:          hBitMap = hMidLive;
        !           522:         else
        !           523:          hBitMap = hMidDead;
        !           524:        }
        !           525:       else
        !           526:        {
        !           527:         if(clients[index].live)        // If live = red, else = gray.
        !           528:          hBitMap = hEndLive;
        !           529:         else
        !           530:          hBitMap = hEndDead;
        !           531:        }
        !           532:                                        // Calculate coordinates:
        !           533:       X = BITMAP_X;                    // X position is constant.
        !           534:       Y = index * BITMAP_Y;            // Y is based on index in the array.
        !           535: 
        !           536:                                        // Blt the chosen map.
        !           537:       hDCMem = CreateCompatibleDC(hDC);
        !           538:       SelectObject(hDCMem, hBitMap);
        !           539:       SetMapMode(hDCMem, GetMapMode(hDC));
        !           540: 
        !           541:       GetObject(hBitMap, sizeof(BITMAP), &bm);
        !           542: 
        !           543:       ptSize.x = bm.bmWidth;
        !           544:       ptSize.y = bm.bmHeight;
        !           545:       DPtoLP (hDC, &ptSize, 1);
        !           546: 
        !           547:       ptOrg.x = 0;
        !           548:       ptOrg.y = 0;
        !           549:       DPtoLP (hDCMem, &ptOrg, 1);
        !           550: 
        !           551:       BitBlt(hDC, X, Y, ptSize.x, ptSize.y,
        !           552:              hDCMem, ptOrg.x, ptOrg.y, SRCCOPY);
        !           553: 
        !           554: 
        !           555:       X =  NAME_X;                     // Relocate X,Y for clients name.
        !           556:       Y += NAME_Y;
        !           557:                                        // Write name next to spool.
        !           558:       TextOut (hDC, X, Y, clients[index].Name, strlen(clients[index].Name));
        !           559:       DeleteDC(hDCMem);
        !           560:       }
        !           561: 
        !           562: 
        !           563: }

unix.superglobalmegacorp.com

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