// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1993 - 1998 Microsoft Corporation. All Rights Reserved.
//
// MODULE: connect.c
//
// PURPOSE: Displays the "Connect" dialog box
//
// FUNCTIONS:
// CmdConnect - Displays the "Connect" dialog box
// Connect - Processes messages for "Connect" dialog box.
// MsgConnectInit - Initializes edit controls
// MsgConnectReadyForWrite - Sends name registration message when connection
// established with server
// MsgConnectCommand - Process WM_COMMAND message sent to the connect box.
// CmdConnectDone - Free the connect box and related data.
// CmdConnectNow - Establishes connection to server.
// CmdConnectEnableOK - Enables/Disables OK button when input data is
// valid/invalid
//
// COMMENTS:
//
//
#include <windows.h> // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h> // IPX sockets
#include <wsnetbs.h> // NetBIOS sockets
#include <stdlib.h>
#include <stdio.h>
#include <nspapi.h>
#include <svcguid.h>
#include "globals.h" // prototypes specific to this application
#define WSN_READYFORWRITE 700 // dlg window message indicating the connection is ready to send on
// Function Definitions
LRESULT CALLBACK Connect(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectInit(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectReadyForWrite(HWND, UINT, WPARAM, LPARAM);
LRESULT MsgConnectCommand(HWND, UINT, WPARAM, LPARAM);
LRESULT CmdConnectNow(HWND, WORD, WORD, HWND);
LRESULT CmdConnectDone(HWND, WORD, WORD, HWND);
LRESULT CmdConnectEnableOK(HWND, WORD, WORD, HWND);
// Connect dialog message table definition.
MSD rgmsdConnect[] =
{
{WM_COMMAND, MsgConnectCommand},
{WM_INITDIALOG, MsgConnectInit},
{WSN_READYFORWRITE, MsgConnectReadyForWrite}
};
MSDI msdiConnect =
{
sizeof(rgmsdConnect) / sizeof(MSD),
rgmsdConnect,
edwpNone
};
// Connect dialog command table definition.
CMD rgcmdConnect[] =
{
{IDOK, CmdConnectNow},
{IDCANCEL, CmdConnectDone},
{CD_NAME, CmdConnectEnableOK},
{CD_PROTOCOL, CmdConnectEnableOK},
{CD_SERVER, CmdConnectEnableOK}
};
CMDI cmdiConnect =
{
sizeof(rgcmdConnect) / sizeof(CMD),
rgcmdConnect,
edwpNone
};
// Module specific "globals" Used when a variable needs to be
// accessed in more than one handler function.
HFONT hfontDlg;
//
// FUNCTION: CmdConnect(HWND, WORD, WORD, HWND)
//
// PURPOSE: Displays the "Connect" dialog box
//
// PARAMETERS:
// hwnd - Window handle
// wCommand - IDM_CONNECT (unused)
// wNotify - Notification number (unused)
// hwndCtrl - NULL (unused)
//
// RETURN VALUE:
//
// Always returns 0 - Message handled
//
// COMMENTS:
// To process the IDM_CONNECT message, call DialogBox() to display the
// Connect dialog box.
LRESULT CmdConnect(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
// Start dialog box
if(DialogBox(hInst, "ConnectBox", hwnd, (DLGPROC)Connect))
{
// We have a connection and have registered our name. Set up Select dialog.
PostMessage(hwnd, WM_COMMAND, MAKELONG(IDM_SELECT,0), 0);
return 0;
}
// Connection failed - reset window title
SetWindowText(hwnd, szTitle);
return 0;
}
//
// FUNCTION: Connect(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for "Connect" dialog box.
//
// PARAMETERS:
// hdlg - window handle of the dialog box
// wMessage - type of message
// wparam - message-specific information
// lparam - message-specific information
//
// RETURN VALUE:
// TRUE - message handled
// FALSE - message not handled
//
// COMMENTS:
//
// Gets connection information from user and then establishes a connection.
//
// Connect when user clicks on the OK button. Kill Dialog when connection
// established.
//
LRESULT CALLBACK Connect(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
return DispMessage(&msdiConnect, hdlg, uMessage, wparam, lparam);
}
//
// FUNCTION: MsgConnectInit(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: To center dialog, limit size of edit controls, and set up available protocols
// to connect with
//
// PARAMETERS:
// hdlg - The window handling the message.
// uMessage - The message number. (unused).
// wparam - Message specific data (unused).
// lparam - Message specific data (unused).
//
// RETURN VALUE:
// Always returns 0 - message handled.
//
// COMMENTS:
// Set size of edit controls for the following
// Network 8 chars (4 2-digit hex numbers)
// Node 12 chars (6 2-digit hex numbers)
// Socket 4 chars (2 2-digit hex numbers)
//
LRESULT MsgConnectInit(HWND hdlg, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
int numStrucs;
int j, k;
int iProtBufSize = 0;
char outtext[80];
// Create a font to use
hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
VARIABLE_PITCH | FF_SWISS, "");
// Center the dialog over the application window
CenterWindow (hdlg, GetWindow (hdlg, GW_OWNER));
// Call EnumProtocols with NULL buffer in order to determine size of buffer required
EnumProtocols(NULL, NULL, &iProtBufSize);
if (((LPVOID)lpProtBuf = VirtualAlloc(NULL,
iProtBufSize,
MEM_COMMIT,
PAGE_READWRITE)) == NULL)
{
// ERROR -- abort
return 0;
}
// Calling EnumProtocols with large enough buffer
if ((numStrucs = EnumProtocols(NULL, lpProtBuf, &iProtBufSize)) == SOCKET_ERROR)
{
// Error -- abort
return 0;
}
// Add every connection oriented protocol to protocol combo box
for (j = 0, k = 0; j < numStrucs; j++)
{
if ((lpProtBuf[j].dwServiceFlags & XP_CONNECTIONLESS) == 0)
{
wsprintf(outtext, "%2d: %s", k, lpProtBuf[j].lpProtocol);
SendMessage(GetDlgItem(hdlg, CD_PROTOCOL), CB_ADDSTRING, 0, (LPARAM)outtext);
goodprots[k++] = j; // Keep track of good protocols
}
}
// Limit size of edit controls
SendDlgItemMessage(hdlg, CD_NAME, EM_LIMITTEXT, 15, 0);
// Initialize Edit Controls
SetDlgItemText(hdlg, CD_NAME, szConnectName);
return (TRUE);
}
//
// FUNCTION: MsgConnectReadyForWrite(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Receive notification of connected socket and register name
//
// PARAMETERS:
// hwnd - The window handing the message.
// uMessage - The message number. (unused).
// wparam - Message specific data (unused).
// lparam - Message specific data (unused).
//
// RETURN VALUE:
// Always returns 0 - message handled.
//
// COMMENTS:
// Uses this DispCommand function defined in wndproc.c combined
// with the cmdiConnect structure defined in this file to handle
// the command messages for the Connect dialog box.
//
LRESULT MsgConnectReadyForWrite(HWND hwnd,
UINT uMessage,
WPARAM wparam,
LPARAM lparam)
{
char outtext[80];
// Build our name
GetDlgItemText(hwnd, CD_NAME, MySock.name, sizeof(MySock.name));
// Build name registration packet
xferbuf.hdr.signature = MYSIGNATURE; // signature
xferbuf.hdr.length = REALLEN(MySock.name) + HDRSIZE;
lstrcpy(xferbuf.data, MySock.name); // copy name
xferbuf.hdr.command = REGISTER_NAME; // REGISTER_NAME command
// Lets send it
senddatamessage(MySock.sock, &xferbuf);
wsprintf(outtext, GetStringRes(IDS_CHAT_AVAIL), MySock.name);
SetWindowText(GetParent(hwnd), outtext);
EndDialog(hwnd, TRUE); // Exit the dialog
DeleteObject (hfontDlg);
return (TRUE);
}
//
// FUNCTION: MsgConnectCommand(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Process WM_COMMAND message sent to the Connect box.
//
// PARAMETERS:
// hwnd - The window handing the message.
// uMessage - The message number. (unused).
// wparam - Message specific data (unused).
// lparam - Message specific data (unused).
//
// RETURN VALUE:
// Always returns 0 - message handled.
//
// COMMENTS:
// Uses this DispCommand function defined in wndproc.c combined
// with the cmdiConnect structure defined in this file to handle
// the command messages for the Connect dialog box.
//
LRESULT MsgConnectCommand(HWND hwnd,
UINT uMessage,
WPARAM wparam,
LPARAM lparam)
{
return DispCommand(&cmdiConnect, hwnd, wparam, lparam);
}
//
// FUNCTION: CmdConnectDone(HWND, WORD, HWND)
//
// PURPOSE: Free the Connect box and related data.
//
// PARAMETERS:
// hdlg - The window handling the command.
// wCommand - The command to be handled (unused).
// wNotify - Notification number (unused)
// hwndCtrl - NULL (unused).
//
// RETURN VALUE:
// Always returns TRUE.
//
// COMMENTS:
// Cleans up sockets then calls EndDialog to finish the dialog session.
//
LRESULT CmdConnectDone(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
closesocket(MySock.sock); // Free any aborted socket resources
EndDialog(hdlg, FALSE); // Exit Dialog -- rtrn false since no connection
return TRUE;
}
//
// FUNCTION: CmdConnectNow(HWND, WORD, HWND)
//
// PURPOSE: Establish the connection
//
// PARAMETERS:
// hdlg - The window handling the command.
// wCommand - The command to be handled (unused).
// wNotify - Notification number (unused)
// hwndCtrl - NULL (unused).
//
// RETURN VALUE:
// Always returns TRUE.
//
// COMMENTS:
// Makes Connection calls
//
LRESULT CmdConnectNow(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
int iIndex;
char outtext[80];
int numStrucs;
GUID guidNW = SVCID_NETWARE(NWCHATID);
GUID guidDNS = SVCID_TCP(DNSCHATID);
// Get protocol selected
iIndex = SendMessage(GetDlgItem(hdlg, CD_PROTOCOL), CB_GETCURSEL, 0, 0);
// ************************************************************************
//
// Below we will be calling GetAddressByName with the RES_FIND_MULTIPLE
// option in order to find a remote address to connect() to. At this
// point, GetAddressByName() only supports the DNS and the SAP/Bindery
// name spaces. Ultimately a single call to GetAddressByName will
// query all available protocols and name spaces, but this requires a
// central database which is currently not available. In the mean time
// we will make name space specific calls to GetAddressByName.
//
// ***********************************************************************
switch (lpProtBuf[goodprots[iIndex]].iAddressFamily)
{
case AF_IPX:
// SAP/Bindery Name Space
dwCSABufsize = sizeof(CSABuf);
if (((numStrucs = GetAddressByName(0, // GUID indicates name space so we don't need to specify
&guidNW, // GUID generated from NetWare service type
"GLOBCHAT", // Name of service to look for
NULL, // GUID implies IPX/SPX protocols
RES_FIND_MULTIPLE, // We might get multiple responses
NULL, // Not currently supported
CSABuf, // results buffer
&dwCSABufsize, // size of results buffer
NULL, // not supported
NULL) // not supported
) == SOCKET_ERROR) || (numStrucs == 0))
{
// Error -- try another protocol. We've got lots!
MessageBox(hdlg, GetStringRes(IDS_ERR_SERVER_NOT_FOUND), NULL, MB_OK);
EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
SetFocus(GetDlgItem(hdlg, CD_PROTOCOL));
return 0;
}
break;
case AF_INET:
// DNS Name Space
// Static Name Space requires us to specify the host name
GetDlgItemText(hdlg, CD_SERVER, lpServName, sizeof(lpServName));
dwCSABufsize = sizeof(CSABuf);
if (((numStrucs = GetAddressByName(0, // GUID indicates name space so we don't need to specify
&guidDNS, // GUID generated from TCP port number
lpServName, // Name of host to look for
NULL, // GUID implies UDP/TCP protocols
RES_FIND_MULTIPLE, // We might get multiple responses
NULL, // Not currently supported
CSABuf, // results buffer
&dwCSABufsize, // size of results buffer
NULL, // not supported
NULL) // not supported
) == SOCKET_ERROR) || (numStrucs == 0))
{
// Error -- try another protocol. We've got lots!
MessageBox(hdlg, GetStringRes(IDS_ERR_SERVER_NOT_FOUND), NULL, MB_OK);
EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
SetFocus(GetDlgItem(hdlg, CD_PROTOCOL));
return 0;
}
break;
case AF_NETBIOS:
// NetBIOS name space???
// no netbios name space provider so fill in lpCSABuf ourselves
numStrucs = 1;
CSABuf[0].iSocketType = lpProtBuf[goodprots[iIndex]].iSocketType;
CSABuf[0].iProtocol = lpProtBuf[goodprots[iIndex]].iProtocol;
SET_NETBIOS_SOCKADDR(&NBAddr,
NETBIOS_GROUP_NAME,
"GLOBSERV",
0);
CSABuf[0].RemoteAddr.lpSockaddr = (LPSOCKADDR)&NBAddr;
CSABuf[0].RemoteAddr.iSockaddrLength = sizeof(NBAddr);
break;
default:
// We don't support anything else
MessageBox(hdlg,
GetStringRes(IDS_ERR_NAMESPACE_NOT_SUPPORTED),
NULL, MB_OK);
EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
SetFocus(GetDlgItem(hdlg, CD_PROTOCOL));
return 0;
}
// Populate SOCKDATA struct
MySock.type = lpProtBuf[goodprots[iIndex]].iSocketType;
MySock.protocol = lpProtBuf[goodprots[iIndex]].iProtocol;
SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_CALLING_SOCKET));
// Call socket() using triple provided by EnumProtocols()
if((MySock.sock = socket(lpProtBuf[goodprots[iIndex]].iAddressFamily,
lpProtBuf[goodprots[iIndex]].iSocketType,
lpProtBuf[goodprots[iIndex]].iProtocol)) == INVALID_SOCKET)
{
// ERROR
wsprintf(outtext, GetStringRes(IDS_ERR_SOCKET_FAILED), WSAGetLastError());
SetDlgItemText(hdlg, CD_HELP, outtext);
return 0;
}
SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_CALLING_CONNECT));
// Call connect() using info from GetAddressByName()
if (connect(MySock.sock,
CSABuf[0].RemoteAddr.lpSockaddr,
CSABuf[0].RemoteAddr.iSockaddrLength) == SOCKET_ERROR)
{
// ERROR
wsprintf(outtext,
GetStringRes(IDS_ERR_CONNECT_FAILED), WSAGetLastError());
SetDlgItemText(hdlg, CD_HELP, outtext);
return 0;
}
// Signal for when we are ready for writing
SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_WAITING_FOR_ACCEPT));
// Specify message to signal accepted socket
if (WSAAsyncSelect(MySock.sock, hdlg, WSN_READYFORWRITE, FD_WRITE) == SOCKET_ERROR)
{
wsprintf(outtext,
GetStringRes(IDS_ERR_WSAASYNCSELECT), WSAGetLastError());
SetDlgItemText(hdlg, CD_HELP, outtext);
return 0;
}
return 0;
}
//
// FUNCTION: CmdConnectEnableOK(HWND, WORD, WORD, HWND)
//
// PURPOSE: Enable/Disable OK button when input data is valid/invalid
//
// PARAMETERS:
// hdlg - The window handling the command.
// wCommand - The command to be handled (unused).
// wNotify - Notification number (unused)
// hwndCtrl - NULL (unused).
//
// RETURN VALUE:
// Always returns TRUE.
//
// COMMENTS:
// Checks for acceptable name and protocol. We also need an acceptable
// machine name for TCP/IP protocol since DNS name space is static
//
LRESULT CmdConnectEnableOK(HWND hdlg, WORD wCommand, WORD wNotify, HWND hwndCtrl)
{
char buf[128];
int iIndex;
// Is message a change notification?
if ((wNotify == CBN_SELCHANGE) || (wNotify == EN_CHANGE))
{
// Yes!, get protocol
iIndex = SendMessage(GetDlgItem(hdlg, CD_PROTOCOL), CB_GETCURSEL, 0, 0);
if (iIndex == LB_ERR)
{
// No protocol selected...OK disabled
EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
return TRUE;
}
// Protocol selected! Check which one...
switch(lpProtBuf[goodprots[iIndex]].iAddressFamily)
{
case AF_IPX:
case AF_NETBIOS:
// For IPX and NetBIOS, we don't need the machine name
// so hide that edit control
ShowWindow(GetDlgItem(hdlg, CD_SERVER), SW_HIDE);
ShowWindow(GetDlgItem(hdlg, CD_SERVER_TEXT), SW_HIDE);
// We already have a protocol selected. Do we have a name?
if(GetDlgItemText(hdlg, CD_NAME, buf, sizeof(buf)) >= 1)
{
// Yes, we have a name and a protocol. Enable OK.
EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
}
else
{
// We don't have a name. Disable OK.
EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
}
return TRUE;
case AF_INET:
// For TCP/IP we need a protocol, name, and machine name
// in order for data to be valid
// Enable Machine name edit control and text
ShowWindow(GetDlgItem(hdlg, CD_SERVER), SW_SHOW);
ShowWindow(GetDlgItem(hdlg, CD_SERVER_TEXT), SW_SHOW);
EnableWindow(GetDlgItem(hdlg, CD_SERVER), TRUE);
EnableWindow(GetDlgItem(hdlg, CD_SERVER_TEXT), TRUE);
// We have a protocol selected. Do we have a name and a server name
if((GetDlgItemText(hdlg, CD_NAME, buf, sizeof(buf)) >= 1) &&
(GetDlgItemText(hdlg, CD_SERVER, buf, sizeof(buf)) >= 1))
{
// YES! Enable OK.
EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
SendMessage(GetDlgItem(hdlg, IDOK), BM_SETSTYLE, (WPARAM) LOWORD(BS_DEFPUSHBUTTON), MAKELPARAM(TRUE, 0));
}
else
{
// No name or server name. Disable OK.
EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
}
return TRUE;
default:
// We only support DNS, SAP/Bindery, and we simulate NetBIOS name spaces.
SetDlgItemText(hdlg, CD_HELP,
GetStringRes(IDS_ERR_UNSUPPORTED_PROTOCOL));
EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
return TRUE;
}
}
// When protocol combo box gets the focus, set help text at bottom of dialog
if (wNotify == CBN_SETFOCUS)
{
SetDlgItemText(hdlg, CD_HELP,
GetStringRes(IDS_SELECT_PROTOCOL));
}
if (wNotify == EN_SETFOCUS)
{
if (hwndCtrl == GetDlgItem(hdlg, CD_NAME))
{
// When name edit control gets the focus, set help text at bottom of dialog.
SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_ENTER_NAME));
}
else
{
// When server edit control gets the focus, set help text at bottom of dialog.
SetDlgItemText(hdlg, CD_HELP, GetStringRes(IDS_ENTER_MACHINE_NAME));
}
}
return TRUE;
}