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