QOSCHAT.C
/*++ 
 
Copyright (c) 1995 Intel Corp 
 
Module Name: 
 
    qoschat.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 <winsock2.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include "qoschat.h" 
#include "chatsock.h" 
#include "chatdlg.h" 
#include "queue.h" 
 
 
// 
// Globals 
// 
DWORD   ThisAddr=0; 
DWORD   OtherAddr=0; 
DWORD   NumSockets=1; 
DWORD   LocalPort=9009; 
BOOL    UseAsyncSelect = FALSE; 
 
// 
// 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[] = "QOSChatFrame"; // 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; 
    } 
 
    ParseCmdLine( CmdLine ); 
 
    // 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() 
 
 
  
VOID 
ParseCmdLine( 
    LPSTR   CmdLine 
    ) 
/*++ 
 
Routine Description: 
 
    Read the Cmdline and find out what options have been passed into this routine. 
 
Arguments: 
 
    CmdLine - ptr to the command line 
 
Return Value: 
 
    none 
 
--*/ 
{ 
 
    UCHAR   Name[100]; 
 
    while (*CmdLine) 
    { 
        if ((*CmdLine == '-') || (*CmdLine == '/') ){ 
 
            char    c, *p = (++CmdLine); // skip the "-" 
 
            if (*p == '\0') 
                exit(usage()); 
 
            c = *p; 
 
            switch (c) { 
 
 
            case 'a': 
 
                // 
                // Use WSAAsyncSelect rather than WSAEventSelect 
                // 
                UseAsyncSelect = TRUE; 
                break; 
 
            case 'c': 
 
                // 
                // Count of sockets and events 
                // 
                if (*++CmdLine && *++CmdLine) 
                { 
                    p = CmdLine; 
 
                    NumSockets = atol( p ); 
 
                } 
                break; 
 
            case 'd': 
 
                // 
                // other machine 
                // 
                if (*++CmdLine && *++CmdLine) 
                { 
                    p = CmdLine; 
 
                    if (strlen(p) < sizeof(Name)){ 
                        strcpy(Name,p); 
                    }else{ 
                        exit(usage()); 
                    } 
 
                    OtherAddr = inet_addr(Name); 
                    if (OtherAddr == INADDR_NONE) { 
                        struct hostent *host; 
                        host = gethostbyname(Name); 
                        if (host) { 
                            OtherAddr = *((DWORD *)host->h_addr_list[0]); 
                        } 
                    } 
 
                } 
                break; 
 
            case 'l': 
 
                // 
                // local address 
                // 
                if (*++CmdLine && *++CmdLine) 
                { 
                    p = CmdLine; 
 
                    if (strlen(p) < sizeof(Name)){ 
                        strcpy(Name,p); 
                    }else{ 
                        exit(usage()); 
                    } 
 
                    ThisAddr = inet_addr(Name); 
                } 
                break; 
 
            case 'p': 
                // 
                // Count of sockets and events 
                // 
                if (*++CmdLine && *++CmdLine) 
                { 
                    DWORD   Port; 
                    p = CmdLine; 
 
                    Port = atol( p ); 
                    LocalPort = htons((USHORT)Port); 
 
 
                } 
                break; 
 
            default: 
 
                exit(usage()); 
                break; 
 
            } 
        } else { 
            CmdLine++; 
        } 
    } 
} 
 
  
INT 
usage() 
/*++ 
 
Routine Description: 
 
    Print out the command line args that are possible. 
 
Arguments: 
 
 
Return Value: 
 
 
--*/ 
{ 
    printf("\nUsage:\n"); 
    printf("    qoschat -[d <OtherMachine>] -[s] -[r] \n"); 
    printf("           -c      -specifies the number of sockets to create \n"); 
    printf("           -d      -specifies the destination/source of the session \n"); 
    printf("                    (destination if the sender and source if the reciever\n"); 
    printf("           -l      -specifies the local IP address(optional for multihomed)\n"); 
 
    return(0); 
} 
 
  
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 
    INT                i; 
 
    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: 
 
 
            for (i = 0;i < (INT)NumSockets ;i++ ) { 
                // 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); 
            DestroyWindow( ChildWindow); 
            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 
    DWORD             RsvpStatus; 
    WSANETWORKEVENTS  NetworkEvents; 
    switch (Message) { 
 
    case WM_CREATE: 
 
        StartConnection( WindowHandle ); 
 
        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 USMSG_QOS: 
 
        // Connection has been accepted.  Change the title of the 
        // connection window to reflect who the user has connected to 
        ConnData = GetConnData(WindowHandle); 
 
        RsvpStatus = WSAGETSELECTERROR( LParam ); 
        NetworkEvents.iErrorCode[FD_QOS_BIT] = RsvpStatus; 
        NetworkEvents.lNetworkEvents = WSAGETSELECTEVENT( LParam ); 
 
        // An Event has occured in Winsock, so call 
        // a separate function.  Break out of the thread if 
        // HandleSocketEvent returns FALSE, indicating error. 
        // 
        if (HandleEvents(ConnData,&NetworkEvents) != CHAT_OK) { 
 
            // 
            // close our window if an error occurs 
            // 
            DestroyWindow(WindowHandle); 
 
        } 
        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() 
 
DWORD 
StartConnection( 
    IN HWND   WindowHandle 
    ) 
 
/*++ 
 
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 
 
    // 
    // Allocate memory for private window data. 
    // 
 
    ConnData = (PCONNDATA)malloc(sizeof(CONNDATA)); 
    if (ConnData == NULL) { 
        ChatSysError("malloc()", 
                     "ConnWndProc()", 
                     TRUE); 
    } 
    memset((char *)ConnData, 0, sizeof(CONNDATA)); 
 
    // Zero is a valid socket ID so set the socket member to INVALID_SOCKET 
    ConnData->Socket = INVALID_SOCKET; 
    ConnData->RefCount = 2; 
 
 
    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->ThreadTerminationEvent = 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 ); 
} 
 
 
  
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, Index1;         // indices into the edit control text 
    char *Current = String;     // the character we are examining 
    char *First = String;       // the first character we have not output 
    char Buffer[BUFFER_LENGTH]; // scratch buffer for SendMessage 
    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 last line of the text into 'Buffer'. 
            Index = GetWindowTextLength(RecvWindow); 
            Index1 = SendMessage(RecvWindow, EM_LINEFROMCHAR, Index, 0); 
            *((LPDWORD)Buffer) = BUFFER_LENGTH; 
            NumChars = SendMessage(RecvWindow, EM_GETLINE, Index1, 
                                   (LPARAM)Buffer); 
 
            // 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 
 
    ConnData = GetConnData(ConnectionWindow); 
 
    if (OtherAddr) { 
        struct sockaddr_in *SockAddrInet;                 // Inet socket address 
 
        ConnData->ProtocolInfo = GetProtoFromIndex(0);; 
 
        ConnData->RemoteSockAddr.len = sizeof(struct sockaddr_in); 
        ConnData->RemoteSockAddr.buf = malloc(ConnData->RemoteSockAddr.len); 
        if (ConnData->RemoteSockAddr.buf == NULL) { 
            ChatSysError("malloc()", 
                         "InetConnDlgProc()", 
                         TRUE); 
        } 
 
        SockAddrInet = (struct sockaddr_in *)ConnData->RemoteSockAddr.buf; 
        SockAddrInet->sin_family = AF_INET; 
        SockAddrInet->sin_port = (USHORT)LocalPort; 
        SockAddrInet->sin_addr.S_un.S_addr = OtherAddr; 
 
    } else { 
        // 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; 
 
        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()