|
|
/****************************************************************************/ /* 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)
|