Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

876 lines
31 KiB

/****************************************************************************/
/* robocli.c */
/* */
/* RoboClient scalability testing utility source file */
/* */
/* Copyright (c) 1999 Microsoft Corporation */
/****************************************************************************/
#include <windows.h>
#include "resource.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <process.h>
#include <time.h>
#include <tchar.h>
#include <crtdbg.h>
#pragma warning (push, 4)
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof((a)[0]))
#define WM_Socket WM_APP+0
#define MAX_CONNECTIONS 64
#define MAX_CONNECTIONS_IN_UI 5
#define MAX_EDIT_TEXT_LENGTH 100
#define MAX_DISPLAY_STRING_LENGTH 80
#define BUFSIZE 100
#define MAXADDR 16 // xxx.xxx.xxx.xxx + 1
#define RECONNECT_TIMEOUT 60000
#define STATE_DISCONNECTED 0
#define STATE_CONNECTED 1
#define NUM_TABBED_ITEMS 4
#define DEFAULT_PORT 9877
// Globals
UINT_PTR g_Timer = 1;
int g_dontreboot = 0;
struct CONNECTIONSTATUS {
SOCKET sock;
int state;
HWND hStatusText;
};
typedef struct CONNECTIONSTATUS CONNECTIONSTATUS;
CONNECTIONSTATUS g_cs[MAX_CONNECTIONS];
int g_nNumConnections = 0;
// Old procedures for dialog items
WNDPROC g_OldProc[NUM_TABBED_ITEMS];
// HWNDs for dialog items
HWND g_hwnd[NUM_TABBED_ITEMS];
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
int DoConnect(TCHAR psServerName[], HWND hWnd);
TCHAR *GetCommandLineArg(TCHAR *psCommandLine);
int AllConnected();
int AnyConnected();
int NoneConnected();
int GetIndexFromSocket(SOCKET s);
int UpdateButtons(HWND hwnd);
LRESULT CALLBACK TabProc(HWND hwnd, UINT Msg,
WPARAM wParam, LPARAM lParam);
// CopyStrToTStr
//
// Helper function. In Unicode, copies a string into a WCHAR[] buffer. In ANSI,
// just copies it into a char[] buffer.
__inline void CopyStrToTStr(TCHAR *szTDest, char *szSrc, int nLength) {
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, szSrc, -1,
szTDest, nLength);
#else
strncpy(szTDest, szSrc, nLength);
#endif // UNICODE
}
// CopyTStrToStr
//
// Helper function. Copies either wide or ansi into an ansi buffer.
__inline void CopyTStrToStr(char *szDest, TCHAR *szTSrc, int nLength) {
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, szTSrc, -1,
szDest, nLength, 0, 0);
#else
strncpy(szDest, szTSrc, nLength);
#endif // UNICODE
}
// entry point for the application
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
static TCHAR szAppName[] = _T("RoboClient");
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass;
HWND hEditBox, hErrorText, hOKButton, hDisconButton, hCancelButton;
WORD wVersionRequested;
int err;
WSADATA wsaData;
int i;
TCHAR *psCommandLine;
TCHAR *psServerName;
TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
// unreferenced parameters
lpCmdLine;
hPrevInstance;
// Only one instance may run at a time (not TS-aware though)
// Don't need to clean up because the system closes the handle automatically
// when the process terminates, and we want the handle to persist for the
// lifetime of the process
CreateMutex(NULL, FALSE, _T("RoboCli, the one and only"));
if (GetLastError() == ERROR_ALREADY_EXISTS) {
TCHAR psDisplayTitleString[MAX_DISPLAY_STRING_LENGTH];
LoadString(NULL, IDS_ROBOCLIALREADYRUNNING, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
LoadString(NULL, IDS_FATALERROR, psDisplayTitleString,
MAX_DISPLAY_STRING_LENGTH);
MessageBox(0, psDisplayString, psDisplayTitleString, 0);
return -1;
}
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = DLGWINDOWEXTRA;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(RoboClient));
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) (COLOR_ACTIVEBORDER + 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(RoboClient));
RegisterClassEx(&wndclass);
hwnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), 0, NULL);
hEditBox = GetDlgItem(hwnd, IDC_SERVNAMEEDIT);
hErrorText = GetDlgItem(hwnd, IDC_ERRORTEXT);
hOKButton = GetDlgItem(hwnd, IDOK);
hDisconButton = GetDlgItem(hwnd, IDDISCONNECT);
hCancelButton = GetDlgItem(hwnd, IDCANCEL);
psCommandLine = GetCommandLine();
if ((psServerName = GetCommandLineArg(psCommandLine)) == NULL)
SetWindowText(hEditBox, _T("ts-dev"));
else
SetWindowText(hEditBox, psServerName);
g_cs[0].hStatusText = GetDlgItem(hwnd, IDC_CONN1);
g_cs[1].hStatusText = GetDlgItem(hwnd, IDC_CONN2);
g_cs[2].hStatusText = GetDlgItem(hwnd, IDC_CONN3);
g_cs[3].hStatusText = GetDlgItem(hwnd, IDC_CONN4);
g_cs[4].hStatusText = GetDlgItem(hwnd, IDC_CONN5);
// Use main status line for status after the first five
for (i = 5; i < MAX_CONNECTIONS; i++) {
g_cs[i].hStatusText = hErrorText;
}
for (i = 0; i < MAX_CONNECTIONS; i++) {
_ASSERTE(IsWindow(g_cs[i].hStatusText));
g_cs[i].sock = INVALID_SOCKET;
g_cs[i].state = STATE_DISCONNECTED;
LoadString(NULL, IDS_NOTCONNECTED, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText, psDisplayString);
}
ShowWindow(hwnd, nCmdShow);
// Initialize Winsock
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
TCHAR psDisplayTitleString[MAX_DISPLAY_STRING_LENGTH];
LoadString(NULL, IDS_WINSOCKNOINIT, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
LoadString(NULL, IDS_FATALERROR, psDisplayTitleString,
MAX_DISPLAY_STRING_LENGTH);
MessageBox(0, psDisplayString, psDisplayTitleString, 0);
return -1;
}
LoadString(NULL, IDS_WELCOME, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
// There is now a timer that will fire every RECONNECT_TIMEOUT seconds
g_Timer = SetTimer(hwnd, g_Timer, RECONNECT_TIMEOUT, 0);
if (g_Timer == 0) {
LoadString(NULL, IDS_CANTSETTIMER, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
}
// Store old window procedures for controls so that I can subclass them
// Also, store the HWND of each control for searching
g_OldProc[0] = (WNDPROC)SetWindowLongPtr(hEditBox, GWLP_WNDPROC,
(LONG_PTR) TabProc);
g_hwnd[0] = hEditBox;
g_OldProc[1] = (WNDPROC)SetWindowLongPtr(hOKButton, GWLP_WNDPROC,
(LONG_PTR) TabProc);
g_hwnd[1] = hOKButton;
g_OldProc[2] = (WNDPROC)SetWindowLongPtr(hDisconButton, GWLP_WNDPROC,
(LONG_PTR) TabProc);
g_hwnd[2] = hDisconButton;
g_OldProc[3] = (WNDPROC)SetWindowLongPtr(hCancelButton, GWLP_WNDPROC,
(LONG_PTR) TabProc);
g_hwnd[3] = hCancelButton;
// Limit the length of the text in the edit box
SendMessage(hEditBox, EM_LIMITTEXT, MAX_EDIT_TEXT_LENGTH, 0);
// Highlight the text in the edit box
SendMessage(hEditBox, EM_SETSEL, 0, -1);
// Set the focus to the edit box
SetFocus(hEditBox);
// Connect immediately
SendMessage(hwnd, WM_COMMAND, IDOK, 0);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
WSACleanup();
return (int) msg.wParam;
}
// window procedure: processes window messages in a big switch statement
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
HWND hErrorText;
HWND hEditBox;
TCHAR psEditText[MAX_EDIT_TEXT_LENGTH];
TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
hEditBox = GetDlgItem(hwnd, IDC_SERVNAMEEDIT);
hErrorText = GetDlgItem(hwnd, IDC_ERRORTEXT);
switch (iMsg)
{
case WM_DESTROY:
if (AnyConnected())
SendMessage(hwnd, WM_COMMAND, IDDISCONNECT, 0);
PostQuitMessage(0);
return 0;
case WM_COMMAND:
if (LOWORD(wParam) == IDCANCEL)
{
if (AnyConnected())
SendMessage(hwnd, WM_COMMAND, IDDISCONNECT, 0);
// Should call DestroyWindow() any time we now postquitmessage
PostQuitMessage(0);
return TRUE;
}
if (LOWORD(wParam) == IDOK)
{
// IDOK is the "Connect" button.
LoadString(NULL, IDS_CONNECTALL, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
GetWindowText(hEditBox, psEditText, MAX_EDIT_TEXT_LENGTH);
if (DoConnect(psEditText, hwnd) != 0) {
LoadString(NULL, IDS_ERRORDOINGCONNECT, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
}
UpdateButtons(hwnd);
return TRUE;
}
if (LOWORD(wParam) == IDDISCONNECT)
{
int i;
LoadString(NULL, IDS_DISCONNECTALL, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
// Close all connected sockets and update button state
for (i = 0; i < MAX_CONNECTIONS; i++) {
if (g_cs[i].state == STATE_CONNECTED) {
int err; // Used for debugging
// For some reason, we have to shut down in some cases or
// else the server will not know that the client has
// disconnected
err = shutdown(g_cs[i].sock, SD_BOTH);
err = closesocket(g_cs[i].sock);
LoadString(NULL, IDS_NOTCONNECTED, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText, psDisplayString);
g_cs[i].state = STATE_DISCONNECTED;
}
}
UpdateButtons(hwnd);
return TRUE;
}
return 0;
case WM_CREATE:
break;
case WM_TIMER:
if (!AllConnected())
PostMessage(hwnd, WM_COMMAND, IDOK, 0);
break;
case WM_SYSKEYDOWN:
// NOTE INTENTIONAL FALLTHROUGH!
case WM_KEYDOWN:
if (wParam == VK_TAB) {
if (!AllConnected()) {
SetFocus(g_hwnd[0]);
SendMessage(g_hwnd[0], EM_SETSEL, 0, -1);
} else {
SetFocus(g_hwnd[2]);
}
}
if (wParam == VK_RETURN) {
if (GetFocus() == g_hwnd[3])
SendMessage(hwnd, WM_COMMAND, IDCANCEL, 0);
else if (GetFocus() == g_hwnd[2])
SendMessage(hwnd, WM_COMMAND, IDDISCONNECT, 0);
else
SendMessage(hwnd, WM_COMMAND, IDOK, 0);
}
break;
case WM_Socket:
switch (WSAGETSELECTEVENT(lParam)) {
case FD_CLOSE:
{
int i;
i = GetIndexFromSocket((SOCKET) wParam);
if (i == -1) {
LoadString(NULL, IDS_SOCKNOTFOUND, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
break;
}
closesocket(g_cs[i].sock);
LoadString(NULL, IDS_SERVERENDEDCONN, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText,
psDisplayString);
g_cs[i].state = STATE_DISCONNECTED;
UpdateButtons(hwnd);
break;
}
case FD_READ:
{
// TODO: try using just one buffer or at least make these good names
char psInputDataRead[BUFSIZE];
TCHAR psInputDataReadT[BUFSIZE];
TCHAR debugString[200];
TCHAR *psBaseScriptName;
TCHAR *psUserName;
int n;
int i; // index into our connectionstatus structure
i = GetIndexFromSocket((SOCKET) wParam);
if (i == -1) {
LoadString(NULL, IDS_CANTLOCATESOCKINFO, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
return FALSE;
}
LoadString(NULL, IDS_DATAREADY, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText, psDisplayString);
n = recv(g_cs[i].sock, psInputDataRead, sizeof(
psInputDataRead), 0);
if (n == SOCKET_ERROR) {
LoadString(NULL, IDS_SOCKERR, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText,psDisplayString);
} else {
CopyStrToTStr(psInputDataReadT, psInputDataRead, BUFSIZE);
psInputDataReadT[n] = 0; // null terminate
// check for client auto-update command
// TODO: what if recv returns gibberish or 0?
if (_tcsncmp(psInputDataReadT, _T("update"),
(n >= 6) ? 6 : n) == 0) {
LoadString(NULL, IDS_UPDATINGCLIENT, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
if (_spawnl(_P_NOWAIT, "update.cmd", "update.cmd", 0)
== -1) {
LoadString(NULL, IDS_CANTRUNUPDATE, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
break;
} else {
// the client update script has been successfully
// initiated
// Terminate self
PostQuitMessage(0);
return TRUE;
}
}
// check for reboot command
if (_tcsncmp(psInputDataReadT, _T("reboot"), (n >= 6) ? 6 : n)
== 0) {
// If we receive more than one reboot command,
// ignore the extras
if (g_dontreboot == 1)
return TRUE;
LoadString(NULL, IDS_REBOOTINGCLIENT, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
if (_spawnl(_P_WAIT, "reboot.cmd", "reboot.cmd", 0)
== -1) {
LoadString(NULL, IDS_ERRORREBOOTING, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
break;
} else {
// Disable further reboots
g_dontreboot = 1;
PostQuitMessage(0);
return TRUE;
}
}
// If it's not a command, then it's a run script command
// in our wire format. See robosrv code for what that is.
_tcstok(psInputDataReadT, _T("/")); // Terminate with NULL
psBaseScriptName = _tcstok(0, _T("/")); // Get the script name
psUserName = _tcstok(0, _T("/")); // Get the user name to
// replace the template name
if (psBaseScriptName == 0) {
LoadString(NULL, IDS_ERRGETTINGSTUFF, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText, psDisplayString);
break;
}
if (psUserName == 0) {
LoadString(NULL, IDS_ERRGETTINGUSERNAME, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText, psDisplayString);
break;
}
// Now we prepare to run a batch file on the robocli
// machine called "runscript.bat".
LoadString(NULL, IDS_NOWRUNNING, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
wsprintf(debugString, psDisplayString, psBaseScriptName,
psInputDataReadT);
if (_tspawnl(_P_NOWAIT, _T("runscript.bat"), _T("runscript.bat"),
psBaseScriptName, psInputDataReadT, psUserName, NULL) == -1) {
LoadString(NULL, IDS_ERRRUNNING, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
wsprintf(debugString, psDisplayString,
psBaseScriptName, psInputDataReadT);
if (send(g_cs[i].sock, "errorsmclient",
(int) strlen("errorsmclient") + 1, 0)
== SOCKET_ERROR) {
LoadString(NULL, IDS_SENDERRSENDINGSMERR,
psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
strcpy(debugString, psDisplayString);
}
} else {
if (send(g_cs[i].sock, "success",
(int) strlen("success") + 1, 0) == SOCKET_ERROR) {
LoadString(NULL, IDS_SENDERRSENDINGSUCCESS,
psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
strcpy(debugString, psDisplayString);
}
}
SetWindowText(g_cs[i].hStatusText, debugString);
}
return TRUE;
}
}
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
// Takes the command line string as an argument and returns a pointer
// inside that string of the server name, NULL if there is no such string.
// Pops up a messagebox on error
TCHAR *GetCommandLineArg(TCHAR *psCommandLine) {
TCHAR *psCurrPtr = psCommandLine;
TCHAR *retval;
TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
TCHAR psDisplayTitleString[MAX_DISPLAY_STRING_LENGTH];
if (*psCurrPtr == '\"') {
psCurrPtr++; // skip that character
// Handle if the first arg is quoted
while ((*psCurrPtr != 0) && (*psCurrPtr != '\"'))
psCurrPtr++;
// then skip the " character
if (*psCurrPtr == '\"')
psCurrPtr++;
} else {
// go forward in the array until you get a ' ' or until NULL
while((*psCurrPtr != 0) && (*psCurrPtr != ' '))
psCurrPtr++;
}
// skip spaces
while(*psCurrPtr == ' ')
psCurrPtr++;
// if the character is NULL, return NULL (no args)
if (*psCurrPtr == 0)
return 0;
// now, check that the next three are "-s:" and then non-null,
if (_tcsncmp(psCurrPtr, _T("-s:"), 3) != 0)
goto SHOWMSGBOX;
// and that there isn't another argument afterward
// but first, store retval in case it's ok
retval = &psCurrPtr[3];
if ((*retval == 0) || (*retval == ' '))
goto SHOWMSGBOX;
while ((*psCurrPtr != 0) && (*psCurrPtr != ' '))
psCurrPtr++;
if (*psCurrPtr != 0)
goto SHOWMSGBOX;
// return the pointer to that non-null thing
return retval; // I'm not going to allow a servername to be quoted
SHOWMSGBOX:
LoadString(NULL, IDS_ROBOCLI_SYNTAX, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
LoadString(NULL, IDS_ROBOCLI_SYNTAX_TITLE, psDisplayTitleString,
MAX_DISPLAY_STRING_LENGTH);
MessageBox(0, psDisplayString, psDisplayTitleString, 0);
return NULL;
}
// Tries to connect all sockets not in the connected (STATE_CONNECTED) state
// Returns nonzero on error. Responsible for setting status lines
int DoConnect(TCHAR *psServerName, HWND hWnd) {
struct hostent *pSrv_info;
struct sockaddr_in addr;
int i, cData;
HWND hErrorText;
TCHAR debugString[100];
char psNumConns[6];
char psServerNameA[MAX_EDIT_TEXT_LENGTH];
TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
hErrorText = GetDlgItem(hWnd, IDC_ERRORTEXT);
CopyTStrToStr(psServerNameA, psServerName, MAX_EDIT_TEXT_LENGTH);
pSrv_info = gethostbyname(psServerNameA);
if (pSrv_info == NULL || pSrv_info->h_length > sizeof(addr.sin_addr) ) {
LoadString(NULL, IDS_UNKNOWNHOST, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
goto err;
}
else {
memcpy(&addr.sin_addr, pSrv_info->h_addr, pSrv_info->h_length);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(DEFAULT_PORT);
// "First time through the loop" -- make a connection and set
// nNumConnections
if (g_nNumConnections == 0) {
// nNumConnections == 0 indicates no connection has ever been made
// If it is nonzero it indicates the total number of connections the
// RoboClient is to make
if (g_cs[0].state != STATE_CONNECTED) {
LoadString(NULL, IDS_MAKINGINITCONN, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
LoadString(NULL, IDS_CONNECTING, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[0].hStatusText, psDisplayString);
g_cs[0].sock = socket(AF_INET, SOCK_STREAM, 0);
if (g_cs[0].sock == INVALID_SOCKET) {
LoadString(NULL, IDS_SOCKETERR, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[0].hStatusText, psDisplayString);
goto err;
}
if (connect(g_cs[0].sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
// This is duplicated functionality
LoadString(NULL, IDS_UNABLETOCONNECT, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
_sntprintf(debugString, 100, psDisplayString, psServerName);
SetWindowText(g_cs[0].hStatusText, debugString);
goto err;
}
// Set nNumConnections
cData = recv(g_cs[0].sock, psNumConns, sizeof(psNumConns), 0);
// psNumConns is an array but we should really only receive one
// byte, so...
g_nNumConnections = psNumConns[0] - '0';
if ((g_nNumConnections < 1)
|| (g_nNumConnections > MAX_CONNECTIONS)) {
LoadString(NULL, IDS_INVALIDCONNECTIONSFROMSERVER,
psDisplayString, MAX_DISPLAY_STRING_LENGTH);
SetWindowText(hErrorText, psDisplayString);
g_nNumConnections = 0;
}
LoadString(NULL, IDS_CONNECTEDNCONNECTIONS, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
_sntprintf(debugString, SIZEOF_ARRAY(debugString), psDisplayString, g_nNumConnections);
debugString[SIZEOF_ARRAY(debugString) - 1] = 0;
SetWindowText(hErrorText, debugString);
// Disable status lines for all unused connections,
// enable for used ones
for (i = 0; i < g_nNumConnections; i++) {
EnableWindow(g_cs[i].hStatusText, TRUE);
}
// disable connections up to 5
for (i = g_nNumConnections; i < MAX_CONNECTIONS_IN_UI; i++) {
EnableWindow(g_cs[i].hStatusText, FALSE);
}
g_cs[0].state = STATE_CONNECTED;
WSAAsyncSelect(g_cs[0].sock, hWnd, WM_Socket, FD_READ | FD_CLOSE);
LoadString(NULL, IDS_CONNECTED, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
_sntprintf(debugString, SIZEOF_ARRAY(debugString), psDisplayString, g_cs[0].sock);
debugString[SIZEOF_ARRAY(debugString) - 1] = 0;
SetWindowText(g_cs[0].hStatusText, debugString);
} else {
// extremely bad
}
}
// start from 0 because what if the initial connection was disconnected
// and we are trying to reconnect?
for (i = 0; i < g_nNumConnections; i++) {
if (g_cs[i].state != STATE_CONNECTED) {
// TODO: this is duplicated functionality
LoadString(NULL, IDS_CONNECTING, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText, psDisplayString);
g_cs[i].sock = socket(AF_INET, SOCK_STREAM, 0);
if (g_cs[i].sock == INVALID_SOCKET) {
LoadString(NULL, IDS_SOCKETERR, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
SetWindowText(g_cs[i].hStatusText, psDisplayString);
goto err;
}
if (connect(g_cs[i].sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
LoadString(NULL, IDS_UNABLETOCONNECT, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
_sntprintf(debugString, 100, psDisplayString, psServerName);
SetWindowText(g_cs[i].hStatusText, debugString);
goto err;
}
// Ignore nNumConnections
cData = recv(g_cs[i].sock, psNumConns, sizeof(psNumConns), 0);
g_cs[i].state = STATE_CONNECTED;
WSAAsyncSelect(g_cs[i].sock, hWnd, WM_Socket, FD_READ | FD_CLOSE);
LoadString(NULL, IDS_CONNECTED, psDisplayString,
MAX_DISPLAY_STRING_LENGTH);
_sntprintf(debugString, 100, psDisplayString, g_cs[i].sock);
SetWindowText(g_cs[i].hStatusText, debugString);
}
}
return 0;
err:
return -1;
}
// predicate functions
// Are all connections up to the number requested connected?
int AllConnected() {
int i;
if (g_nNumConnections == 0)
return 0;
for (i = 0; i < g_nNumConnections; i++) {
if (g_cs[i].state == STATE_DISCONNECTED)
return 0;
}
return 1;
}
// Are any connections connected?
int AnyConnected() {
int i;
for (i = 0; i < g_nNumConnections; i++) {
if (g_cs[i].state == STATE_CONNECTED)
return 1;
}
return 0;
}
// None connected?
int NoneConnected() {
return !AnyConnected();
}
// Extremely useful function to get the robolient index (i.e., 0-based index
// in the status line and in our data structure). Returns -1 on error
int GetIndexFromSocket(SOCKET s) {
int i;
for (i = 0; i < MAX_CONNECTIONS; i++) {
if (g_cs[i].state == STATE_CONNECTED)
if (g_cs[i].sock == s)
return i;
}
return -1;
}
// Update the state of the buttons based on the states of the connections
int UpdateButtons(HWND hwnd) {
HWND hConnectButton;
HWND hDisconnectButton;
HWND hEditBox;
// TODO: init all dlg items at once and never check again
hConnectButton = GetDlgItem(hwnd, IDOK);
hDisconnectButton = GetDlgItem(hwnd, IDDISCONNECT);
hEditBox = GetDlgItem(hwnd, IDC_SERVNAMEEDIT);
if (AnyConnected()) {
EnableWindow(hDisconnectButton, TRUE);
EnableWindow(hEditBox, FALSE); // Can't connect to different servers
}
if (AllConnected()) {
EnableWindow(hConnectButton, FALSE);
SetFocus(g_hwnd[2]);
}
if (NoneConnected()) {
EnableWindow(hConnectButton, TRUE);
EnableWindow(hEditBox, TRUE);
EnableWindow(hDisconnectButton, FALSE);
g_nNumConnections = 0; // This means we can change it on next connect
g_dontreboot = 0; // reset this if none is connected anymore
SetFocus(g_hwnd[0]);
SendMessage(g_hwnd[0], EM_SETSEL, 0, -1);
}
return 0;
}
// Subclass procedure to handle tabs
LRESULT CALLBACK TabProc(HWND hwnd, UINT Msg,
WPARAM wParam, LPARAM lParam) {
int i;
// Find the id of the hwnd
for (i = 0; i < NUM_TABBED_ITEMS; i++) {
if (g_hwnd[i] == hwnd)
break;
}
switch (Msg) {
case WM_KEYDOWN:
if (wParam == VK_TAB) {
int newItem = i;
// Find the next or previous enabled item
do {
newItem = (newItem + (GetKeyState(VK_SHIFT) < 0 ?
NUM_TABBED_ITEMS - 1 : 1)) % NUM_TABBED_ITEMS;
} while (IsWindowEnabled(g_hwnd[newItem]) == 0);
// set the focus to the next or previous item
SetFocus(g_hwnd[newItem]);
// if the control is an edit box control, select all text
if (newItem == 0)
SendMessage(g_hwnd[newItem], EM_SETSEL, 0, -1);
}
if (wParam == VK_ESCAPE) {
SendMessage(GetParent(hwnd), WM_COMMAND, IDCANCEL, 0);
}
if (wParam == VK_RETURN) {
SendMessage(GetParent(hwnd), WM_KEYDOWN, wParam, lParam);
}
break;
}
return CallWindowProc(g_OldProc[i], hwnd, Msg, wParam, lParam);
}
#pragma warning (pop)