/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/
/*************************************************************************\
* PROGRAM: server32.c
*
* PURPOSE:
*
* To demonstrate the use of named pipes and the overlapped structure.
* This code serves as the server side of the named pipe instances.
* For more details on an overview of this codes designs or use, see
* the README file. For details on the implementation, see the comments
* in this code.
*
*
\*************************************************************************/
#define STRICT
#include <windows.h>
#include "server32.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// clients[] is a global array of
// structures used to keep track
// of the multiple instances of
// the server side of the named
// pipe. As a client connects
// to a given instance, a new
// server thread is created and
// added to the array.
WRTHANDLE clients[MAX_PIPE_INSTANCES];
DWORD clientCount = 0; // Global count of connected clients.
HWND hWnd;
HANDLE hInst;
CHAR lpBuffer[255];
/*************************************************************************\
*
* FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
* PURPOSE: calls initialization function, processes message loop
*
* COMMENTS:
*
\*************************************************************************/
int APIENTRY WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
WNDCLASS wc;
UNREFERENCED_PARAMETER( lpCmdLine );
UNREFERENCED_PARAMETER( hPrevInstance );
//
// Detect platform and exit gracefully if not Windows NT.
//
{
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
GetVersionEx (&osvi);
if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
LoadString(hInst, IDS_WRONGOS, lpBuffer, sizeof(lpBuffer));
MessageBox (NULL, lpBuffer, "SERVER32", MB_OK | MB_ICONSTOP);
return 0;
}
}
hInst = hInstance;
wc.style = 0;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, "npserver");
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = "PipeMenu";
wc.lpszClassName = "PipeWClass";
RegisterClass(&wc);
LoadString(hInst, IDS_WINDOWTITLE, lpBuffer, sizeof(lpBuffer));
hWnd = CreateWindow ("PipeWClass",
lpBuffer,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow (hWnd, nCmdShow);
while (GetMessage (&msg, NULL, 0, 0))
DispatchMessage (&msg);
return (msg.wParam);
}
/*************************************************************************\
*
* FUNCTION: MainWndProc (HWND, UINT, WPARAM, LPARAM)
*
* PURPOSE: To process messages. To launch client and server threads
*
\*************************************************************************/
LONG CALLBACK MainWndProc (HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
LONG lpServerThreadID;
PAINTSTRUCT paintStruct;
HDC hDC;
switch (message)
{
case WM_PAINT:
// DrawBranch is used to paint the spools and text to the window.
hDC = BeginPaint (hwnd, &paintStruct);
DrawBranch (hDC);
EndPaint (hwnd, &paintStruct);
return(0);
case WM_CREATE :
// Create the first instance of a server side of the pipe.
CreateThread ((LPSECURITY_ATTRIBUTES)NULL, // No security.
(DWORD)0, // Same stack size.
(LPTHREAD_START_ROUTINE)ServerProc,// Thread procedure.
(LPVOID)&hwnd, // Parameter.
(DWORD)0, // Start immediatly.
(LPDWORD)&lpServerThreadID); // Thread ID.
return (0);
case WM_DESTROY :
PostQuitMessage (0);
return (0);
}
return DefWindowProc (hwnd, message, wParam, lParam);
}
/*************************************************************************\
*
* PROCEDURE: ServerProc (HWND *hWnd)
*
* PURPOSE:
*
* A thread procedure, which creates an instance of the server side of
* the named pipe, and then blocks waiting for a client to connect.
* Once the client connects, a global array is updated with the specific
* clients information, and this procedure is called again
* to launch another waiting server thread. After launching the new
* thread, this thread begins to loop, reading the named pipe. When
* a message comes from it's client, it uses TellAll() to broadcast
* the message to the other clients in the array.
*
* CALLED BY:
*
* ServerProc();
* WinMain();
*
* CALLS TO:
*
* TellAll();
* ServerProc().
*
* COMMENTS:
*
* Clients is a global array which hold information on each client
* connected to the named pipe. This procedure recieves a buffer.
* It then steps through this global array, and for each client it
* writes the buffer.
*
\*************************************************************************/
VOID ServerProc(HWND *hWnd)
{
HANDLE hPipe; // Pipe handle.
CHAR inBuf[IN_BUF_SIZE] = ""; // Input buffer for pipe.
DWORD ServerThreadID; // Used for CreateThread().
CHAR errorBuf[LINE_LEN] = ""; // Used for error messages.
DWORD bytesRead; // Used in ReadFile().
DWORD retCode; // Used to trap return codes.
DWORD clientIndex; // Index into global array, for this
// instances client.
DWORD lastError; // Traps returns from GetLastError().
BOOL ExitLoop = FALSE; // Boolean Flag to exit loop.
OVERLAPPED OverLapWrt; // Overlapped structure for writing.
HANDLE hEventWrt; // Event handle for overlapped write.
OVERLAPPED OverLapRd; // Overlapped structure for reading.
HANDLE hEventRd; // Event handle for overlapped reads.
DWORD bytesTransRd; // Bytes transferred by overlapped.
PSECURITY_DESCRIPTOR pSD;
SECURITY_ATTRIBUTES sa;
// create a security NULL security
// descriptor, one that allows anyone
// to write to the pipe... WARNING
// entering NULL as the last attribute
// of the CreateNamedPipe() will
// indicate that you wish all
// clients connecting to it to have
// all of the same security attributes
// as the user that started the
// pipe server.
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pSD == NULL)
{
MessageBox (*hWnd, "Error in LocalAlloc for pSD",
"Debug: ServerProc()", MB_OK);
return;
}
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
wsprintf (errorBuf, "Error: InitializeSecurityDescriptor() %d",
GetLastError());
MessageBox (*hWnd, errorBuf, "Debug: ServerProc()", MB_OK);
LocalFree((HLOCAL)pSD);
return;
}
// add a NULL disc. ACL to the
// security descriptor.
if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE))
{
wsprintf (errorBuf, "Error: SetSecurityDescriptorDacl() %d",
GetLastError());
MessageBox (*hWnd, errorBuf, "Debug: ServerProc()", MB_OK);
LocalFree((HLOCAL)pSD);
return;
}
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = TRUE;
// Create a local named pipe with
// the name '\\.\PIPE\test'. The
// '.' signifies local pipe.
hPipe = CreateNamedPipe ("\\\\.\\PIPE\\test", // Pipe name = 'test'.
PIPE_ACCESS_DUPLEX // 2 way pipe.
| FILE_FLAG_OVERLAPPED, // Use overlapped structure.
PIPE_WAIT // Wait on messages.
| PIPE_READMODE_MESSAGE // Specify message mode pipe.
| PIPE_TYPE_MESSAGE,
MAX_PIPE_INSTANCES, // Maximum instance limit.
OUT_BUF_SIZE, // Buffer sizes.
IN_BUF_SIZE,
TIME_OUT, // Specify time out.
&sa); // Security attributes.
// Check Errors.
if ((DWORD)hPipe == 0xFFFFFFFF)
{
retCode = GetLastError(); // Report any error, it should always succeed.
LoadString(hInst, IDS_ERRORCODE, lpBuffer, sizeof(lpBuffer));
wsprintf (errorBuf, lpBuffer, retCode);
LoadString(hInst, IDS_DEBUGTITLE, lpBuffer, sizeof(lpBuffer));
MessageBox (*hWnd, errorBuf, lpBuffer,
MB_ICONINFORMATION | MB_OK | MB_APPLMODAL);
};
// Block until a client connects.
ConnectNamedPipe(hPipe, NULL);
// Create and init overlap for writing.
hEventWrt = CreateEvent (NULL, TRUE, FALSE, NULL);
memset (&OverLapWrt, 0, sizeof(OVERLAPPED));
OverLapWrt.hEvent = hEventWrt;
// Set the clientIndex, then increment
// the count. Fill in the structure
// for this client in the array.
clientIndex = clientCount++;
clients[clientIndex].hPipe = hPipe;
clients[clientIndex].live = TRUE;
clients[clientIndex].overLap = OverLapWrt;
clients[clientIndex].hEvent = hEventWrt;
// Create and init overlap for reading.
hEventRd = CreateEvent(NULL,TRUE,FALSE,NULL);
memset (&OverLapRd, 0, sizeof(OVERLAPPED));
OverLapRd.hEvent = hEventRd;
// Read from the client, the first
// first message should always be
// the clients user name.
retCode = ReadFile (hPipe, inBuf, PLEASE_READ, &bytesRead, &OverLapRd);
if (!retCode)
lastError = GetLastError();
if (lastError == ERROR_IO_PENDING) // Wait on read if need be.
WaitForSingleObject (hEventRd, (DWORD)-1);
// Put client's name in the array.
strcpy (clients[clientIndex].Name, inBuf);
// Create a thread which will make
// another server instance of the
// named pipe.
CreateThread ((LPSECURITY_ATTRIBUTES)NULL, // No security attributes.
(DWORD)0, // Use same stack size.
(LPTHREAD_START_ROUTINE)ServerProc, // Thread procedure.
(LPVOID)hWnd, // Parameter to pass.
(DWORD)0, // Run immediately.
(LPDWORD)&ServerThreadID); // Thread identifier.
TellAll(""); // Forces a paint, draws a red spool
// and name for this client.
// Do loop which basically reads from
// this specific client, and then
// uses TellAll() to broadcast the
// message to all the connected
// clients.
do{
// Read the pipe.
retCode = ReadFile (hPipe, inBuf, PLEASE_READ, &bytesRead, &OverLapRd);
// Check for three kinds of errors:
// If Error = IO_PENDING, wait til
// the event handle signals success,
// If BROKEN_PIPE, exit the do loop.
// Any other error, flag it to the
// user and exit the do loop.
if (!retCode)
{
lastError = GetLastError();
switch (lastError)
{
// IO_PENDING, wait on the event.
case ERROR_IO_PENDING:
WaitForSingleObject (hEventRd, (DWORD)-1);
break;
// Pipe is broken, exit the loop.
case ERROR_BROKEN_PIPE:
ExitLoop = TRUE;
break;
// Something else is wrong, exit the
// the loop after telling the user.
default:
LoadString(hInst, IDS_READERROR, lpBuffer, sizeof(lpBuffer));
wsprintf (errorBuf, lpBuffer, lastError);
LoadString(hInst, IDS_DEBUGINFO, lpBuffer, sizeof(lpBuffer));
MessageBox (*hWnd, errorBuf, lpBuffer, MB_OK);
ExitLoop = TRUE;
break;
}
}
if (!ExitLoop)
{
GetOverlappedResult (hPipe, &OverLapRd, &bytesTransRd, FALSE);
// Use TellAll to broadcast the message.
if (bytesTransRd)
TellAll(inBuf);
else
TellAll("");
}
}while(!ExitLoop);
clients[clientIndex].live = FALSE; // Turns spool gray.
CloseHandle (hPipe); // Close handles.
CloseHandle (hEventRd);
CloseHandle (hEventWrt);
DisconnectNamedPipe (hPipe); // Close pipe instance.
ExitThread(0); // Clean up and die.
}
/*************************************************************************\
*
* PROCEDURE: TellAll (CHAR *buffer)
*
* PURPOSE:
*
* To write the buffer (input parameter) to all of the clients listed
* in the global array "clients".
*
* CALLED BY:
*
* ServerProc();
*
* COMMENTS:
*
* Clients is a global array which hold information on each client
* connected to the named pipe. This procedure recieves a buffer.
* It then steps through this global array, and for each client it
* writes the buffer.
*
\*************************************************************************/
VOID TellAll( CHAR *buffer )
{
DWORD i; // Index through array.
DWORD bytesWritten; // Used in WriteFile().
DWORD retCode; // Traps return codes.
CHAR Buf[LINE_LEN]; // Message Buffer.
DWORD lastError; // Traps returns from GetLastError().
for(i=0; i < clientCount; i++) // For all clients in the array.
{
// If client isn't alive, don't waste
// time writing to it.
if (clients[i].live)
{
retCode = WriteFile (clients[i].hPipe, buffer, strlen(buffer),
&bytesWritten, &clients[i].overLap);
// Check 3 kinds of errors: IO_PENDING,
// NO_DATA, or other. Wait on event
// handle if IO_PENDING, else, if it's
// anything other than NO_DATA (pipe
// client disconnected), flag the user.
// In any case, if it's not IO_PENDING,
// clients[i].live = FALSE, spool turns
// gray.
if (!retCode)
{
lastError = GetLastError();
// IO_PENDING, wait on event handle.
if (lastError == ERROR_IO_PENDING)
{
WaitForSingleObject (clients[i].hEvent, (DWORD)-1);
}
else
{
// If not NO_DATA, flag user.
if (lastError != ERROR_NO_DATA)
{
LoadString(hInst, IDS_DEBUGLAST, lpBuffer, sizeof(lpBuffer));
wsprintf (Buf, "%s = %d", buffer, GetLastError());
MessageBox(hWnd, Buf, lpBuffer, MB_OK);
}
clients[i].live = FALSE;
}
}
} //if client.live
} // for loop
// Paint window with new information.
InvalidateRect(hWnd, NULL, TRUE);
}
/*************************************************************************\
*
* PROCEDURE: DrawBranch (HDC hDC)
*
* PURPOSE:
*
* To draw one of four bitmaps for each client, depending upon the clients
* status (alive = red spool, dead or disconnected = gray), and location in
* the array. It also draws the clients user name beside the spool.
* This procedure is executed when the WM_PAINT message is trapped.
*
* CALLED BY:
*
* WinMain();
*
* COMMENTS:
*
\*************************************************************************/
VOID DrawBranch(HDC hDC)
{
// Spool bitmaps.
HBITMAP hEndLive, hEndDead, hMidLive, hMidDead, hBitMap;
HDC hDCMem;
int X, Y;
BITMAP bm;
POINT ptSize, ptOrg;
DWORD index;
// Load bitmaps: two red (live),
// two dead (gray). End = end
// of tree (last client to connect),
// mid means in the middle somewhere.
hEndLive = LoadBitmap (hInst, "EndLive");
hEndDead = LoadBitmap (hInst, "EndDead");
hMidLive = LoadBitmap (hInst, "MidLive");
hMidDead = LoadBitmap (hInst, "MidDead");
// For each client, determine if
// if alive or not, and position;
// then blt appropriate map and
// clients name.
for (index = 0; index < clientCount; index++)
{
if (index < clientCount - 1) // ClientCount - 1 = last (end) client.
{
if(clients[index].live) // If live = red, else = gray.
hBitMap = hMidLive;
else
hBitMap = hMidDead;
}
else
{
if(clients[index].live) // If live = red, else = gray.
hBitMap = hEndLive;
else
hBitMap = hEndDead;
}
// Calculate coordinates:
X = BITMAP_X; // X position is constant.
Y = index * BITMAP_Y; // Y is based on index in the array.
// Blt the chosen map.
hDCMem = CreateCompatibleDC(hDC);
SelectObject(hDCMem, hBitMap);
SetMapMode(hDCMem, GetMapMode(hDC));
GetObject(hBitMap, sizeof(BITMAP), &bm);
ptSize.x = bm.bmWidth;
ptSize.y = bm.bmHeight;
DPtoLP (hDC, &ptSize, 1);
ptOrg.x = 0;
ptOrg.y = 0;
DPtoLP (hDCMem, &ptOrg, 1);
BitBlt(hDC, X, Y, ptSize.x, ptSize.y,
hDCMem, ptOrg.x, ptOrg.y, SRCCOPY);
X = NAME_X; // Relocate X,Y for clients name.
Y += NAME_Y;
// Write name next to spool.
TextOut (hDC, X, Y, clients[index].Name, strlen(clients[index].Name));
DeleteDC(hDCMem);
}
}