WS2CHAT.C

/*++ 

Copyright (c) 1995 Intel Corp

Module Name:

ws2chat.c

Abstract:

Contains WinMain and other user interface code for the WinSock2
Chat sample application.

--*/


#include "nowarn.h" /* turn off benign warnings */
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <windows.h>

#include "nowarn.h" /* some warnings may have been turned back on */
#include <stdlib.h>
#include "ws2chat.h"
#include "chatsock.h"
#include "chatdlg.h"
#include "queue.h"


//
// Forward References -- internal functions.
//

// Startup/Initialization Functions
int PASCAL
WinMain(
IN HINSTANCE InstanceHandle,
IN HINSTANCE PrevInstanceHandle,
IN LPSTR CmdLine,
IN int CmdShow);

BOOL
InitUI(
IN HINSTANCE InstanceHandle,
IN HINSTANCE PrevInstanceHandle);

long CALLBACK
MainWndProc(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam);

long CALLBACK
ConnWndProc(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam);

long CALLBACK
SendEditSubClass(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam);

long CALLBACK
RecvEditSubClass(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam);

BOOL CALLBACK
CloseEnumProc(
IN HWND WindowHandle,
IN LONG LParam);

BOOL
GetConnectionInfo(
IN HWND ConnectionWindow);

void
QueryAndMaximize(void);


//
// Static Global Variables
//

static HMENU MainMenu; // the main MDI menu
static HMENU MainMenuWindow; // the window associated with the above
static FARPROC OldEditWndProc; // edit control WndProc before subclassing



//
// Externally-Visible Global Variables
//

HANDLE GlobalInstance; // ientifies the instance of chat
char ConnClassStr[] = "ConnChild"; // string to register window class
char ChatClassStr[] = "WS2ChatFrame"; // string to register window class
HWND GlobalFrameWindow; // Chat's main (frame) window



//
// Function Definitions
//


int CALLBACK
WinMain(
IN HINSTANCE InstanceHandle,
IN HINSTANCE PrevInstanceHandle,
IN LPSTR CmdLine,
IN int CmdShow)

/*++

Routine Description:

WinMain is the main entry point for WinSock 2 Chat. This function
calls initialization functions and goes into a message loop.

Arguments:

InstanceHandle -- Supplies the current instance handle.

PrevInstanceHandle -- Supplies the previous instance handle.

CmdLine -- Supplies the address of the command line.

CmdShow -- Supplies the show state of the window.

Return Value:

0 -- Initialization failure

!0 -- Returns the Message.wParam of the WM_QUIT message that ended
the message loop.

--*/
{
HANDLE AccelTable; // handle to the accelerator table
HWND ClientWindow; // MDI Client window
MSG Message; // holds the message
int ReturnValue; // return value

ReturnValue = 0;
GlobalInstance = InstanceHandle;

// Initialize Chat.
if (!InitUI(InstanceHandle, PrevInstanceHandle)) {
MessageBox(NULL, "Couldn't Initialize Chat!", "Error",
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
goto Done;
}

// Create the frame window.
GlobalFrameWindow = CreateWindow(ChatClassStr,
"WinSock 2 Chat",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
(HMENU)MainMenu,
InstanceHandle,
NULL);

if (GlobalFrameWindow == NULL) {
ChatSysError("CreateWindow()",
"WinMain()",
TRUE);
}

// Startup WinSock 2.
if (!InitWS2()) {
goto Done;
}

// Find out about protocols installed.
if (!FindProtocols()) {
goto Done;
}

// Listen on a local address for all installed protocols.
if (!ListenAll()) {
goto Done;
}

// Load some handles, update the application window, and enter the
// main message loop.
ClientWindow = GetWindow(GlobalFrameWindow, GW_CHILD);
AccelTable = LoadAccelerators(InstanceHandle, "MdiAccel");
if (AccelTable == NULL) {
ChatSysError("LoadAccelerators()",
"WinMain()",
FALSE);
}
ShowWindow(GlobalFrameWindow, CmdShow);
UpdateWindow(GlobalFrameWindow);

// Enter the GetMessage loop.
while (GetMessage(&Message, NULL, 0, 0)) {
if (!TranslateMDISysAccel(ClientWindow, &Message) &&
!TranslateAccelerator(GlobalFrameWindow, AccelTable, &Message)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
}

// WM_QUIT was received. Cleanup and return.
WSACleanup();
ReturnValue = Message.wParam;

Done:

return(ReturnValue);

} // WinMain()





BOOL
InitUI(
IN HINSTANCE InstanceHandle,
IN HINSTANCE PrevInstanceHandle)

/*++

Routine Description:

Initializes chat's user interface. Registers the window classes
used by chat and obtains handles to chat's menus.

Arguments:

InstanceHandle - Supplies the current instance handle.

PrevInstanceHandle - Supplies the previous instance handle.

Return Value:

TRUE - Chat successfully initialized

FALSE - Failure during chat initialization

--*/
{
WNDCLASS WndClass; // window class structure
BOOL ReturnValue = TRUE; // holds return value

// If this is the first instance of chat, register the window
// classes.
if (!PrevInstanceHandle) {
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = MainWndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = InstanceHandle;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = ChatClassStr;
if (!RegisterClass(&WndClass)) {
ReturnValue = FALSE;
goto Done;
}

WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = ConnWndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = CONN_WND_EXTRA;
WndClass.hInstance = InstanceHandle;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = ConnClassStr;
if (!RegisterClass(&WndClass)) {
ReturnValue = FALSE;
goto Done;
}
}

// Obtain handles to the menus & submenus.
MainMenu = LoadMenu(GlobalInstance, "MdiMenuMain");
if (MainMenu == NULL) {
ChatSysError("LoadMenu()",
"InitUI()",
TRUE);
}
MainMenuWindow = GetSubMenu(MainMenu, MAIN_MENU_POS);

Done:

return(ReturnValue);

} // InitUI()





long CALLBACK
MainWndProc(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam)

/*++

Routine Description:

WinSock 2 chat's main window procedure.

Arguments:

WindowHandle - Supplies the window handle for chat's frame window.

Message - Supplies the message identifier.

WParam - Supplies the first message parameter.

LParam - Supplies the second message parameter.

Return Value:

Return value depends on the message sent.

--*/
{
static HWND ClientWindow; // the MDI client window
CLIENTCREATESTRUCT ClientCreate; // MDI Client window creation data
HWND ChildWindow; // gets handle to child conn. windows
MDICREATESTRUCT MdiCreate; // MDI Child window creation data

switch (Message) {

case WM_CREATE:

// Create the client window.
ClientCreate.hWindowMenu = MainMenuWindow;
ClientCreate.idFirstChild = IDM_FIRSTCHILD;
ClientWindow = CreateWindow("MDICLIENT",
NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
0, 0, 0, 0,
WindowHandle,
(HMENU)CLIENT_WINDOW_ID,
GlobalInstance,
(LPSTR)&ClientCreate);
if (ClientWindow == NULL) {
ChatSysError("CreateWindow()",
"MainWndProc()",
TRUE);

}

return (0);

case WM_COMMAND:

// We know the wm_command is coming from a menu item, so we
// can safely assume that the high-order word of WParam is
// zero.
switch (WParam) {

case IDM_CONNECT:

// Create a Connection child window.
MdiCreate.szClass = ConnClassStr;
MdiCreate.szTitle = "Connecting...";
MdiCreate.hOwner = GlobalInstance;
MdiCreate.x = CW_USEDEFAULT;
MdiCreate.y = CW_USEDEFAULT;
MdiCreate.cx = CW_USEDEFAULT;
MdiCreate.cy = CW_USEDEFAULT;
MdiCreate.style = 0;
MdiCreate.lParam = 0;
ChildWindow =
(HWND) SendMessage(ClientWindow, WM_MDICREATE, 0,
(LPARAM)(LPMDICREATESTRUCT)&MdiCreate);

if (ChildWindow == NULL) {
ChatSysError("SendMessage(WM_MDICREATE)",
"MainWndProc()",
TRUE);
}

// Get the connection information from the caller and
// attempt to make a connection...
if (!GetConnectionInfo(ChildWindow) ||
!MakeConnection(ChildWindow)) {
DestroyWindow(ChildWindow);
}

break;

case IDM_CLOSE:

// Close the active window.
ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE, 0,
0);
SendMessage(ClientWindow, WM_MDIDESTROY, (WPARAM)ChildWindow, 0);
break;


case IDM_EXIT:

// Exit the program.
SendMessage(WindowHandle, WM_CLOSE, 0, 0);
break;

case IDM_TILE:

SendMessage(ClientWindow, WM_MDITILE, 0, 0);
break;

case IDM_CASCADE:

SendMessage(ClientWindow, WM_MDICASCADE, 0, 0);
break;

case IDM_ARRANGE:

SendMessage(ClientWindow, WM_MDIICONARRANGE, 0, 0);
break;

case IDM_CLOSEALL:

// Close all child windows.
EnumChildWindows(ClientWindow, CloseEnumProc, 0);
break;

default:

// Pass to active child.
ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDIGETACTIVE,
0, 0);

if (IsWindow(ChildWindow)) {
SendMessage(ChildWindow, WM_COMMAND, WParam, LParam);
}

// This causes us to drop out of the switch and to pass
// the message on to DefFrameProc().
break;

} // switch (WParam)
break;

case WM_CLOSE:

// Close all child windows.
SendMessage(WindowHandle, WM_COMMAND, IDM_CLOSEALL, 0);
if (GetWindow(ClientWindow, GW_CHILD) != NULL) {
return (0);
}
break;

case USMSG_ACCEPT:

// Create a MDI Child connection window (even though we aren't
// yet sure if we will accept the connection -- if we don't,
// it will be immediately destroyed.)
MdiCreate.szClass = ConnClassStr;
MdiCreate.szTitle = "Connection Request...";
MdiCreate.hOwner = GlobalInstance;
MdiCreate.x = CW_USEDEFAULT;
MdiCreate.y = CW_USEDEFAULT;
MdiCreate.cx = CW_USEDEFAULT;
MdiCreate.cy = CW_USEDEFAULT;
MdiCreate.style = 0;
ChildWindow = (HWND)SendMessage(ClientWindow, WM_MDICREATE, 0,
(LPARAM)(LPMDICREATESTRUCT)&MdiCreate);
if (ChildWindow == NULL) {
ChatSysError("SendMessage(WM_MDICREATE)",
"MainWndProc()",
TRUE);
}

// Service the incoming connection request.
HandleAcceptMessage(ChildWindow, (SOCKET)WParam, LParam);
break;

case WM_DESTROY:

// Clean up the listening sockets and kill the application.
CleanUpSockets();
PostQuitMessage(0);
break;

} // switch(Message)

// Pass unprocessed messages to DefFrameProc.
return (DefFrameProc(WindowHandle,
ClientWindow,
Message,
WParam,
LParam));

} // MainWndProc()





long CALLBACK
ConnWndProc(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam)

/*++

Routine Description:

Windows procedure for each connection window.

Arguments:

WindowHandle - Supplies the handle to the connection window which
is to receive the message.

Message - Supplies the message identifier.

WParam - Supplies the first message parameter.

LParam - Supplies the second message parameter.

Return Value:

Return value depends on the message sent.

--*/
{
PCONNDATA ConnData; // pointer to data for this connection
RECT Rect; // used for painting the window
HDC Hdc; // device context for painting

switch (Message) {

case WM_CREATE:

// Allocate memory for private window data.
ConnData = (PCONNDATA)malloc(sizeof(CONNDATA));
if (ConnData == NULL) {
ChatSysError("malloc()",
"ConnWndProc()",
TRUE);
}
memset((char *)ConnData, 0, sizeof(CONNDATA));
// Let this to be filled by a getsockopt call later
ConnData->MaxMsgSize = 0x01;

// Zero is a valid socket ID so set the socket member to INVALID_SOCKET
ConnData->Socket = INVALID_SOCKET;

GetClientRect(WindowHandle, &Rect);

// Create Send/Recv edit controls.
ConnData->SendWindow =
CreateWindow("EDIT",
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0,
0,
Rect.right,
Rect.bottom/2,
WindowHandle,
(HMENU)EC_SEND_CHILD,
GlobalInstance,
NULL);

ConnData->RecvWindow =
CreateWindow("EDIT",
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0,
Rect.bottom/2 + 1,
Rect.right,
Rect.bottom - Rect.bottom/2 - 1,
WindowHandle,
(HMENU)EC_RECV_CHILD,
GlobalInstance,
NULL);

if (!ConnData->SendWindow || !ConnData->RecvWindow) {
ChatSysError("CreateWindow()",
"ConnWndProc()",
TRUE);
}

// Fill in some connection-specific data.
ConnData->SocketEventObject = CreateEvent(NULL,
FALSE,
FALSE,
NULL);
ConnData->OutputEventObject = CreateEvent(NULL,
FALSE,
FALSE,
NULL);
ConnData->OutputQueue = QCreate();
if (!ConnData->SocketEventObject || !ConnData->OutputEventObject ||
!ConnData->OutputQueue) {
ChatSysError("CreateEvent() or QCreate()",
"ConnWndProc()",
TRUE);
}
ConnData->ConnectionWindow = WindowHandle;
ConnData->WriteOk = FALSE;

// Subclass the edit controls. Both functions necessarily
// return the same FARPROC...
OldEditWndProc = (FARPROC)SetWindowLong(ConnData->SendWindow,
GWL_WNDPROC,
(DWORD)SendEditSubClass);
SetWindowLong(ConnData->RecvWindow,
GWL_WNDPROC,
(DWORD)RecvEditSubClass);

// Split the connection window in half.
Hdc = GetDC(WindowHandle);
MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
LineTo(Hdc, Rect.right, Rect.bottom/2);
ReleaseDC(WindowHandle, Hdc);

// Set the private window data to be a pointer to ConnData.
SetWindowLong(WindowHandle, GWL_CONNINFO, (LONG)ConnData);

return (0);

case WM_COMMAND:

switch (HIWORD(WParam)) {

case 0:

// If the high-order word is zero, this message is being
// sent from a menu-item (hopefully it was passed to us
// from the frame window, since the mdi child connection
// windows, of course, have no menus).
switch (LOWORD(WParam)) {

case IDM_CLEAR_SENDBUFFER:

// Clear the send buffer.
ConnData = GetConnData(WindowHandle);
PostMessage(ConnData->SendWindow, EM_SETSEL, 0, -1);
PostMessage(ConnData->SendWindow, WM_CLEAR, 0, 0);
return (0);

case IDM_CLEAR_RECVBUFFER:

// Clear the receive buffer.
ConnData = GetConnData(WindowHandle);
PostMessage(ConnData->RecvWindow, EM_SETSEL, 0, -1);
PostMessage(ConnData->RecvWindow, WM_CLEAR, 0, 0);
return (0);

default:

// An unknown menu item... just return.
return (0);

} // switch (LOWORD(WParam))

case 1:

// The high-order word is 1, so thus the wm_command
// message is coming from an accelerator. Ignore it.
return(0);

default:

// Let the default window procedure handle it (below).
break;

} // switch (HIWORD(WParam))
break;


case WM_PAINT:

// Window needs to be painted, split the connection window in
// half.
GetClientRect(WindowHandle, &Rect);
Hdc = GetDC(WindowHandle);
MoveToEx(Hdc, 0, Rect.bottom/2, NULL);
LineTo(Hdc, Rect.right, Rect.bottom/2);
ReleaseDC(WindowHandle, Hdc);

return (DefMDIChildProc(WindowHandle,
Message,
WParam,
LParam));

case WM_SIZE:

ConnData = GetConnData(WindowHandle);

// Resize the edit control windows.
MoveWindow(ConnData->SendWindow,
0,
0,
LOWORD(LParam),
HIWORD(LParam)/2,
TRUE);

MoveWindow(ConnData->RecvWindow,
0,
HIWORD(LParam)/2 + 1,
LOWORD(LParam),
HIWORD(LParam) - HIWORD(LParam)/2 - 1,
TRUE);

return (DefMDIChildProc(WindowHandle,
Message,
WParam,
LParam));

case USMSG_CONNECT:

// Handle the new connection.
HandleConnectMessage(WindowHandle, LParam);
return(0);

case WM_DESTROY:

ConnData = GetConnData(WindowHandle);
CleanupConnection(ConnData);

return(DefMDIChildProc(WindowHandle,
Message,
WParam,
LParam));

} // switch (Message)

// Pass unprocessed message to DefMDIChildProc.
return(DefMDIChildProc(WindowHandle,
Message,
WParam,
LParam));

} // ConnWndProc()





long CALLBACK
SendEditSubClass(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam)

/*++

Routine Description:

Window procedure used to subclass the edit control for the sending
window.

Implementation:

Text is only allowed to be appended to or deleted from the end of
an edit control's text buffer. When text is either typed or
paste, Chat creates an output request data structure and sticks it
on the output thread for this connection.

Arguments:

WindowHandle - Supplies the handle to the edit control's window

Message - Supplies the message identifier.

WParam - Supplies the first message parameter.

LParam - Supplies the second message parameter.

Return Value:

ReturnValue - Depends of the message sent.

--*/
{
int Index; // an index into the edit control's text
HANDLE CBDataHandle; // handle to the clipboard data
LPSTR CBDataPtr; // pointer to data to send
PCONNDATA ConnData; // pointer to connection-specific data
POUTPUT_REQUEST OutReq; // output request data
int TextSize; // number of bytes of text to send
DWORD BytesLeft; // number of bytes left to send

ConnData = GetConnData(GetParent(WindowHandle));

switch (Message) {

case WM_CHAR:

// If the character is sendable, enqueue the character on the
// output queue, and signal the output event.
if (IsSendable((char)WParam)) {

OutReq = (POUTPUT_REQUEST) malloc(sizeof(OUTPUT_REQUEST));
if (OutReq == NULL) {
ChatSysError("malloc()",
"SendEditSubClass()",
TRUE);
}

// Rather than malloc a one-character buffer, just point
// Buffer into the already-allocated Char field of the
// OutReq structure. This gets rid of a heavy-weight
// malloc for each character typed.
OutReq->Buffer.len = sizeof(char);
OutReq->Buffer.buf = &OutReq->Character;

// Fill in the OutReq structure.
*(OutReq->Buffer.buf) = (char)WParam;
OutReq->Type = NON_OVERLAPPED_IO;
OutReq->ConnData = ConnData;

QInsert(ConnData->OutputQueue, (LPVOID)OutReq);
SetEvent(ConnData->OutputEventObject);
}

// Move the cursor to the end of the text buffer.
Index = GetWindowTextLength(WindowHandle);
CallWindowProc((WNDPROC)OldEditWndProc,
WindowHandle,
EM_SETSEL,
Index,
Index);

// Make room for the new text in the edit control.
MakeRoom(WindowHandle, 1);

// Pass on the message to the original Edit Window Procedure.
return (CallWindowProc((WNDPROC)OldEditWndProc,
WindowHandle,
Message,
WParam,
LParam));

case WM_PASTE:

// Get the buffer from the edit control and send it over the
// socket.
if (IsClipboardFormatAvailable(CF_TEXT)) {

if (OpenClipboard(WindowHandle)) {
CBDataHandle = GetClipboardData(CF_TEXT);

if (CBDataHandle) {

CBDataPtr = GlobalLock(CBDataHandle);
TextSize = strlen(CBDataPtr);

// Make room in the edit control for the new text; if
// this is impossible, just return because MakeRoom
// notifies the user internally.
if (!MakeRoom(WindowHandle, TextSize)) {
GlobalUnlock(CBDataHandle);
CloseClipboard();
return(0);
}

// If the text is longer than the maximum message
// size, we need to split it into bite-size pieces
// for consumption by the transport layer.
BytesLeft = TextSize;
while (BytesLeft != 0) {

// Create a new output request.
OutReq =
(POUTPUT_REQUEST)malloc(sizeof(OUTPUT_REQUEST));
if (OutReq == NULL) {
ChatSysError("malloc()",
"SendEditSubClass",
TRUE);
}

// Determine how much data to send with this
// request, and allocate a buffer that size.
if (BytesLeft > ConnData->MaxMsgSize) {
OutReq->Buffer.len = ConnData->MaxMsgSize;
} else {
OutReq->Buffer.len = BytesLeft;
}
OutReq->Buffer.buf =
(char *)malloc(OutReq->Buffer.len);
if (OutReq->Buffer.buf == NULL) {
ChatSysError("malloc()",
"SendEditSubClass",
TRUE);
}

// Fill in the rest of the OutReq structure
// and put it in the output queue.
OutReq->Type = OVERLAPPED_IO;
OutReq->ConnData = ConnData;
memcpy(OutReq->Buffer.buf, CBDataPtr,
OutReq->Buffer.len);
QInsert(ConnData->OutputQueue, (LPVOID)OutReq);

// Update the number of bytes left to send and
// the pointer to the rest of the data.
BytesLeft -= OutReq->Buffer.len;
CBDataPtr += OutReq->Buffer.len;
}

// Signal the ouput event.
SetEvent(ConnData->OutputEventObject);
GlobalUnlock(CBDataHandle);

} else {

MessageBox(WindowHandle,
"Sorry, couldn't retrieve the clipoard data.",
"Error.",
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
}

CloseClipboard();

} // if (OpenClipboard(WindowHandle))
else {

MessageBox(WindowHandle,
"Couldn't open the clipboard!!",
"Try again.",
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
}

} // if (IsClipboardFormatAvailable(CF_TEXT))
else {

MessageBox(WindowHandle,
"Sorry, the CF_TEXT format not available.",

"Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND); 
}
// Move the cursor to the end of the text buffer.
Index = GetWindowTextLength(WindowHandle);
CallWindowProc((WNDPROC)OldEditWndProc,
WindowHandle,
EM_SETSEL,
Index,
Index);

// Call the real edit control window procedure.
return (CallWindowProc((WNDPROC)OldEditWndProc,
WindowHandle,
Message,
WParam,
LParam));

case WM_KEYDOWN:

// Capture the delete key. nVirtKey: 46 = 'Del'.
if (WParam == 46) {
return(0);
} else {
return (CallWindowProc((WNDPROC)OldEditWndProc, WindowHandle,
Message, WParam, LParam));
}

case WM_CUT: // Flow through
case WM_UNDO:

return (0);

default:

return (CallWindowProc((WNDPROC)OldEditWndProc,
WindowHandle,
Message,
WParam,
LParam));

} // switch (Message)

} // SendEditSubClass()





long CALLBACK
RecvEditSubClass(
IN HWND WindowHandle,
IN UINT Message,
IN WPARAM WParam,
IN LPARAM LParam)

/*++

Routine Description:

Window procedure used to subclass the edit control for the
receiving window.

Implementation:

The user cannot modify any of the text displayed in the receiving
edit control. All text received from the socket associated with
this edit control is displayed.

Arguments:

WindowHandle - Supplies the handle to the edit control's window

Message - Supplies the message identifier.

WParam - Supplies the first message parameter.

LParam - Supplies the second message parameter.

Return Value:

ReturnValue - Depends of the message sent.

--*/
{
switch (Message) {

case WM_CHAR: // Flow through
case WM_KEYDOWN: // Flow through
case WM_UNDO: // Flow through
case WM_PASTE: // Flow through
case WM_CUT:

return (0); // Effectively disables the above messages.

default:

return (CallWindowProc((WNDPROC)OldEditWndProc,
WindowHandle,
Message,
WParam,
LParam));
}

} // RecvEditSubClass()





BOOL CALLBACK
CloseEnumProc(
IN HWND WindowHandle,
IN LONG LParam)

/*++

Routine Description:

A callback function used to destroy all of chat's connection
windows.

Arguments:

WindowHandle - Supplies the handle to the child's window which
is to be closed.

LParam - Not Used.

Return Value:

TRUE - Returns true in order to continue enumeration.

--*/
{
if (!GetWindow(WindowHandle, GW_OWNER)) {

SendMessage(GetParent(WindowHandle), WM_MDIRESTORE,
(WPARAM)WindowHandle, 0);

if (SendMessage(WindowHandle, WM_QUERYENDSESSION, 0, 0)) {
SendMessage(GetParent(WindowHandle), WM_MDIDESTROY,
(WPARAM)WindowHandle, 0);
}
}
return (TRUE);

} // CloseEnumProc()





void
OutputString(
IN HWND RecvWindow,
IN char *String)
/*++

Routine Description:

This function sends the string received over the socket to the
edit window given by RecvWindow.

Implementation:

This function treats certain non-printable characters specially so
that they mean the same thing on this end as they did on the end
that sent them.

Arguments:

RecvWindow -- Handle to the edit control window which is to
receive the text.

String -- Points to the NULL-terminated string to be output.

Return Value:

None.

--*/
{

int Index; // indices into the edit control text
char *Current = String; // the character we are examining
char *First = String; // the first character we have not output
int NumChars; // how many characters we put in Buffer

// Make room for the string in the edit control.
MakeRoom(RecvWindow, strlen(String));

// This loop goes through the whole string we have been instructed
// to print in the edit control; when it encounters certain
// characters in the string, it sends certain messages to the edit
// control to simulate the right behavior.
while (*Current) {

switch (*Current) {

case '\b':

// The current character is a backspace. Print out the
// string we have so far, and then manually erase a
// character.
*Current = '\0';

// Output the string at 'First'...
Index = GetWindowTextLength(RecvWindow);
SendMessage(RecvWindow, EM_SETSEL, Index, Index);
SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);

// Get the number of chars in the last line.
Index = GetWindowTextLength(RecvWindow);
NumChars = SendMessage(RecvWindow, EM_LINELENGTH, Index, 0);

// If the line had no characters in it, then it's the
// beginning of a new line, and we should erase two
// characters (the preceding \r\n) rather than just one.
if (NumChars == 0) {
SendMessage(RecvWindow, EM_SETSEL, Index - 2, Index);
SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
} else {
SendMessage(RecvWindow, EM_SETSEL, Index - 1, Index);
SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"");
}

First = Current + 1;
break;

case '\r':

// A newline. If the Chat window is minimized, ask the
// user if they want it maximized.
QueryAndMaximize();

// If a remote user typed a newline, we'll receive '\r',
// but if the remote user pasted it in, we'll get '\r\n'.
if (*(Current + 1) != '\n') {

// The newline was typed in...output the string up to
// the newline, then "\r\n".
*Current = '\0';

Index = GetWindowTextLength(RecvWindow);
SendMessage(RecvWindow, EM_SETSEL, Index, Index);
SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);

Index = GetWindowTextLength(RecvWindow);
SendMessage(RecvWindow, EM_SETSEL, Index, Index);
SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)"\r\n");

First = Current + 1;
}

break;

default:

break;

} // switch (*Current)
Current++;

} // while (*Current)

// Ouptut any string leftover in First
Index = GetWindowTextLength(RecvWindow);
SendMessage(RecvWindow, EM_SETSEL, Index, Index);
SendMessage(RecvWindow, EM_REPLACESEL, 0, (LPARAM)First);
}





void
QueryAndMaximize(void)
/*++

Routine Description:

If Chat's main frame window is minimized, this function asks the
user if they want to activate and display Chat, and acts
accordingly.

Arguments:

None.

Return Value:

None.

--*/
{
WINDOWPLACEMENT WinPlace; // holds window placement data

WinPlace.length = sizeof(WINDOWPLACEMENT);

// Get chat's main window's state.
GetWindowPlacement(GlobalFrameWindow, &WinPlace);

// If it's minimized, ask the user if they want to maximize.
if (WinPlace.showCmd == SW_SHOWMINIMIZED) {

if (MessageBox(GlobalFrameWindow,
"There is activity on a Chat connection. Maximize?",
"Chat has data.",
MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND)
== IDYES) {

// Maximize chat's main window.
WinPlace.showCmd = SW_RESTORE;
SetWindowPlacement(GlobalFrameWindow, &WinPlace);
}
}
} // QueryAndMaximize()





BOOL
GetConnectionInfo(
IN HWND ConnectionWindow)

/*++

Routine Description:

Queries the user as to what sort of connection (i.e. what address
family/protocol) she wants to make.

Implementation:

After popping up a listbox, filling it in with all the available
protocol choices, and getting the user's choice, this function
uses it to determine what kind of dialog box to pop up to get the
actual connection parameters, which are stored in the CONNDATA
associated with the given ConnectionWindow.

Arguments:

ConnectionWindow -- Handle to the connection window associated
with this connection.

Return Value:

TRUE - The user closed a dialog box by clicking the 'Ok' button.

FALSE - The user closed a dialog box by clicking the 'Cancel'
button.

--*/

{
PCONNDATA ConnData; // connection-specific data
BOOL ReturnValue = TRUE; // holds the return value
struct sockaddr_atm * pATMSockAddr;

ConnData = GetConnData(ConnectionWindow);

// Pop up a dialog box from which the user chooses the protocol; a
// pointer to a protocol info struct is stored in ConnData.
if (!DialogBoxParam(GlobalInstance,
"ChooseFamilyDlg",
ConnectionWindow,
ChooseFamilyDlgProc,
(LPARAM)ConnData)) {
ReturnValue = FALSE;
goto Done;
}

// The user has selected a protocol; now pop up an address family
// specific dialog box to get the address to which we want to make
// a chat connection, a pointer to which is stored in
// ConnData->SockAddr (see InetConnDlg, for example).
switch (ConnData->ProtocolInfo->iAddressFamily) {

case AF_INET:

ReturnValue = DialogBoxParam(GlobalInstance,
"InetConnDlg",
ConnectionWindow,
InetConnDlgProc,
(LPARAM)ConnData);
goto Done;

case AF_ATM:
ConnData->RemoteSockAddr.len = sizeof(struct sockaddr_atm);
ConnData->RemoteSockAddr.buf =
(char FAR *)malloc(ConnData->RemoteSockAddr.len);

pATMSockAddr = (struct sockaddr_atm *)ConnData->RemoteSockAddr.buf;
pATMSockAddr->satm_number.AddressType = ATM_NSAP;
pATMSockAddr->satm_blli.Layer2Protocol = SAP_FIELD_ABSENT;
pATMSockAddr->satm_blli.Layer3Protocol = SAP_FIELD_ABSENT;
pATMSockAddr->satm_bhli.HighLayerInfoType = BHLI_UserSpecific;

ReturnValue = DialogBoxParam(GlobalInstance,
"ATMSOCKADDRDLG",
ConnectionWindow,
ATMSockAddrProc,
(LPARAM)pATMSockAddr);
goto Done;

default:

ReturnValue = DialogBoxParam(GlobalInstance,
"DefaultConnDlg",
ConnectionWindow,
DefaultConnDlgProc,
(LPARAM)ConnData);
goto Done;
}

Done:

return(ReturnValue);

} // GetConnectionInfo()





BOOL
TranslateHex(
OUT LPVOID Buffer,
IN int BufferLen,
IN char *HexString,
IN HWND WindowHandle)
/*++

Routine Description:

This function converts an arbitrarily long string of hexidecimal
digits into a binary representation.

Example -- if the string "0fbf" is passed in, then two bytes will
be written into Buffer -- 15 and 191.

Arguments:

Buffer -- Points to a buffer where we want the data stored.

BufferLen -- The length, in bytes, of Buffer.

HexString -- A null-terminated string of digits with values in the
ASCII range of 0-9 and a-f.

WindowHandle -- Window handle needed in case we pop up a dialog
box.

Return Value:

TRUE -- HexString was successfully translated.

FALSE -- One of the characters does not represent a hex digit, or
the HexString is too long to be translated into BufferLen bytes.

--*/
{

int HexStringLen; // the length of the HexString, in bytes
int i; // counting variable
char TwoCharString[3]; // stores two hex characters + a NULL char
char *NextByte; // next free byte in the buffer
BOOL ReturnValue = TRUE; // return value

HexStringLen = strlen(HexString);
NextByte = (char *)Buffer;
TwoCharString[2] = '\0';

// Zero out the output buffer.
memset(NextByte, 0, BufferLen);

// If the hex string in more than twice as long as the buffer, we
// don't have a big enough buffer.
if ((HexStringLen / 2) > BufferLen) {
ReturnValue = FALSE;
} else {

// Go through the hex string two characters at a time...
for (i = 0; i < HexStringLen; i += 2) {

// Copy the next two bytes of the hex string into
// TwoCharString; then check to make sure they are both
// hexidecimal digits.
memcpy(TwoCharString, HexString + i, 2);
if (!isxdigit(TwoCharString[0]) || !isxdigit(TwoCharString[1])) {
MessageBox(WindowHandle, "Type a hexidecimal number, please",
"Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
ReturnValue = FALSE;
break;
}
*NextByte++ = (char)strtol(TwoCharString, NULL, 16);

} // for
} // else
return(ReturnValue);

} // TranslateHex()





BOOL
PackTwoStrings(
OUT char *Buffer,
IN int BufferLen,
IN char *String1,
IN char *String2)
/*++

Routine Description:

Packs two strings into one buffer. Useful for packing caller data
into a buffer for connection-time data transfer. It is not an
error if either string is of zero length, i.e. just the '\0'
character.

Arguments:

Buffer -- Pointer to a buffer into which the two strings will be
packed.

BufferLen -- The length, in bytes, of Buffer.

String1 - Address of the first NULL terminated string.

String2 - Address of the second NULL terminated string.

Return Value:

TRUE -- The strings were successfully packed into the buffer.

FALSE -- The passed in buffer was NULL, one or both of the strings
were NULL, or Buffer is too small.

--*/

{
int Length1, Length2; // holds the length of the strings
BOOL ReturnValue = TRUE; // holds the return value

if (!Buffer || !String1 || !String2) {
ReturnValue = FALSE;
goto Done;
}

Length1 = strlen(String1);
Length2 = strlen(String2);

if (BufferLen < (Length1 + Length2 + 2)) {
ReturnValue = FALSE;
goto Done;
}

strcpy(Buffer, String1);
strcpy(Buffer + Length1 + 1, String2);

Done:

return(ReturnValue);

} // PackTwoStrings()





BOOL
ExtractTwoStrings(
IN char *Buffer,
OUT char *String1,
IN char Length1,
OUT char *String2,
IN int Length2)

/*++

Routine Description:

Extracts two strings from a buffer, expecting them to be packed as
PackTwoStrings does it.

Arguments:

Buffer -- The buffer which contains two strings.

String1 -- A pointer to a buffer of size Length1.

Length1 -- The length, in bytes, of String1.

String2 -- A pointer to a buffer of size Length2.

Length2 -- The length, in bytes, of String2.

Return Value:

TRUE -- The strings were successfully extracted.

FALSE -- One of the pointers was NULL, or a packed string was too
big to be extracted out into a buffer.

--*/

{
int BufStrLen1, BufStrLen2; // the length of the packed strings
char *BufStr2; // points to the second packed string
BOOL ReturnValue = TRUE; // holds the return value

if (!Buffer || !String1 || !String2) {
ReturnValue = FALSE;
goto Done;
}

BufStrLen1 = strlen(Buffer);
BufStr2 = Buffer + BufStrLen1 + 1;
BufStrLen2 = strlen(BufStr2);

if ((BufStrLen1 > Length1) || (BufStrLen2 > Length2)) {
ReturnValue = FALSE;
goto Done;
}

strcpy(String1, Buffer);
strcpy(String2, BufStr2);

Done:

return(ReturnValue);

} // ExtractTwoStrings()





void
ChatSysError(
IN char *FailedFunction,
IN char *InFunction,
IN BOOL Fatal)
/*++

Routine Description:

Pops up a standard message box to display fatal system errors; if
the error is fatal, the process is ended and this function does
not return.

Arguments:

FailedFunction -- Pointer to a string that contains the name of
the system function that failed.

InFunction -- Pointer to a string that contains the name of the
chat function in which the failure occurred.

Fatal -- Boolean indicating whether the error is fatal to the
process or not.

Return Value:

None.

--*/
{

char MsgText[MSG_LEN]; // holds message strings

wsprintf(MsgText,
"System call %s failed in chat function %s. Error code: %d",
FailedFunction, InFunction, GetLastError());
MessageBox(GlobalFrameWindow,
MsgText,
Fatal ? "Fatal Error." : "Non-Fatal Error.",
MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);

if (Fatal) {
ExitProcess(CHAT_ERROR);
}

} // ChatSysError()





BOOL
MakeRoom(
IN HWND EditControl,
IN int HowMuch)
/*++

Routine Description:

This function makes room in an edit control for more text, if
necessary.

Arguments:

EditControl -- The edit control in question.

HowMuch -- How many characters do we want to put into the edit
control?

Return Value:

TRUE -- If there was not enough room in the edit control, MakeRoom
successfully chopped off some text from the beginning, and there
is now enough room.

FALSE -- The requested amount can not fit in the edit control,
period; a message box has informed the user.

--*/
{

int TextLength; // how much text is in the edit control
int CharIndex1;
int CharIndex2;
int LineIndex; // index variables
BOOL ReturnValue; // return value

if (HowMuch > MAX_EC_TEXT) {

MessageBox(EditControl,
"Clipboard too long. Try pasting a smaller amount.",
"Error.", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
ReturnValue = FALSE;

} else {

TextLength = GetWindowTextLength(EditControl);

if (TextLength + HowMuch > MAX_EC_TEXT) {

// Inserting the text would exceed our limit. This code
// chops off roughly the first half of the edit control's
// text.
CharIndex1 = TextLength / 2;
LineIndex = SendMessage(EditControl, EM_LINEFROMCHAR,
(WPARAM)CharIndex1, 0);
CharIndex2 = SendMessage(EditControl, EM_LINEINDEX,
(WPARAM)LineIndex, 0);

SendMessage(EditControl, EM_SETSEL, 0, CharIndex2);
SendMessage(EditControl, EM_REPLACESEL, 0,
(LPARAM)"");
}

ReturnValue = TRUE;
} // else

return(ReturnValue);
} // MakeRoom()