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

  1. /****************************************************************************/
  2. /* robocli.c */
  3. /* */
  4. /* RoboClient scalability testing utility source file */
  5. /* */
  6. /* Copyright (c) 1999 Microsoft Corporation */
  7. /****************************************************************************/
  8. #include <windows.h>
  9. #include "resource.h"
  10. #include <winsock2.h>
  11. #include <ws2tcpip.h>
  12. #include <stdio.h>
  13. #include <process.h>
  14. #include <time.h>
  15. #include <tchar.h>
  16. #include <crtdbg.h>
  17. #pragma warning (push, 4)
  18. #define SIZEOF_ARRAY(a) (sizeof(a)/sizeof((a)[0]))
  19. #define WM_Socket WM_APP+0
  20. #define MAX_CONNECTIONS 64
  21. #define MAX_CONNECTIONS_IN_UI 5
  22. #define MAX_EDIT_TEXT_LENGTH 100
  23. #define MAX_DISPLAY_STRING_LENGTH 80
  24. #define BUFSIZE 100
  25. #define MAXADDR 16 // xxx.xxx.xxx.xxx + 1
  26. #define RECONNECT_TIMEOUT 60000
  27. #define STATE_DISCONNECTED 0
  28. #define STATE_CONNECTED 1
  29. #define NUM_TABBED_ITEMS 4
  30. #define DEFAULT_PORT 9877
  31. // Globals
  32. UINT_PTR g_Timer = 1;
  33. int g_dontreboot = 0;
  34. struct CONNECTIONSTATUS {
  35. SOCKET sock;
  36. int state;
  37. HWND hStatusText;
  38. };
  39. typedef struct CONNECTIONSTATUS CONNECTIONSTATUS;
  40. CONNECTIONSTATUS g_cs[MAX_CONNECTIONS];
  41. int g_nNumConnections = 0;
  42. // Old procedures for dialog items
  43. WNDPROC g_OldProc[NUM_TABBED_ITEMS];
  44. // HWNDs for dialog items
  45. HWND g_hwnd[NUM_TABBED_ITEMS];
  46. LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
  47. int DoConnect(TCHAR psServerName[], HWND hWnd);
  48. TCHAR *GetCommandLineArg(TCHAR *psCommandLine);
  49. int AllConnected();
  50. int AnyConnected();
  51. int NoneConnected();
  52. int GetIndexFromSocket(SOCKET s);
  53. int UpdateButtons(HWND hwnd);
  54. LRESULT CALLBACK TabProc(HWND hwnd, UINT Msg,
  55. WPARAM wParam, LPARAM lParam);
  56. // CopyStrToTStr
  57. //
  58. // Helper function. In Unicode, copies a string into a WCHAR[] buffer. In ANSI,
  59. // just copies it into a char[] buffer.
  60. __inline void CopyStrToTStr(TCHAR *szTDest, char *szSrc, int nLength) {
  61. #ifdef UNICODE
  62. MultiByteToWideChar(CP_ACP, 0, szSrc, -1,
  63. szTDest, nLength);
  64. #else
  65. strncpy(szTDest, szSrc, nLength);
  66. #endif // UNICODE
  67. }
  68. // CopyTStrToStr
  69. //
  70. // Helper function. Copies either wide or ansi into an ansi buffer.
  71. __inline void CopyTStrToStr(char *szDest, TCHAR *szTSrc, int nLength) {
  72. #ifdef UNICODE
  73. WideCharToMultiByte(CP_ACP, 0, szTSrc, -1,
  74. szDest, nLength, 0, 0);
  75. #else
  76. strncpy(szDest, szTSrc, nLength);
  77. #endif // UNICODE
  78. }
  79. // entry point for the application
  80. int APIENTRY WinMain(HINSTANCE hInstance,
  81. HINSTANCE hPrevInstance,
  82. LPSTR lpCmdLine,
  83. int nCmdShow)
  84. {
  85. static TCHAR szAppName[] = _T("RoboClient");
  86. HWND hwnd;
  87. MSG msg;
  88. WNDCLASSEX wndclass;
  89. HWND hEditBox, hErrorText, hOKButton, hDisconButton, hCancelButton;
  90. WORD wVersionRequested;
  91. int err;
  92. WSADATA wsaData;
  93. int i;
  94. TCHAR *psCommandLine;
  95. TCHAR *psServerName;
  96. TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
  97. // unreferenced parameters
  98. lpCmdLine;
  99. hPrevInstance;
  100. // Only one instance may run at a time (not TS-aware though)
  101. // Don't need to clean up because the system closes the handle automatically
  102. // when the process terminates, and we want the handle to persist for the
  103. // lifetime of the process
  104. CreateMutex(NULL, FALSE, _T("RoboCli, the one and only"));
  105. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  106. TCHAR psDisplayTitleString[MAX_DISPLAY_STRING_LENGTH];
  107. LoadString(NULL, IDS_ROBOCLIALREADYRUNNING, psDisplayString,
  108. MAX_DISPLAY_STRING_LENGTH);
  109. LoadString(NULL, IDS_FATALERROR, psDisplayTitleString,
  110. MAX_DISPLAY_STRING_LENGTH);
  111. MessageBox(0, psDisplayString, psDisplayTitleString, 0);
  112. return -1;
  113. }
  114. wndclass.cbSize = sizeof(wndclass);
  115. wndclass.style = CS_HREDRAW | CS_VREDRAW;
  116. wndclass.lpfnWndProc = WndProc;
  117. wndclass.cbClsExtra = 0;
  118. wndclass.cbWndExtra = DLGWINDOWEXTRA;
  119. wndclass.hInstance = hInstance;
  120. wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(RoboClient));
  121. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  122. wndclass.hbrBackground = (HBRUSH) (COLOR_ACTIVEBORDER + 1);
  123. wndclass.lpszMenuName = NULL;
  124. wndclass.lpszClassName = szAppName;
  125. wndclass.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(RoboClient));
  126. RegisterClassEx(&wndclass);
  127. hwnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), 0, NULL);
  128. hEditBox = GetDlgItem(hwnd, IDC_SERVNAMEEDIT);
  129. hErrorText = GetDlgItem(hwnd, IDC_ERRORTEXT);
  130. hOKButton = GetDlgItem(hwnd, IDOK);
  131. hDisconButton = GetDlgItem(hwnd, IDDISCONNECT);
  132. hCancelButton = GetDlgItem(hwnd, IDCANCEL);
  133. psCommandLine = GetCommandLine();
  134. if ((psServerName = GetCommandLineArg(psCommandLine)) == NULL)
  135. SetWindowText(hEditBox, _T("ts-dev"));
  136. else
  137. SetWindowText(hEditBox, psServerName);
  138. g_cs[0].hStatusText = GetDlgItem(hwnd, IDC_CONN1);
  139. g_cs[1].hStatusText = GetDlgItem(hwnd, IDC_CONN2);
  140. g_cs[2].hStatusText = GetDlgItem(hwnd, IDC_CONN3);
  141. g_cs[3].hStatusText = GetDlgItem(hwnd, IDC_CONN4);
  142. g_cs[4].hStatusText = GetDlgItem(hwnd, IDC_CONN5);
  143. // Use main status line for status after the first five
  144. for (i = 5; i < MAX_CONNECTIONS; i++) {
  145. g_cs[i].hStatusText = hErrorText;
  146. }
  147. for (i = 0; i < MAX_CONNECTIONS; i++) {
  148. _ASSERTE(IsWindow(g_cs[i].hStatusText));
  149. g_cs[i].sock = INVALID_SOCKET;
  150. g_cs[i].state = STATE_DISCONNECTED;
  151. LoadString(NULL, IDS_NOTCONNECTED, psDisplayString,
  152. MAX_DISPLAY_STRING_LENGTH);
  153. SetWindowText(g_cs[i].hStatusText, psDisplayString);
  154. }
  155. ShowWindow(hwnd, nCmdShow);
  156. // Initialize Winsock
  157. wVersionRequested = MAKEWORD( 2, 2 );
  158. err = WSAStartup( wVersionRequested, &wsaData );
  159. if ( err != 0 ) {
  160. TCHAR psDisplayTitleString[MAX_DISPLAY_STRING_LENGTH];
  161. LoadString(NULL, IDS_WINSOCKNOINIT, psDisplayString,
  162. MAX_DISPLAY_STRING_LENGTH);
  163. LoadString(NULL, IDS_FATALERROR, psDisplayTitleString,
  164. MAX_DISPLAY_STRING_LENGTH);
  165. MessageBox(0, psDisplayString, psDisplayTitleString, 0);
  166. return -1;
  167. }
  168. LoadString(NULL, IDS_WELCOME, psDisplayString,
  169. MAX_DISPLAY_STRING_LENGTH);
  170. SetWindowText(hErrorText, psDisplayString);
  171. // There is now a timer that will fire every RECONNECT_TIMEOUT seconds
  172. g_Timer = SetTimer(hwnd, g_Timer, RECONNECT_TIMEOUT, 0);
  173. if (g_Timer == 0) {
  174. LoadString(NULL, IDS_CANTSETTIMER, psDisplayString,
  175. MAX_DISPLAY_STRING_LENGTH);
  176. SetWindowText(hErrorText, psDisplayString);
  177. }
  178. // Store old window procedures for controls so that I can subclass them
  179. // Also, store the HWND of each control for searching
  180. g_OldProc[0] = (WNDPROC)SetWindowLongPtr(hEditBox, GWLP_WNDPROC,
  181. (LONG_PTR) TabProc);
  182. g_hwnd[0] = hEditBox;
  183. g_OldProc[1] = (WNDPROC)SetWindowLongPtr(hOKButton, GWLP_WNDPROC,
  184. (LONG_PTR) TabProc);
  185. g_hwnd[1] = hOKButton;
  186. g_OldProc[2] = (WNDPROC)SetWindowLongPtr(hDisconButton, GWLP_WNDPROC,
  187. (LONG_PTR) TabProc);
  188. g_hwnd[2] = hDisconButton;
  189. g_OldProc[3] = (WNDPROC)SetWindowLongPtr(hCancelButton, GWLP_WNDPROC,
  190. (LONG_PTR) TabProc);
  191. g_hwnd[3] = hCancelButton;
  192. // Limit the length of the text in the edit box
  193. SendMessage(hEditBox, EM_LIMITTEXT, MAX_EDIT_TEXT_LENGTH, 0);
  194. // Highlight the text in the edit box
  195. SendMessage(hEditBox, EM_SETSEL, 0, -1);
  196. // Set the focus to the edit box
  197. SetFocus(hEditBox);
  198. // Connect immediately
  199. SendMessage(hwnd, WM_COMMAND, IDOK, 0);
  200. while(GetMessage(&msg, NULL, 0, 0))
  201. {
  202. TranslateMessage(&msg);
  203. DispatchMessage(&msg);
  204. }
  205. WSACleanup();
  206. return (int) msg.wParam;
  207. }
  208. // window procedure: processes window messages in a big switch statement
  209. LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  210. {
  211. HWND hErrorText;
  212. HWND hEditBox;
  213. TCHAR psEditText[MAX_EDIT_TEXT_LENGTH];
  214. TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
  215. hEditBox = GetDlgItem(hwnd, IDC_SERVNAMEEDIT);
  216. hErrorText = GetDlgItem(hwnd, IDC_ERRORTEXT);
  217. switch (iMsg)
  218. {
  219. case WM_DESTROY:
  220. if (AnyConnected())
  221. SendMessage(hwnd, WM_COMMAND, IDDISCONNECT, 0);
  222. PostQuitMessage(0);
  223. return 0;
  224. case WM_COMMAND:
  225. if (LOWORD(wParam) == IDCANCEL)
  226. {
  227. if (AnyConnected())
  228. SendMessage(hwnd, WM_COMMAND, IDDISCONNECT, 0);
  229. // Should call DestroyWindow() any time we now postquitmessage
  230. PostQuitMessage(0);
  231. return TRUE;
  232. }
  233. if (LOWORD(wParam) == IDOK)
  234. {
  235. // IDOK is the "Connect" button.
  236. LoadString(NULL, IDS_CONNECTALL, psDisplayString,
  237. MAX_DISPLAY_STRING_LENGTH);
  238. SetWindowText(hErrorText, psDisplayString);
  239. GetWindowText(hEditBox, psEditText, MAX_EDIT_TEXT_LENGTH);
  240. if (DoConnect(psEditText, hwnd) != 0) {
  241. LoadString(NULL, IDS_ERRORDOINGCONNECT, psDisplayString,
  242. MAX_DISPLAY_STRING_LENGTH);
  243. SetWindowText(hErrorText, psDisplayString);
  244. }
  245. UpdateButtons(hwnd);
  246. return TRUE;
  247. }
  248. if (LOWORD(wParam) == IDDISCONNECT)
  249. {
  250. int i;
  251. LoadString(NULL, IDS_DISCONNECTALL, psDisplayString,
  252. MAX_DISPLAY_STRING_LENGTH);
  253. SetWindowText(hErrorText, psDisplayString);
  254. // Close all connected sockets and update button state
  255. for (i = 0; i < MAX_CONNECTIONS; i++) {
  256. if (g_cs[i].state == STATE_CONNECTED) {
  257. int err; // Used for debugging
  258. // For some reason, we have to shut down in some cases or
  259. // else the server will not know that the client has
  260. // disconnected
  261. err = shutdown(g_cs[i].sock, SD_BOTH);
  262. err = closesocket(g_cs[i].sock);
  263. LoadString(NULL, IDS_NOTCONNECTED, psDisplayString,
  264. MAX_DISPLAY_STRING_LENGTH);
  265. SetWindowText(g_cs[i].hStatusText, psDisplayString);
  266. g_cs[i].state = STATE_DISCONNECTED;
  267. }
  268. }
  269. UpdateButtons(hwnd);
  270. return TRUE;
  271. }
  272. return 0;
  273. case WM_CREATE:
  274. break;
  275. case WM_TIMER:
  276. if (!AllConnected())
  277. PostMessage(hwnd, WM_COMMAND, IDOK, 0);
  278. break;
  279. case WM_SYSKEYDOWN:
  280. // NOTE INTENTIONAL FALLTHROUGH!
  281. case WM_KEYDOWN:
  282. if (wParam == VK_TAB) {
  283. if (!AllConnected()) {
  284. SetFocus(g_hwnd[0]);
  285. SendMessage(g_hwnd[0], EM_SETSEL, 0, -1);
  286. } else {
  287. SetFocus(g_hwnd[2]);
  288. }
  289. }
  290. if (wParam == VK_RETURN) {
  291. if (GetFocus() == g_hwnd[3])
  292. SendMessage(hwnd, WM_COMMAND, IDCANCEL, 0);
  293. else if (GetFocus() == g_hwnd[2])
  294. SendMessage(hwnd, WM_COMMAND, IDDISCONNECT, 0);
  295. else
  296. SendMessage(hwnd, WM_COMMAND, IDOK, 0);
  297. }
  298. break;
  299. case WM_Socket:
  300. switch (WSAGETSELECTEVENT(lParam)) {
  301. case FD_CLOSE:
  302. {
  303. int i;
  304. i = GetIndexFromSocket((SOCKET) wParam);
  305. if (i == -1) {
  306. LoadString(NULL, IDS_SOCKNOTFOUND, psDisplayString,
  307. MAX_DISPLAY_STRING_LENGTH);
  308. SetWindowText(hErrorText, psDisplayString);
  309. break;
  310. }
  311. closesocket(g_cs[i].sock);
  312. LoadString(NULL, IDS_SERVERENDEDCONN, psDisplayString,
  313. MAX_DISPLAY_STRING_LENGTH);
  314. SetWindowText(g_cs[i].hStatusText,
  315. psDisplayString);
  316. g_cs[i].state = STATE_DISCONNECTED;
  317. UpdateButtons(hwnd);
  318. break;
  319. }
  320. case FD_READ:
  321. {
  322. // TODO: try using just one buffer or at least make these good names
  323. char psInputDataRead[BUFSIZE];
  324. TCHAR psInputDataReadT[BUFSIZE];
  325. TCHAR debugString[200];
  326. TCHAR *psBaseScriptName;
  327. TCHAR *psUserName;
  328. int n;
  329. int i; // index into our connectionstatus structure
  330. i = GetIndexFromSocket((SOCKET) wParam);
  331. if (i == -1) {
  332. LoadString(NULL, IDS_CANTLOCATESOCKINFO, psDisplayString,
  333. MAX_DISPLAY_STRING_LENGTH);
  334. SetWindowText(hErrorText, psDisplayString);
  335. return FALSE;
  336. }
  337. LoadString(NULL, IDS_DATAREADY, psDisplayString,
  338. MAX_DISPLAY_STRING_LENGTH);
  339. SetWindowText(g_cs[i].hStatusText, psDisplayString);
  340. n = recv(g_cs[i].sock, psInputDataRead, sizeof(
  341. psInputDataRead), 0);
  342. if (n == SOCKET_ERROR) {
  343. LoadString(NULL, IDS_SOCKERR, psDisplayString,
  344. MAX_DISPLAY_STRING_LENGTH);
  345. SetWindowText(g_cs[i].hStatusText,psDisplayString);
  346. } else {
  347. CopyStrToTStr(psInputDataReadT, psInputDataRead, BUFSIZE);
  348. psInputDataReadT[n] = 0; // null terminate
  349. // check for client auto-update command
  350. // TODO: what if recv returns gibberish or 0?
  351. if (_tcsncmp(psInputDataReadT, _T("update"),
  352. (n >= 6) ? 6 : n) == 0) {
  353. LoadString(NULL, IDS_UPDATINGCLIENT, psDisplayString,
  354. MAX_DISPLAY_STRING_LENGTH);
  355. SetWindowText(hErrorText, psDisplayString);
  356. if (_spawnl(_P_NOWAIT, "update.cmd", "update.cmd", 0)
  357. == -1) {
  358. LoadString(NULL, IDS_CANTRUNUPDATE, psDisplayString,
  359. MAX_DISPLAY_STRING_LENGTH);
  360. SetWindowText(hErrorText, psDisplayString);
  361. break;
  362. } else {
  363. // the client update script has been successfully
  364. // initiated
  365. // Terminate self
  366. PostQuitMessage(0);
  367. return TRUE;
  368. }
  369. }
  370. // check for reboot command
  371. if (_tcsncmp(psInputDataReadT, _T("reboot"), (n >= 6) ? 6 : n)
  372. == 0) {
  373. // If we receive more than one reboot command,
  374. // ignore the extras
  375. if (g_dontreboot == 1)
  376. return TRUE;
  377. LoadString(NULL, IDS_REBOOTINGCLIENT, psDisplayString,
  378. MAX_DISPLAY_STRING_LENGTH);
  379. SetWindowText(hErrorText, psDisplayString);
  380. if (_spawnl(_P_WAIT, "reboot.cmd", "reboot.cmd", 0)
  381. == -1) {
  382. LoadString(NULL, IDS_ERRORREBOOTING, psDisplayString,
  383. MAX_DISPLAY_STRING_LENGTH);
  384. SetWindowText(hErrorText, psDisplayString);
  385. break;
  386. } else {
  387. // Disable further reboots
  388. g_dontreboot = 1;
  389. PostQuitMessage(0);
  390. return TRUE;
  391. }
  392. }
  393. // If it's not a command, then it's a run script command
  394. // in our wire format. See robosrv code for what that is.
  395. _tcstok(psInputDataReadT, _T("/")); // Terminate with NULL
  396. psBaseScriptName = _tcstok(0, _T("/")); // Get the script name
  397. psUserName = _tcstok(0, _T("/")); // Get the user name to
  398. // replace the template name
  399. if (psBaseScriptName == 0) {
  400. LoadString(NULL, IDS_ERRGETTINGSTUFF, psDisplayString,
  401. MAX_DISPLAY_STRING_LENGTH);
  402. SetWindowText(g_cs[i].hStatusText, psDisplayString);
  403. break;
  404. }
  405. if (psUserName == 0) {
  406. LoadString(NULL, IDS_ERRGETTINGUSERNAME, psDisplayString,
  407. MAX_DISPLAY_STRING_LENGTH);
  408. SetWindowText(g_cs[i].hStatusText, psDisplayString);
  409. break;
  410. }
  411. // Now we prepare to run a batch file on the robocli
  412. // machine called "runscript.bat".
  413. LoadString(NULL, IDS_NOWRUNNING, psDisplayString,
  414. MAX_DISPLAY_STRING_LENGTH);
  415. wsprintf(debugString, psDisplayString, psBaseScriptName,
  416. psInputDataReadT);
  417. if (_tspawnl(_P_NOWAIT, _T("runscript.bat"), _T("runscript.bat"),
  418. psBaseScriptName, psInputDataReadT, psUserName, NULL) == -1) {
  419. LoadString(NULL, IDS_ERRRUNNING, psDisplayString,
  420. MAX_DISPLAY_STRING_LENGTH);
  421. wsprintf(debugString, psDisplayString,
  422. psBaseScriptName, psInputDataReadT);
  423. if (send(g_cs[i].sock, "errorsmclient",
  424. (int) strlen("errorsmclient") + 1, 0)
  425. == SOCKET_ERROR) {
  426. LoadString(NULL, IDS_SENDERRSENDINGSMERR,
  427. psDisplayString,
  428. MAX_DISPLAY_STRING_LENGTH);
  429. strcpy(debugString, psDisplayString);
  430. }
  431. } else {
  432. if (send(g_cs[i].sock, "success",
  433. (int) strlen("success") + 1, 0) == SOCKET_ERROR) {
  434. LoadString(NULL, IDS_SENDERRSENDINGSUCCESS,
  435. psDisplayString,
  436. MAX_DISPLAY_STRING_LENGTH);
  437. strcpy(debugString, psDisplayString);
  438. }
  439. }
  440. SetWindowText(g_cs[i].hStatusText, debugString);
  441. }
  442. return TRUE;
  443. }
  444. }
  445. break;
  446. }
  447. return DefWindowProc(hwnd, iMsg, wParam, lParam);
  448. }
  449. // Takes the command line string as an argument and returns a pointer
  450. // inside that string of the server name, NULL if there is no such string.
  451. // Pops up a messagebox on error
  452. TCHAR *GetCommandLineArg(TCHAR *psCommandLine) {
  453. TCHAR *psCurrPtr = psCommandLine;
  454. TCHAR *retval;
  455. TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
  456. TCHAR psDisplayTitleString[MAX_DISPLAY_STRING_LENGTH];
  457. if (*psCurrPtr == '\"') {
  458. psCurrPtr++; // skip that character
  459. // Handle if the first arg is quoted
  460. while ((*psCurrPtr != 0) && (*psCurrPtr != '\"'))
  461. psCurrPtr++;
  462. // then skip the " character
  463. if (*psCurrPtr == '\"')
  464. psCurrPtr++;
  465. } else {
  466. // go forward in the array until you get a ' ' or until NULL
  467. while((*psCurrPtr != 0) && (*psCurrPtr != ' '))
  468. psCurrPtr++;
  469. }
  470. // skip spaces
  471. while(*psCurrPtr == ' ')
  472. psCurrPtr++;
  473. // if the character is NULL, return NULL (no args)
  474. if (*psCurrPtr == 0)
  475. return 0;
  476. // now, check that the next three are "-s:" and then non-null,
  477. if (_tcsncmp(psCurrPtr, _T("-s:"), 3) != 0)
  478. goto SHOWMSGBOX;
  479. // and that there isn't another argument afterward
  480. // but first, store retval in case it's ok
  481. retval = &psCurrPtr[3];
  482. if ((*retval == 0) || (*retval == ' '))
  483. goto SHOWMSGBOX;
  484. while ((*psCurrPtr != 0) && (*psCurrPtr != ' '))
  485. psCurrPtr++;
  486. if (*psCurrPtr != 0)
  487. goto SHOWMSGBOX;
  488. // return the pointer to that non-null thing
  489. return retval; // I'm not going to allow a servername to be quoted
  490. SHOWMSGBOX:
  491. LoadString(NULL, IDS_ROBOCLI_SYNTAX, psDisplayString,
  492. MAX_DISPLAY_STRING_LENGTH);
  493. LoadString(NULL, IDS_ROBOCLI_SYNTAX_TITLE, psDisplayTitleString,
  494. MAX_DISPLAY_STRING_LENGTH);
  495. MessageBox(0, psDisplayString, psDisplayTitleString, 0);
  496. return NULL;
  497. }
  498. // Tries to connect all sockets not in the connected (STATE_CONNECTED) state
  499. // Returns nonzero on error. Responsible for setting status lines
  500. int DoConnect(TCHAR *psServerName, HWND hWnd) {
  501. struct hostent *pSrv_info;
  502. struct sockaddr_in addr;
  503. int i, cData;
  504. HWND hErrorText;
  505. TCHAR debugString[100];
  506. char psNumConns[6];
  507. char psServerNameA[MAX_EDIT_TEXT_LENGTH];
  508. TCHAR psDisplayString[MAX_DISPLAY_STRING_LENGTH];
  509. hErrorText = GetDlgItem(hWnd, IDC_ERRORTEXT);
  510. CopyTStrToStr(psServerNameA, psServerName, MAX_EDIT_TEXT_LENGTH);
  511. pSrv_info = gethostbyname(psServerNameA);
  512. if (pSrv_info == NULL || pSrv_info->h_length > sizeof(addr.sin_addr) ) {
  513. LoadString(NULL, IDS_UNKNOWNHOST, psDisplayString,
  514. MAX_DISPLAY_STRING_LENGTH);
  515. SetWindowText(hErrorText, psDisplayString);
  516. goto err;
  517. }
  518. else {
  519. memcpy(&addr.sin_addr, pSrv_info->h_addr, pSrv_info->h_length);
  520. }
  521. addr.sin_family = AF_INET;
  522. addr.sin_port = htons(DEFAULT_PORT);
  523. // "First time through the loop" -- make a connection and set
  524. // nNumConnections
  525. if (g_nNumConnections == 0) {
  526. // nNumConnections == 0 indicates no connection has ever been made
  527. // If it is nonzero it indicates the total number of connections the
  528. // RoboClient is to make
  529. if (g_cs[0].state != STATE_CONNECTED) {
  530. LoadString(NULL, IDS_MAKINGINITCONN, psDisplayString,
  531. MAX_DISPLAY_STRING_LENGTH);
  532. SetWindowText(hErrorText, psDisplayString);
  533. LoadString(NULL, IDS_CONNECTING, psDisplayString,
  534. MAX_DISPLAY_STRING_LENGTH);
  535. SetWindowText(g_cs[0].hStatusText, psDisplayString);
  536. g_cs[0].sock = socket(AF_INET, SOCK_STREAM, 0);
  537. if (g_cs[0].sock == INVALID_SOCKET) {
  538. LoadString(NULL, IDS_SOCKETERR, psDisplayString,
  539. MAX_DISPLAY_STRING_LENGTH);
  540. SetWindowText(g_cs[0].hStatusText, psDisplayString);
  541. goto err;
  542. }
  543. if (connect(g_cs[0].sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
  544. // This is duplicated functionality
  545. LoadString(NULL, IDS_UNABLETOCONNECT, psDisplayString,
  546. MAX_DISPLAY_STRING_LENGTH);
  547. _sntprintf(debugString, 100, psDisplayString, psServerName);
  548. SetWindowText(g_cs[0].hStatusText, debugString);
  549. goto err;
  550. }
  551. // Set nNumConnections
  552. cData = recv(g_cs[0].sock, psNumConns, sizeof(psNumConns), 0);
  553. // psNumConns is an array but we should really only receive one
  554. // byte, so...
  555. g_nNumConnections = psNumConns[0] - '0';
  556. if ((g_nNumConnections < 1)
  557. || (g_nNumConnections > MAX_CONNECTIONS)) {
  558. LoadString(NULL, IDS_INVALIDCONNECTIONSFROMSERVER,
  559. psDisplayString, MAX_DISPLAY_STRING_LENGTH);
  560. SetWindowText(hErrorText, psDisplayString);
  561. g_nNumConnections = 0;
  562. }
  563. LoadString(NULL, IDS_CONNECTEDNCONNECTIONS, psDisplayString,
  564. MAX_DISPLAY_STRING_LENGTH);
  565. _sntprintf(debugString, SIZEOF_ARRAY(debugString), psDisplayString, g_nNumConnections);
  566. debugString[SIZEOF_ARRAY(debugString) - 1] = 0;
  567. SetWindowText(hErrorText, debugString);
  568. // Disable status lines for all unused connections,
  569. // enable for used ones
  570. for (i = 0; i < g_nNumConnections; i++) {
  571. EnableWindow(g_cs[i].hStatusText, TRUE);
  572. }
  573. // disable connections up to 5
  574. for (i = g_nNumConnections; i < MAX_CONNECTIONS_IN_UI; i++) {
  575. EnableWindow(g_cs[i].hStatusText, FALSE);
  576. }
  577. g_cs[0].state = STATE_CONNECTED;
  578. WSAAsyncSelect(g_cs[0].sock, hWnd, WM_Socket, FD_READ | FD_CLOSE);
  579. LoadString(NULL, IDS_CONNECTED, psDisplayString,
  580. MAX_DISPLAY_STRING_LENGTH);
  581. _sntprintf(debugString, SIZEOF_ARRAY(debugString), psDisplayString, g_cs[0].sock);
  582. debugString[SIZEOF_ARRAY(debugString) - 1] = 0;
  583. SetWindowText(g_cs[0].hStatusText, debugString);
  584. } else {
  585. // extremely bad
  586. }
  587. }
  588. // start from 0 because what if the initial connection was disconnected
  589. // and we are trying to reconnect?
  590. for (i = 0; i < g_nNumConnections; i++) {
  591. if (g_cs[i].state != STATE_CONNECTED) {
  592. // TODO: this is duplicated functionality
  593. LoadString(NULL, IDS_CONNECTING, psDisplayString,
  594. MAX_DISPLAY_STRING_LENGTH);
  595. SetWindowText(g_cs[i].hStatusText, psDisplayString);
  596. g_cs[i].sock = socket(AF_INET, SOCK_STREAM, 0);
  597. if (g_cs[i].sock == INVALID_SOCKET) {
  598. LoadString(NULL, IDS_SOCKETERR, psDisplayString,
  599. MAX_DISPLAY_STRING_LENGTH);
  600. SetWindowText(g_cs[i].hStatusText, psDisplayString);
  601. goto err;
  602. }
  603. if (connect(g_cs[i].sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
  604. LoadString(NULL, IDS_UNABLETOCONNECT, psDisplayString,
  605. MAX_DISPLAY_STRING_LENGTH);
  606. _sntprintf(debugString, 100, psDisplayString, psServerName);
  607. SetWindowText(g_cs[i].hStatusText, debugString);
  608. goto err;
  609. }
  610. // Ignore nNumConnections
  611. cData = recv(g_cs[i].sock, psNumConns, sizeof(psNumConns), 0);
  612. g_cs[i].state = STATE_CONNECTED;
  613. WSAAsyncSelect(g_cs[i].sock, hWnd, WM_Socket, FD_READ | FD_CLOSE);
  614. LoadString(NULL, IDS_CONNECTED, psDisplayString,
  615. MAX_DISPLAY_STRING_LENGTH);
  616. _sntprintf(debugString, 100, psDisplayString, g_cs[i].sock);
  617. SetWindowText(g_cs[i].hStatusText, debugString);
  618. }
  619. }
  620. return 0;
  621. err:
  622. return -1;
  623. }
  624. // predicate functions
  625. // Are all connections up to the number requested connected?
  626. int AllConnected() {
  627. int i;
  628. if (g_nNumConnections == 0)
  629. return 0;
  630. for (i = 0; i < g_nNumConnections; i++) {
  631. if (g_cs[i].state == STATE_DISCONNECTED)
  632. return 0;
  633. }
  634. return 1;
  635. }
  636. // Are any connections connected?
  637. int AnyConnected() {
  638. int i;
  639. for (i = 0; i < g_nNumConnections; i++) {
  640. if (g_cs[i].state == STATE_CONNECTED)
  641. return 1;
  642. }
  643. return 0;
  644. }
  645. // None connected?
  646. int NoneConnected() {
  647. return !AnyConnected();
  648. }
  649. // Extremely useful function to get the robolient index (i.e., 0-based index
  650. // in the status line and in our data structure). Returns -1 on error
  651. int GetIndexFromSocket(SOCKET s) {
  652. int i;
  653. for (i = 0; i < MAX_CONNECTIONS; i++) {
  654. if (g_cs[i].state == STATE_CONNECTED)
  655. if (g_cs[i].sock == s)
  656. return i;
  657. }
  658. return -1;
  659. }
  660. // Update the state of the buttons based on the states of the connections
  661. int UpdateButtons(HWND hwnd) {
  662. HWND hConnectButton;
  663. HWND hDisconnectButton;
  664. HWND hEditBox;
  665. // TODO: init all dlg items at once and never check again
  666. hConnectButton = GetDlgItem(hwnd, IDOK);
  667. hDisconnectButton = GetDlgItem(hwnd, IDDISCONNECT);
  668. hEditBox = GetDlgItem(hwnd, IDC_SERVNAMEEDIT);
  669. if (AnyConnected()) {
  670. EnableWindow(hDisconnectButton, TRUE);
  671. EnableWindow(hEditBox, FALSE); // Can't connect to different servers
  672. }
  673. if (AllConnected()) {
  674. EnableWindow(hConnectButton, FALSE);
  675. SetFocus(g_hwnd[2]);
  676. }
  677. if (NoneConnected()) {
  678. EnableWindow(hConnectButton, TRUE);
  679. EnableWindow(hEditBox, TRUE);
  680. EnableWindow(hDisconnectButton, FALSE);
  681. g_nNumConnections = 0; // This means we can change it on next connect
  682. g_dontreboot = 0; // reset this if none is connected anymore
  683. SetFocus(g_hwnd[0]);
  684. SendMessage(g_hwnd[0], EM_SETSEL, 0, -1);
  685. }
  686. return 0;
  687. }
  688. // Subclass procedure to handle tabs
  689. LRESULT CALLBACK TabProc(HWND hwnd, UINT Msg,
  690. WPARAM wParam, LPARAM lParam) {
  691. int i;
  692. // Find the id of the hwnd
  693. for (i = 0; i < NUM_TABBED_ITEMS; i++) {
  694. if (g_hwnd[i] == hwnd)
  695. break;
  696. }
  697. switch (Msg) {
  698. case WM_KEYDOWN:
  699. if (wParam == VK_TAB) {
  700. int newItem = i;
  701. // Find the next or previous enabled item
  702. do {
  703. newItem = (newItem + (GetKeyState(VK_SHIFT) < 0 ?
  704. NUM_TABBED_ITEMS - 1 : 1)) % NUM_TABBED_ITEMS;
  705. } while (IsWindowEnabled(g_hwnd[newItem]) == 0);
  706. // set the focus to the next or previous item
  707. SetFocus(g_hwnd[newItem]);
  708. // if the control is an edit box control, select all text
  709. if (newItem == 0)
  710. SendMessage(g_hwnd[newItem], EM_SETSEL, 0, -1);
  711. }
  712. if (wParam == VK_ESCAPE) {
  713. SendMessage(GetParent(hwnd), WM_COMMAND, IDCANCEL, 0);
  714. }
  715. if (wParam == VK_RETURN) {
  716. SendMessage(GetParent(hwnd), WM_KEYDOWN, wParam, lParam);
  717. }
  718. break;
  719. }
  720. return CallWindowProc(g_OldProc[i], hwnd, Msg, wParam, lParam);
  721. }
  722. #pragma warning (pop)