]> git.saurik.com Git - wxWidgets.git/blame - src/msw/gsockmsw.cpp
set error to GSOCK_TIMEOUT if the socket timed out (modified and extended patch 1303554)
[wxWidgets.git] / src / msw / gsockmsw.cpp
CommitLineData
b7ceceb1 1/* -------------------------------------------------------------------------
99d80019
JS
2 * Project: GSocket (Generic Socket)
3 * Name: gsockmsw.cpp
4 * Copyright: (c) Guilhem Lavaux
5 * Licence: wxWindows Licence
6 * Author: Guillermo Rodriguez Garcia <guille@iies.es>
7 * Purpose: GSocket GUI-specific MSW code
8 * CVSID: $Id$
b7ceceb1
DE
9 * -------------------------------------------------------------------------
10 */
11
86afa786
VS
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16 #pragma hdrstop
17#endif
18
b7ceceb1 19/*
dbfb61b3 20 * DONE: for WinCE we need to replace WSAAsyncSelect
b7ceceb1
DE
21 * (Windows message-based notification of network events for a socket)
22 * with another mechanism.
dbfb61b3
JS
23 * As WSAAsyncSelect is not present on WinCE, it now uses
24 * WSACreateEvent, WSAEventSelect, WSAWaitForMultipleEvents and WSAEnumNetworkEvents.
25 * When enabling eventhandling for a socket a new thread it created that keeps track of the events
26 * and posts a messageto the hidden window to use the standard message loop.
b7ceceb1
DE
27 */
28
b7ceceb1
DE
29/* including rasasync.h (included from windows.h itself included from
30 * wx/setup.h and/or winsock.h results in this warning for
31 * RPCNOTIFICATION_ROUTINE
32 */
33#ifdef _MSC_VER
1c8681b4 34# pragma warning(disable:4115) /* named type definition in parentheses */
b7ceceb1
DE
35#endif
36
37/* This needs to be before the wx/defs/h inclusion
38 * for some reason
39 */
40
41#ifdef __WXWINCE__
1c8681b4
DE
42 /* windows.h results in tons of warnings at max warning level */
43# ifdef _MSC_VER
44# pragma warning(push, 1)
45# endif
46# include <windows.h>
47# ifdef _MSC_VER
48# pragma warning(pop)
49# pragma warning(disable:4514)
50# endif
b7ceceb1
DE
51#endif
52
53#ifndef __GSOCKET_STANDALONE__
54# include "wx/platform.h"
55# include "wx/setup.h"
56#endif
57
58#if wxUSE_SOCKETS || defined(__GSOCKET_STANDALONE__)
59
60#ifndef __GSOCKET_STANDALONE__
61
62#include "wx/msw/gsockmsw.h"
63#include "wx/gsocket.h"
64
ebb97a41 65extern "C" WXDLLIMPEXP_BASE HINSTANCE wxGetInstance(void);
b7ceceb1
DE
66#define INSTANCE wxGetInstance()
67
854bac91 68#else /* __GSOCKET_STANDALONE__ */
b7ceceb1
DE
69
70#include "gsockmsw.h"
71#include "gsocket.h"
72
77ffb593 73/* If not using wxWidgets, a global var called hInst must
b7ceceb1
DE
74 * be available and it must contain the app's instance
75 * handle.
76 */
854bac91 77extern HINSTANCE hInst;
b7ceceb1
DE
78#define INSTANCE hInst
79
854bac91 80#endif /* !__GSOCKET_STANDALONE__/__GSOCKET_STANDALONE__ */
b7ceceb1
DE
81
82#ifndef __WXWINCE__
83#include <assert.h>
84#else
85#define assert(x)
86#include <winsock.h>
87#include "wx/msw/wince/net.h"
84aa68c4
JS
88#include <wx/hashmap.h>
89WX_DECLARE_HASH_MAP(int,bool,wxIntegerHash,wxIntegerEqual,SocketHash);
b7ceceb1
DE
90#endif
91
92#include <string.h>
93#include <stdio.h>
94#include <stdlib.h>
95#include <stddef.h>
96#include <ctype.h>
97
98#include <winsock.h>
99
100#ifdef _MSC_VER
101# pragma warning(default:4115) /* named type definition in parentheses */
102#endif
103
104#define CLASSNAME TEXT("_GSocket_Internal_Window_Class")
105
106/* implemented in utils.cpp */
32524c8f 107extern "C" WXDLLIMPEXP_BASE HWND
b7ceceb1
DE
108wxCreateHiddenWindow(LPCTSTR *pclassname, LPCTSTR classname, WNDPROC wndproc);
109
110/* Maximum number of different GSocket objects at a given time.
111 * This value can be modified at will, but it CANNOT be greater
112 * than (0x7FFF - WM_USER + 1)
113 */
114#define MAXSOCKETS 1024
115
116#if (MAXSOCKETS > (0x7FFF - WM_USER + 1))
117#error "MAXSOCKETS is too big!"
118#endif
119
dbfb61b3 120#ifndef __WXWINCE__
1c8681b4 121typedef int (PASCAL *WSAAsyncSelectFunc)(SOCKET,HWND,u_int,long);
dbfb61b3
JS
122#else
123/* Typedef the needed function prototypes and the WSANETWORKEVENTS structure
124*/
125typedef struct _WSANETWORKEVENTS {
126 long lNetworkEvents;
127 int iErrorCode[10];
128} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
129typedef HANDLE (PASCAL *WSACreateEventFunc)(void);
130typedef int (PASCAL *WSAEventSelectFunc)(SOCKET,HANDLE,long);
131typedef int (PASCAL *WSAWaitForMultipleEventsFunc)(long,HANDLE,BOOL,long,BOOL);
132typedef int (PASCAL *WSAEnumNetworkEventsFunc)(SOCKET,HANDLE,LPWSANETWORKEVENTS);
133#endif //__WXWINCE__
b7ceceb1 134
c90cc42e
DE
135LRESULT CALLBACK _GSocket_Internal_WinProc(HWND, UINT, WPARAM, LPARAM);
136
b7ceceb1
DE
137/* Global variables */
138
b7ceceb1
DE
139static HWND hWin;
140static CRITICAL_SECTION critical;
141static GSocket* socketList[MAXSOCKETS];
142static int firstAvailable;
dbfb61b3
JS
143
144#ifndef __WXWINCE__
1c8681b4 145static WSAAsyncSelectFunc gs_WSAAsyncSelect = NULL;
dbfb61b3 146#else
84aa68c4 147static SocketHash socketHash;
dbfb61b3
JS
148static unsigned int currSocket;
149HANDLE hThread[MAXSOCKETS];
150static WSACreateEventFunc gs_WSACreateEvent = NULL;
151static WSAEventSelectFunc gs_WSAEventSelect = NULL;
152static WSAWaitForMultipleEventsFunc gs_WSAWaitForMultipleEvents = NULL;
153static WSAEnumNetworkEventsFunc gs_WSAEnumNetworkEvents = NULL;
154/* This structure will be used to pass data on to the thread that handles socket events.
155*/
156typedef struct thread_data{
84aa68c4
JS
157 HWND hEvtWin;
158 unsigned long msgnumber;
159 unsigned long fd;
160 unsigned long lEvent;
dbfb61b3
JS
161}thread_data;
162#endif
163
1c8681b4 164static HMODULE gs_wsock32dll = 0;
b7ceceb1 165
dbfb61b3
JS
166
167#ifdef __WXWINCE__
168/* This thread handles socket events on WinCE using WSAEventSelect() as WSAAsyncSelect is not supported.
169* When an event occures for the socket, it is checked what kind of event happend and the correct message gets posted
170* so that the hidden window can handle it as it would in other MSW builds.
171*/
172DWORD WINAPI SocketThread(LPVOID data)
173{
84aa68c4
JS
174 WSANETWORKEVENTS NetworkEvents;
175 thread_data* d = (thread_data *)data;
176
177 HANDLE NetworkEvent = gs_WSACreateEvent();
178 gs_WSAEventSelect(d->fd, NetworkEvent, d->lEvent);
179
180 while(socketHash[d->fd] == true)
181 {
182 if ((gs_WSAWaitForMultipleEvents(1, &NetworkEvent, FALSE,INFINITE, FALSE)) == WAIT_FAILED)
183 {
184 printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError());
185 return 0;
186 }
187 if (gs_WSAEnumNetworkEvents(d->fd ,NetworkEvent, &NetworkEvents) == SOCKET_ERROR)
188 {
189 printf("WSAEnumNetworkEvents failed with error %d\n", WSAGetLastError());
190 return 0;
191 }
192
193 long flags = NetworkEvents.lNetworkEvents;
194 if (flags & FD_READ)
195 ::PostMessage(d->hEvtWin, d->msgnumber,d->fd, FD_READ);
196 if (flags & FD_WRITE)
197 ::PostMessage(d->hEvtWin, d->msgnumber,d->fd, FD_WRITE);
198 if (flags & FD_OOB)
199 ::PostMessage(d->hEvtWin, d->msgnumber,d->fd, FD_OOB);
200 if (flags & FD_ACCEPT)
201 ::PostMessage(d->hEvtWin, d->msgnumber,d->fd, FD_ACCEPT);
202 if (flags & FD_CONNECT)
203 ::PostMessage(d->hEvtWin, d->msgnumber,d->fd, FD_CONNECT);
204 if (flags & FD_CLOSE)
205 ::PostMessage(d->hEvtWin, d->msgnumber,d->fd, FD_CLOSE);
206
207 }
208 gs_WSAEventSelect(d->fd, NetworkEvent, 0);
209 ExitThread(0);
210 return 0;
dbfb61b3
JS
211}
212#endif
213
214
c90cc42e 215bool GSocketGUIFunctionsTableConcrete::CanUseEventLoop()
1c6dd11c
WS
216{
217 return true;
218}
c90cc42e 219
b7ceceb1
DE
220/* Global initializers */
221
c90cc42e 222bool GSocketGUIFunctionsTableConcrete::OnInit()
b7ceceb1 223{
84aa68c4
JS
224 static LPCTSTR pclassname = NULL;
225 int i;
b7ceceb1 226
84aa68c4
JS
227 /* Create internal window for event notifications */
228 hWin = wxCreateHiddenWindow(&pclassname, CLASSNAME, _GSocket_Internal_WinProc);
229 if (!hWin)
230 return false;
b7ceceb1 231
84aa68c4
JS
232 /* Initialize socket list */
233 InitializeCriticalSection(&critical);
b7ceceb1 234
84aa68c4
JS
235 for (i = 0; i < MAXSOCKETS; i++)
236 {
237 socketList[i] = NULL;
238 }
239 firstAvailable = 0;
b7ceceb1 240
84aa68c4
JS
241 /* Load WSAAsyncSelect from wsock32.dll (we don't link against it
242 statically to avoid dependency on wsock32.dll for apps that don't use
243 sockets): */
dbfb61b3 244#ifndef __WXWINCE__
84aa68c4
JS
245 gs_wsock32dll = LoadLibrary(wxT("wsock32.dll"));
246 if (!gs_wsock32dll)
247 return false;
248 gs_WSAAsyncSelect =(WSAAsyncSelectFunc)GetProcAddress(gs_wsock32dll,
e9729fc7 249 "WSAAsyncSelect");
84aa68c4
JS
250 if (!gs_WSAAsyncSelect)
251 return false;
dbfb61b3
JS
252#else
253/* On WinCE we load ws2.dll which will provide the needed functions.
254*/
84aa68c4
JS
255 gs_wsock32dll = LoadLibrary(wxT("ws2.dll"));
256 if (!gs_wsock32dll)
257 return false;
258 gs_WSAEventSelect =(WSAEventSelectFunc)GetProcAddress(gs_wsock32dll,
259 wxT("WSAEventSelect"));
260 if (!gs_WSAEventSelect)
261 return false;
1c6dd11c 262
84aa68c4
JS
263 gs_WSACreateEvent =(WSACreateEventFunc)GetProcAddress(gs_wsock32dll,
264 wxT("WSACreateEvent"));
265 if (!gs_WSACreateEvent)
266 return false;
267
268 gs_WSAWaitForMultipleEvents =(WSAWaitForMultipleEventsFunc)GetProcAddress(gs_wsock32dll,
269 wxT("WSAWaitForMultipleEvents"));
270 if (!gs_WSAWaitForMultipleEvents)
271 return false;
272
273 gs_WSAEnumNetworkEvents =(WSAEnumNetworkEventsFunc)GetProcAddress(gs_wsock32dll,
274 wxT("WSAEnumNetworkEvents"));
275 if (!gs_WSAEnumNetworkEvents)
276 return false;
277
278 currSocket = 0;
279#endif
280
281 return true;
b7ceceb1
DE
282}
283
c90cc42e 284void GSocketGUIFunctionsTableConcrete::OnExit()
b7ceceb1 285{
dbfb61b3 286#ifdef __WXWINCE__
1c6dd11c 287/* Delete the threads here */
84aa68c4
JS
288 for(unsigned int i=0; i < currSocket; i++)
289 CloseHandle(hThread[i]);
dbfb61b3 290#endif
b7ceceb1
DE
291 /* Destroy internal window */
292 DestroyWindow(hWin);
293 UnregisterClass(CLASSNAME, INSTANCE);
294
1c8681b4
DE
295 /* Unlock wsock32.dll */
296 if (gs_wsock32dll)
297 {
298 FreeLibrary(gs_wsock32dll);
299 gs_wsock32dll = 0;
300 }
301
b7ceceb1
DE
302 /* Delete critical section */
303 DeleteCriticalSection(&critical);
304}
305
306/* Per-socket GUI initialization / cleanup */
307
c90cc42e 308bool GSocketGUIFunctionsTableConcrete::Init_Socket(GSocket *socket)
b7ceceb1
DE
309{
310 int i;
311
312 /* Allocate a new message number for this socket */
313 EnterCriticalSection(&critical);
314
315 i = firstAvailable;
316 while (socketList[i] != NULL)
317 {
318 i = (i + 1) % MAXSOCKETS;
319
320 if (i == firstAvailable) /* abort! */
321 {
322 LeaveCriticalSection(&critical);
c90cc42e 323 return false;
b7ceceb1
DE
324 }
325 }
326 socketList[i] = socket;
327 firstAvailable = (i + 1) % MAXSOCKETS;
328 socket->m_msgnumber = (i + WM_USER);
329
330 LeaveCriticalSection(&critical);
331
c90cc42e 332 return true;
b7ceceb1
DE
333}
334
c90cc42e 335void GSocketGUIFunctionsTableConcrete::Destroy_Socket(GSocket *socket)
b7ceceb1
DE
336{
337 /* Remove the socket from the list */
338 EnterCriticalSection(&critical);
339 socketList[(socket->m_msgnumber - WM_USER)] = NULL;
340 LeaveCriticalSection(&critical);
341}
342
343/* Windows proc for asynchronous event handling */
344
345LRESULT CALLBACK _GSocket_Internal_WinProc(HWND hWnd,
346 UINT uMsg,
347 WPARAM wParam,
348 LPARAM lParam)
349{
350 GSocket *socket;
351 GSocketEvent event;
352 GSocketCallback cback;
353 char *data;
354
355 if (uMsg >= WM_USER && uMsg <= (WM_USER + MAXSOCKETS - 1))
356 {
357 EnterCriticalSection(&critical);
358 socket = socketList[(uMsg - WM_USER)];
359 event = (GSocketEvent) -1;
360 cback = NULL;
361 data = NULL;
362
363 /* Check that the socket still exists (it has not been
364 * destroyed) and for safety, check that the m_fd field
365 * is what we expect it to be.
366 */
367 if ((socket != NULL) && (socket->m_fd == wParam))
368 {
369 switch WSAGETSELECTEVENT(lParam)
370 {
371 case FD_READ: event = GSOCK_INPUT; break;
372 case FD_WRITE: event = GSOCK_OUTPUT; break;
373 case FD_ACCEPT: event = GSOCK_CONNECTION; break;
374 case FD_CONNECT:
375 {
376 if (WSAGETSELECTERROR(lParam) != 0)
377 event = GSOCK_LOST;
378 else
379 event = GSOCK_CONNECTION;
380 break;
381 }
382 case FD_CLOSE: event = GSOCK_LOST; break;
383 }
384
385 if (event != -1)
386 {
387 cback = socket->m_cbacks[event];
388 data = socket->m_data[event];
389
390 if (event == GSOCK_LOST)
391 socket->m_detected = GSOCK_LOST_FLAG;
392 else
393 socket->m_detected |= (1 << event);
394 }
395 }
396
397 /* OK, we can now leave the critical section because we have
398 * already obtained the callback address (we make no further
399 * accesses to socket->whatever). However, the app should
400 * be prepared to handle events from a socket that has just
401 * been closed!
402 */
403 LeaveCriticalSection(&critical);
404
405 if (cback != NULL)
406 (cback)(socket, event, data);
407
408 return (LRESULT) 0;
409 }
410 else
411 return DefWindowProc(hWnd, uMsg, wParam, lParam);
412}
413
414/* _GSocket_Enable_Events:
415 * Enable all event notifications; we need to be notified of all
416 * events for internal processing, but we will only notify users
417 * when an appropiate callback function has been installed.
418 */
c90cc42e 419void GSocketGUIFunctionsTableConcrete::Enable_Events(GSocket *socket)
b7ceceb1
DE
420{
421 assert (socket != NULL);
422
423 if (socket->m_fd != INVALID_SOCKET)
424 {
425 /* We could probably just subscribe to all events regardless
426 * of the socket type, but MS recommends to do it this way.
427 */
428 long lEvent = socket->m_server?
429 FD_ACCEPT : (FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE);
dbfb61b3 430#ifndef __WXWINCE__
1c8681b4 431 gs_WSAAsyncSelect(socket->m_fd, hWin, socket->m_msgnumber, lEvent);
dbfb61b3
JS
432#else
433/*
434* WinCE creates a thread for socket event handling.
1c6dd11c 435* All needed parameters get passed through the thread_data structure.
dbfb61b3 436*/
84aa68c4
JS
437
438 thread_data* d = new thread_data;
439 d->lEvent = lEvent;
440 d->hEvtWin = hWin;
441 d->msgnumber = socket->m_msgnumber;
442 d->fd = socket->m_fd;
443 socketHash[socket->m_fd] = true;
444 hThread[currSocket++] = CreateThread(NULL, 0, &SocketThread,(LPVOID)d, 0, NULL);
dbfb61b3 445#endif
b7ceceb1
DE
446 }
447}
448
449/* _GSocket_Disable_Events:
450 * Disable event notifications (when shutdowning the socket)
451 */
c90cc42e 452void GSocketGUIFunctionsTableConcrete::Disable_Events(GSocket *socket)
b7ceceb1
DE
453{
454 assert (socket != NULL);
455
456 if (socket->m_fd != INVALID_SOCKET)
457 {
dbfb61b3 458#ifndef __WXWINCE__
1c8681b4 459 gs_WSAAsyncSelect(socket->m_fd, hWin, socket->m_msgnumber, 0);
dbfb61b3 460#else
84aa68c4
JS
461 //Destroy the thread
462 socketHash[socket->m_fd] = false;
dbfb61b3 463#endif
b7ceceb1
DE
464 }
465}
466
467#else /* !wxUSE_SOCKETS */
468
469/*
470 * Translation unit shouldn't be empty, so include this typedef to make the
471 * compiler (VC++ 6.0, for example) happy
472 */
473typedef void (*wxDummy)();
474
475#endif /* wxUSE_SOCKETS || defined(__GSOCKET_STANDALONE__) */