1 cs130_ryutaro 1.1 #include <stdio.h>
2 #include "winsock2.h"
3 #include "windows.h"
4
5 /* The #pragma directives offer a way for each compiler to offer machine-
6 and operating-system-specific features while retaining overall
7 compatibility with the C and C++ languages.
8 The next line places a library-search record, ws2_32, in the object file. */
9 #pragma comment ( lib, "ws2_32" )
10
11 /* constants */
12 #define ASYNC_EVENT (WM_USER +5) // the message we'll use for our async notification
13 #define PORT_NUM 27015
14 #define PACKET_SZ 32
15 #define FILE_SZ 100
16 #define SERVER_START_TIME 3000
17 #define MAX_WAIT_TIME 30000 // 30 seconds expressed in milliseconds
18 #define NUM_THREADS_TO_WAIT 2
19
20 enum // define all the exit codes for a thread
21 {
22 cs130_ryutaro 1.1 TH_START_ERROR = 1001,
23 TH_SOCKET_ERROR,
24 TH_SOCKADDR_ERROR,
25 TH_BIND_ERROR,
26 TH_CONNECT_ERROR,
27 TH_LISTEN_ERROR,
28 TH_ACCEPT_ERROR,
29 TH_SELECT_ERROR,
30 TH_RECV_ERROR,
31 TH_SEND_ERROR,
32 TH_WINDOW_ERROR,
33 TH_MSG_ERROR
34
35 } ThreadExitCodes;
36
37 /* static func prototypes */
38 static int WSAStartup_w( WORD wVersionRequired, LPWSADATA lpWSAData );
39 static SOCKET socket_w( int af, int type, int protocol );
40 static LRESULT CALLBACK WndProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
41 static HWND WineCreateWindow( HINSTANCE hInstance);
42 static void WineThreadCleanUp();
43 cs130_ryutaro 1.1 static void WineCreateFile();
44 static DWORD WINAPI WineRunServer();
45 static DWORD WINAPI WineRunClient();
46
47 /* struct def */
48 typedef struct
49 {
50 DWORD id;
51 HANDLE handle;
52 int bytesRecvd;
53 int bytesSent;
54 char packet[PACKET_SZ];
55 } thread_t;
56
57 /* global vars */
58 static char FileBuf[FILE_SZ];
59 static thread_t ServerThread;
60 static thread_t ClientThread;
61
62 /*
63 Wrapper for WSAStartup().
64 cs130_ryutaro 1.1 The WSAStartup function must be the first Windows Sockets function
65 called by an application or DLL. It allows an application or DLL to
66 specify the version of Windows Sockets required and retrieve details
67 of the specific Windows Sockets implementation.
68
69 Paremeters:
70 wVersionRequested
71 [in] Highest version of Windows Sockets support that the caller can use.
72 The high-order byte specifies the minor version (revision) number;
73 the low-order byte specifies the major version number.
74 lpWSAData
75 [out] Pointer to the WSADATA data structure that is to receive details of
76 the Windows Sockets implementation.
77
78 Error code | Meaning
79 WSASYSNOTREADY | Indicates that the underlying network subsystem is
80 not ready for network communication.
81 WSAVERNOTSUPPORTED | The version of Windows Sockets support requested is
82 not provided by this particular Windows Sockets
83 implementation.
84 WSAEINPROGRESS | A blocking Windows Sockets 1.1 operation is in progress.
85 cs130_ryutaro 1.1 WSAEPROCLIM | Limit on the number of tasks supported by the Windows
86 Sockets implementation has been reached.
87 WSAEFAULT | The lpWSAData is not a valid pointer.
88 */
89 static int WSAStartup_w( WORD wVersionRequired, LPWSADATA lpWSAData )
90 {
91 int errCode = 0;
92
93 // WSAVERNOTSUPPORTED test
94 WSADATA tmpLpWSAData;
95 errCode = WSAStartup( MAKEWORD( 0, 0 ), &tmpLpWSAData );
96 if( errCode != WSAVERNOTSUPPORTED ) {
97
98 // print out the Error Code to be thorough
99 printf( "%d: WSAVERNOTSUPPORTED test failed with error code: %d\n", __LINE__, errCode );
100 errCode = 0;
101 }
102
103 // WSEFAULT test
104 errCode = WSAStartup( wVersionRequired, ( LPWSADATA ) 0 );
105 if( errCode != WSAEFAULT ) {
106 cs130_ryutaro 1.1
107 // print out the Error Code to be thorough
108 printf( "%d: WSAFAULT test failed with error code: %d\n", __LINE__, errCode );
109 errCode = 0;
110 }
111
112 // lastly regular case - should succeed
113 errCode = WSAStartup( wVersionRequired, lpWSAData );
114 if( errCode != 0 ) {
115
116 // print out the Error Code to be thorough
117 errCode = WSAGetLastError();
118 printf( "%d: WSAStartup() failed with error code: %d\n", __LINE__, errCode );
119 }
120
121 return errCode;
122 }
123
124 /*
125 Wrapper for socket().
126 socket() function creates a socket that is bound to a specific service provider.
127 cs130_ryutaro 1.1
128 Parameters:
129 af
130 [in] Address family specification.
131 type
132 [in] Type specification for the new socket. Types are:
133 SOCK_STREAM - Uses TCP
134 SOCK_DGRAM - Uses UDP
135 protocol
136 [in] Protocol to be used with the socket that is specific to the indicated address
137 family.
138
139 If no error occurs, socket returns a descriptor referencing the new socket. Otherwise,
140 a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by
141 calling WSAGetLastError.
142
143 Error code | Meaning
144 WSANOTINITIALISED | A successful WSAStartup call must occur before using this function.
145 WSAENETDOWN | The network subsystem or the associated service provider has failed.
146 WSAEAFNOSUPPORT | The specified address family is not supported.
147 WSAEINPROGRESS | A blocking Windows Sockets 1.1 call is in progress, or the service
148 cs130_ryutaro 1.1 provider is still processing a callback function.
149 WSAEMFILE | No more socket descriptors are available.
150 WSAENOBUFS | No buffer space is available. The socket cannot be created.
151 WSAEPROTONOSUPPORT | The specified protocol is not supported.
152 WSAEPROTOTYPE | The specified protocol is the wrong type for this socket.
153 WSAESOCKTNOSUPPORT | The specified socket type is not supported in this address family.
154
155 NOTE: WSAEPROTOTYPE tried to do test for this error by specifying type=SOCK_DGRAM and
156 protocol=IPPROTO_TCP but the return error code was WSEPROTONOSUPPORT
157 */
158 static SOCKET socket_w( int af, int type, int protocol )
159 {
160 // Create a socket.
161 SOCKET m_socket;
162
163 // WSAEAFNOSUPPORT test
164 m_socket = socket( -999, SOCK_STREAM, IPPROTO_TCP );
165 if( m_socket != INVALID_SOCKET || WSAGetLastError() != WSAEAFNOSUPPORT ){
166 printf( "%d: WSAEAFNOSUPPORT test failed: %d %ld\n", __LINE__, m_socket, WSAGetLastError() );
167 }
168
169 cs130_ryutaro 1.1 // WSAEPROTONOSUPPORT test
170 m_socket = socket( AF_INET, SOCK_STREAM, -999 );
171 if( m_socket != INVALID_SOCKET || WSAGetLastError() != WSAEPROTONOSUPPORT ){
172 printf( "%d: WSAEPROTONOSUPPORT test failed: %d %ld\n", __LINE__, m_socket, WSAGetLastError() );
173 }
174
175 // WSAESOCKTNOSUPPORT test
176 m_socket = socket( AF_INET, -999, IPPROTO_TCP );
177 if( m_socket != INVALID_SOCKET || WSAGetLastError() != WSAESOCKTNOSUPPORT ){
178 printf( "%d: WSAEPROTONOSUPPORT test failed: %d %ld\n", __LINE__, m_socket, WSAGetLastError() );
179 }
180
181 m_socket = socket( af, type, protocol );
182 if( m_socket == INVALID_SOCKET ){
183 printf( "%d: Error at socket(): %ld\n", __LINE__, WSAGetLastError() );
184 }
185
186 return m_socket;
187 }
188
189 /*
190 cs130_ryutaro 1.1 Call back event handler for asynchronous client
191 */
192 static LRESULT CALLBACK WndProcedure( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
193 {
194
195 switch( msg )
196 {
197 case ASYNC_EVENT:
198 int eventCode = WSAGETSELECTEVENT( lParam );
199 SOCKET socket = (SOCKET) wParam;
200 SOCKET acceptSock;
201 int bytesRecv = 0;
202 int i = 0, j = 0;
203 int ret;
204 char* buf;
205 if( GetCurrentThreadId() == ServerThread.id )
206 buf = ServerThread.packet;
207 else
208 buf = ClientThread.packet;
209
210 switch( eventCode )
211 cs130_ryutaro 1.1 {
212 case FD_ACCEPT:
213 printf( "%d accept called\n", GetCurrentThreadId() );
214 acceptSock = accept( socket, NULL, NULL );
215 if( acceptSock == INVALID_SOCKET ){
216 printf( "%d: accept error %ld\n", __LINE__, WSAGetLastError() );
217 closesocket( socket );
218 ExitThread( TH_ACCEPT_ERROR );
219 }
220 printf( "%d: connection accepted\n", GetCurrentThreadId() );
221 break;
222
223 case FD_CONNECT:
224
225 // a call to connect() has completed
226 printf("%d CONNECTED!\n", GetCurrentThreadId() );
227 break;
228
229 case FD_CLOSE:
230
231 // a connection has been closed
232 cs130_ryutaro 1.1 closesocket( socket );
233 printf( "%d: Closed socket\n", GetCurrentThreadId());
234 WSACleanup();
235 ExitThread( 0 );
236 break;
237
238 case FD_READ:
239
240 // WinSock has data waiting to be read
241 bytesRecv = recv( socket, buf, PACKET_SZ, 0 );
242
243 printf( "%d received %d bytes: ", GetCurrentThreadId(), bytesRecv );
244 while( i < PACKET_SZ && buf[i] != (char)EOF ){
245 printf( "%c", buf[i] );
246 i++;
247 }
248 printf( "\n" );
249
250 // Now send it back if you're the server
251 if( GetCurrentThreadId() == ServerThread.id ){
252
253 cs130_ryutaro 1.1 if( send( socket, buf, bytesRecv, 0 ) == SOCKET_ERROR ){
254
255 // check if the network buffer is full and can send no more
256 // data. If so then break from the loop
257 if( WSAGetLastError() == WSAEWOULDBLOCK ){
258 // break from the loop – buffer is full
259 printf( "woud block send!\n" );
260 break;
261 }
262 else{ // another error
263 printf(" some send error\n" );
264 return 0;
265 }
266 }
267
268 printf( "%d sent back %d bytes: ", GetCurrentThreadId(), bytesRecv );
269 j = 0;
270 while( j < bytesRecv ){
271 printf( "%c", buf[j] );
272 j++;
273 }
274 cs130_ryutaro 1.1 printf( "\n" );
275
276 }
277 else{
278
279 // Client: close the connection when it received back FILE_SZ
280 ClientThread.bytesRecvd += bytesRecv;
281 if( ClientThread.bytesRecvd >= FILE_SZ ){
282 printf( "%d closing socket\n", GetCurrentThreadId() );
283 closesocket( socket );
284 WSACleanup();
285 ResumeThread( ServerThread.handle );
286 ExitThread( 0 );
287 }
288 }
289 break;
290
291 case FD_WRITE:
292
293 // don't do anything here for server
294 if( GetCurrentThreadId() == ServerThread.id )
295 cs130_ryutaro 1.1 break;
296
297 // enter an infinite loop
298 BOOL notDone = TRUE;
299 while( notDone ){
300
301 // determine how many bytes to send
302 int bytesToSend = 0;
303 if( sizeof( FileBuf ) - ClientThread.bytesSent < PACKET_SZ ){
304
305 // the bytes remaining to send is smaller than packet size
306 bytesToSend = (int) ( sizeof( FileBuf ) - ClientThread.bytesSent );
307 notDone = FALSE;
308
309 }
310 else{
311
312 // the bytes remaining is greater than or equal to PACKET_SZ
313 bytesToSend = PACKET_SZ;
314
315 }
316 cs130_ryutaro 1.1
317 memcpy( buf, FileBuf + ClientThread.bytesSent, bytesToSend );
318 ClientThread.bytesSent += bytesToSend;
319
320 // send the packet off to the Server if it is filled
321 if( send( socket, buf, bytesToSend, 0 ) == SOCKET_ERROR ){
322
323 // check if the network buffer is full and can send no more
324 // data. If so then break from the loop
325 if( WSAGetLastError() == WSAEWOULDBLOCK ){
326 // break from the loop – buffer is full
327 break;
328 }
329 else{ // another error
330 return 0;
331 }
332 }
333
334 printf( "%d sent: ", GetCurrentThreadId() );
335 j = 0;
336 while( j < PACKET_SZ && buf[j] != (char)EOF ){
337 cs130_ryutaro 1.1 printf( "%c", buf[j] );
338 j++;
339 }
340 printf( "\n" );
341 }
342 break;
343 }
344 }
345 return DefWindowProc( hWnd, msg, wParam, lParam );
346 }
347
348 /*
349 Creates a hidden window object.
350
351 input:
352 none
353 output:
354 window handle
355 */
356 static HWND WineCreateWindow( HINSTANCE hInstance )
357 {
358 cs130_ryutaro 1.1 // initialize the window class attributes.
359 WNDCLASS *windowClass;
360 windowClass = new WNDCLASS;
361 windowClass->lpszClassName = "Hidden_Winsock_Window";
362 windowClass->style = CS_HREDRAW | CS_VREDRAW;
363 windowClass->lpfnWndProc = WndProcedure;
364 windowClass->cbClsExtra = 0;
365 windowClass->cbWndExtra = 0;
366 windowClass->hInstance = hInstance;
367 windowClass->hIcon = NULL;
368 windowClass->hCursor = NULL;
369 windowClass->hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
370 windowClass->lpszMenuName = NULL;
371 RegisterClass(windowClass);
372
373 // create window
374 return CreateWindow(
375 "Hidden_Winsock_Window",
376 "Winsock Window",
377 WS_OVERLAPPEDWINDOW,
378 CW_USEDEFAULT,
379 cs130_ryutaro 1.1 CW_USEDEFAULT,
380 CW_USEDEFAULT,
381 CW_USEDEFAULT,
382 NULL,
383 NULL,
384 hInstance,
385 NULL
386 );
387 }
388
389 /* server */
390 static DWORD WINAPI WineRunServer()
391 {
392 // Retrieve an instance
393 HINSTANCE hInstance = GetModuleHandle( NULL );
394
395 // Create a window
396 HWND hwnd = WineCreateWindow( hInstance );
397 if( hwnd == NULL ){
398 printf( "%d: Window could not be created %ld\n", __LINE__, GetLastError() );
399 ExitThread( TH_WINDOW_ERROR );
400 cs130_ryutaro 1.1 }
401
402 // start winsock
403 WSADATA wsaData;
404 if( WSAStartup_w( MAKEWORD( 2, 2 ), &wsaData ) != 0 ){
405
406 // print out the Error Code to be thorough
407 printf( "%d: WSAStartup() failed with error code: %d\n", __LINE__, WSAGetLastError() );
408 ExitThread( TH_START_ERROR );
409 }
410
411 // Create a socket.
412 SOCKET sock = socket_w( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if( sock == INVALID_SOCKET ){
413 printf( "%d: socket error: %ld\n", __LINE__, WSAGetLastError() );
414 ExitThread( TH_SOCKET_ERROR );
415 }
416
417 // Bind the socket.
418 sockaddr_in sockAddr;
419 sockAddr.sin_family = AF_INET;
420 sockAddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
421 cs130_ryutaro 1.1 sockAddr.sin_port = htons( PORT_NUM );
422 if( bind( sock, (SOCKADDR*) &sockAddr, sizeof( sockAddr ) ) == SOCKET_ERROR ){
423 printf( "%d: bind failed weith error code %ld\n", __LINE__, WSAGetLastError() );
424 closesocket( sock );
425 ExitThread( TH_BIND_ERROR );
426
427 }
428
429 // Listen on the socket.
430 if ( listen( sock, 1 ) == SOCKET_ERROR ){
431 printf( "%d: listen error with error code %ld.\n", __LINE__, WSAGetLastError() );
432 closesocket( sock );
ExitThread( TH_LISTEN_ERROR );
}
433
434 // make the socket asynchronous and notify of read, write, connect and close events
435 if( WSAAsyncSelect( sock, hwnd, ASYNC_EVENT, FD_WRITE | FD_ACCEPT |FD_READ | FD_CLOSE ) == SOCKET_ERROR ){
436
437 printf( "%d: WSAAsyncSelect Failed %ld\n", __LINE__, WSAGetLastError() );
438 closesocket( sock );
439 ExitThread( TH_SELECT_ERROR );
440
441 }
442 cs130_ryutaro 1.1
443 printf( "listening and going to loop %d\n", GetCurrentThreadId() );
444
445 BOOL retVal;
446 MSG msg;
447 while( ( retVal = GetMessage( &msg, NULL, 0, 0 ) ) != 0 ){
448
449 if( retVal == -1 ){
450
451 // handle the error and possibly exit
452 printf( "%d: GetMessage error %ld\n", __LINE__, GetLastError() );
453 closesocket( sock );
454 ExitThread( TH_MSG_ERROR );
455 }
456
457 // suspend the other thread
458 SuspendThread( ClientThread.handle );
459
460 // Translate and dispatch the message
461 TranslateMessage( &msg );
462 DispatchMessage( &msg );
463 cs130_ryutaro 1.1
464 printf( "%d still here?\n", GetCurrentThreadId() );
465
466 ResumeThread( ClientThread.handle );
467
468 }
469
470 return 0;
471 }
472
473 /* client */
474 static DWORD WINAPI WineRunClient()
475 {
476 // Retrieve an instance
477 HINSTANCE hInstance = GetModuleHandle( NULL );
478
479 // Create a window
480 HWND hwnd = WineCreateWindow( hInstance );
481 if( hwnd == NULL ){
482 printf( "%d: Window could not be created %ld\n", __LINE__, GetLastError() );
483 ExitThread( TH_WINDOW_ERROR );
484 cs130_ryutaro 1.1 }
485
486 // start winsock
487 WSADATA wsaData;
488 if( WSAStartup_w( MAKEWORD( 2, 2 ), &wsaData ) != 0 ){
489
490 // print out the Error Code to be thorough
491 printf( "%d: WSAStartup() failed with error code: %d\n", __LINE__, WSAGetLastError() );
492 ExitThread( TH_START_ERROR );
493 }
494
495 // Create a socket. -- This is a TCP test
496 SOCKET sock = socket_w( AF_INET, SOCK_STREAM, IPPROTO_TCP );
497 if ( sock == INVALID_SOCKET ) {
498 printf( "%d: socket returned with error code: %ld\n", __LINE__, WSAGetLastError() );
499 ExitThread( TH_SOCKET_ERROR );
500 }
501
502 // make the socket asynchronous and notify of read, write, connect and close events
503 // this is the client socket
504 if( WSAAsyncSelect( sock, hwnd, ASYNC_EVENT, FD_WRITE | FD_CONNECT | FD_READ | FD_CLOSE ) == SOCKET_ERROR ){
505 cs130_ryutaro 1.1 printf( "%d: WSAAsyncSelect Failed %ld\n", __LINE__, WSAGetLastError() );
506 closesocket( sock );
507 ExitThread( TH_SELECT_ERROR );
508 }
509
510 /* connect */
511 sockaddr_in sockAddr;
512 sockAddr.sin_family = AF_INET;
513 sockAddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
514 sockAddr.sin_port = htons( PORT_NUM );
515 if( connect( sock, (SOCKADDR*) &sockAddr, sizeof( sockAddr ) ) == SOCKET_ERROR ){
516 if( WSAGetLastError() != WSAEWOULDBLOCK ){
517 printf( "%d: Failed to connect.: %d\n", __LINE__, WSAGetLastError() );
518 closesocket( sock );
519 ExitThread( TH_CONNECT_ERROR );
520 }
521 }
522
523 BOOL retVal;
524 MSG msg;
525 while( ( retVal = GetMessage( &msg, NULL, 0, 0 ) ) != 0 ){
526 cs130_ryutaro 1.1
527 if( retVal == -1 ){
528
529 // handle the error and possibly exit
530 printf( "%d: GetMessage error %ld\n", __LINE__, GetLastError() );
531 closesocket( sock );
532 ExitThread( TH_MSG_ERROR );
533 }
534
535 SuspendThread( ServerThread.handle );
536
537 // Translate and dispatch the message
538 TranslateMessage( &msg );
539 DispatchMessage( &msg );
540
541 //printf( "%d still here?\n", GetCurrentThreadId() );
542 ResumeThread( ServerThread.handle );
543 }
544 return 0;
545 }
546
547 cs130_ryutaro 1.1 /* close thread handles and finish up winsock dll */
548 static void WineThreadCleanUp()
549 {
550 if( ServerThread.handle )
551 CloseHandle( ServerThread.handle );
552
553 if( ClientThread.handle )
554 CloseHandle( ClientThread.handle );
555 }
556
557 /* creates the buffer that the client sends */
558 static void WineCreateFile()
559 {
560 /* initialize just in case */
561 memset( FileBuf, 0, sizeof( FileBuf ) );
562
563 /* just customize this any way you want */
564 memset( FileBuf, 'W', PACKET_SZ );
565 memset( FileBuf + PACKET_SZ, 'I', PACKET_SZ );
566 memset( FileBuf + ( 2 * PACKET_SZ ), 'N', PACKET_SZ );
567 memset( FileBuf + ( 3 * PACKET_SZ ), 'E', 3 );
568 cs130_ryutaro 1.1 FileBuf[FILE_SZ - 1] = (char) EOF;
569 }
570
571 void main()
572 {
573 // Create the file that we are going to send.
574 WineCreateFile();
575
576 // Initialize server and client threads
577 memset( &ServerThread, 0, sizeof( ServerThread ) );
578 memset( &ClientThread, 0, sizeof( ClientThread ) );
579
580 // create server thread
581 ServerThread.handle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)WineRunServer,
582 NULL, 0, &ServerThread.id );
583
584 if( ServerThread.handle == NULL ){
585
586 printf( "%d: CreateThread failed %d\n", __LINE__, GetLastError() );
587 WineThreadCleanUp();
588 return;
589 cs130_ryutaro 1.1 }
590
591 printf( "%d: server thread %d created\n", GetCurrentThreadId(), ServerThread.id );
592
593 // Wait for three seconds to let server start
594 DWORD waitRet = WaitForSingleObject( ServerThread.handle, SERVER_START_TIME );
595 if( waitRet == WAIT_FAILED ){
596
597 // WaitForSingleObject failed for whatever reason
598 printf( "%d: WaitForSingleObject failed with error code %d\n", __LINE__, GetLastError() );
599 WineThreadCleanUp();
600 return;
601
602 }
603
604 // get the exit code of the server - error if not STILL_ACTIVE
605 DWORD threadStatus = 0;
606 if( GetExitCodeThread( ServerThread.handle, &threadStatus ) == 0 ){
607
608 printf( "%d: GetExitCodeThread failed with error code %ld\n", __LINE__, GetLastError() );
609 WineThreadCleanUp();
610 cs130_ryutaro 1.1 return;
611
612 }
613
614 printf( "%d: server started successfully now creating client thread\n", GetCurrentThreadId() );
615
616 // Create client thread in suspended state
617 ClientThread.handle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)WineRunClient,
618 NULL, 0, &ClientThread.id );
619
620 if( ClientThread.handle == NULL ){
621
622 printf( "%d: CreateThread failed %d\n", __LINE__, GetLastError() );
623 WineThreadCleanUp();
624 return;
625 }
626
627 printf( "%d: client thread %d created. Now wait.\n", GetCurrentThreadId(), ClientThread.id );
628
629 // Wait for this server to suspsend itself
630 HANDLE threadHandles[NUM_THREADS_TO_WAIT] = { ServerThread.handle, ClientThread.handle };
631 cs130_ryutaro 1.1 waitRet = WaitForMultipleObjects( NUM_THREADS_TO_WAIT, threadHandles, TRUE, MAX_WAIT_TIME );
632 if( waitRet == WAIT_FAILED ){
633
634 // WaitForSingleObject failed for whatever reason
635 printf( "%d: WaitForMultipleObjects failed with error code %d\n", __LINE__, GetLastError() );
636 WineThreadCleanUp();
637 return;
638
639 }
640 else if( waitRet == WAIT_TIMEOUT ){
641
642 printf( "%d: Timed out while waiting for threads to finish\n", __LINE__ );
643 WineThreadCleanUp();
644 return;
645
646 }
647
648 // clean up the threads
649 WineThreadCleanUp();
650
651 printf( "%d: main thread exiting...\n", GetCurrentThreadId() );
652 cs130_ryutaro 1.1 while(1);
653 }
|