1 /* -------------------------------------------------------------------------
2 * Project: GSocket (Generic Socket)
4 * Author: Guillermo Rodriguez Garcia <guille@iies.es>
5 * Purpose: GSocket GUI-specific MSW code
7 * -------------------------------------------------------------------------
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
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.
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
32 # pragma warning(disable:4115) /* named type definition in parentheses */
35 /* This needs to be before the wx/defs/h inclusion
40 /* windows.h results in tons of warnings at max warning level */
42 # pragma warning(push, 1)
47 # pragma warning(disable:4514)
51 #ifndef __GSOCKET_STANDALONE__
52 # include "wx/platform.h"
53 # include "wx/setup.h"
56 #if wxUSE_SOCKETS || defined(__GSOCKET_STANDALONE__)
58 #ifndef __GSOCKET_STANDALONE__
60 #include "wx/msw/gsockmsw.h"
61 #include "wx/gsocket.h"
63 extern "C" WXDLLIMPEXP_BASE HINSTANCE
wxGetInstance(void);
64 #define INSTANCE wxGetInstance()
66 #else /* __GSOCKET_STANDALONE__ */
71 /* If not using wxWidgets, a global var called hInst must
72 * be available and it must contain the app's instance
75 extern HINSTANCE hInst
;
76 #define INSTANCE hInst
78 #endif /* !__GSOCKET_STANDALONE__/__GSOCKET_STANDALONE__ */
85 #include "wx/msw/wince/net.h"
86 #include <wx/hashmap.h>
87 WX_DECLARE_HASH_MAP(int,bool,wxIntegerHash
,wxIntegerEqual
,SocketHash
);
99 # pragma warning(default:4115) /* named type definition in parentheses */
102 #define CLASSNAME TEXT("_GSocket_Internal_Window_Class")
104 /* implemented in utils.cpp */
105 extern "C" WXDLLIMPEXP_BASE HWND
106 wxCreateHiddenWindow(LPCTSTR
*pclassname
, LPCTSTR classname
, WNDPROC wndproc
);
108 /* Maximum number of different GSocket objects at a given time.
109 * This value can be modified at will, but it CANNOT be greater
110 * than (0x7FFF - WM_USER + 1)
112 #define MAXSOCKETS 1024
114 #if (MAXSOCKETS > (0x7FFF - WM_USER + 1))
115 #error "MAXSOCKETS is too big!"
119 typedef int (PASCAL
*WSAAsyncSelectFunc
)(SOCKET
,HWND
,u_int
,long);
121 /* Typedef the needed function prototypes and the WSANETWORKEVENTS structure
123 typedef struct _WSANETWORKEVENTS
{
126 } WSANETWORKEVENTS
, FAR
* LPWSANETWORKEVENTS
;
127 typedef HANDLE (PASCAL
*WSACreateEventFunc
)(void);
128 typedef int (PASCAL
*WSAEventSelectFunc
)(SOCKET
,HANDLE
,long);
129 typedef int (PASCAL
*WSAWaitForMultipleEventsFunc
)(long,HANDLE
,BOOL
,long,BOOL
);
130 typedef int (PASCAL
*WSAEnumNetworkEventsFunc
)(SOCKET
,HANDLE
,LPWSANETWORKEVENTS
);
133 LRESULT CALLBACK
_GSocket_Internal_WinProc(HWND
, UINT
, WPARAM
, LPARAM
);
135 /* Global variables */
138 static CRITICAL_SECTION critical
;
139 static GSocket
* socketList
[MAXSOCKETS
];
140 static int firstAvailable
;
143 static WSAAsyncSelectFunc gs_WSAAsyncSelect
= NULL
;
145 static SocketHash socketHash
;
146 static unsigned int currSocket
;
147 HANDLE hThread
[MAXSOCKETS
];
148 static WSACreateEventFunc gs_WSACreateEvent
= NULL
;
149 static WSAEventSelectFunc gs_WSAEventSelect
= NULL
;
150 static WSAWaitForMultipleEventsFunc gs_WSAWaitForMultipleEvents
= NULL
;
151 static WSAEnumNetworkEventsFunc gs_WSAEnumNetworkEvents
= NULL
;
152 /* This structure will be used to pass data on to the thread that handles socket events.
154 typedef struct thread_data
{
156 unsigned long msgnumber
;
158 unsigned long lEvent
;
162 static HMODULE gs_wsock32dll
= 0;
166 /* This thread handles socket events on WinCE using WSAEventSelect() as WSAAsyncSelect is not supported.
167 * When an event occures for the socket, it is checked what kind of event happend and the correct message gets posted
168 * so that the hidden window can handle it as it would in other MSW builds.
170 DWORD WINAPI
SocketThread(LPVOID data
)
172 WSANETWORKEVENTS NetworkEvents
;
173 thread_data
* d
= (thread_data
*)data
;
175 HANDLE NetworkEvent
= gs_WSACreateEvent();
176 gs_WSAEventSelect(d
->fd
, NetworkEvent
, d
->lEvent
);
178 while(socketHash
[d
->fd
] == true)
180 if ((gs_WSAWaitForMultipleEvents(1, &NetworkEvent
, FALSE
,INFINITE
, FALSE
)) == WAIT_FAILED
)
182 printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError());
185 if (gs_WSAEnumNetworkEvents(d
->fd
,NetworkEvent
, &NetworkEvents
) == SOCKET_ERROR
)
187 printf("WSAEnumNetworkEvents failed with error %d\n", WSAGetLastError());
191 long flags
= NetworkEvents
.lNetworkEvents
;
193 ::PostMessage(d
->hEvtWin
, d
->msgnumber
,d
->fd
, FD_READ
);
194 if (flags
& FD_WRITE
)
195 ::PostMessage(d
->hEvtWin
, d
->msgnumber
,d
->fd
, FD_WRITE
);
197 ::PostMessage(d
->hEvtWin
, d
->msgnumber
,d
->fd
, FD_OOB
);
198 if (flags
& FD_ACCEPT
)
199 ::PostMessage(d
->hEvtWin
, d
->msgnumber
,d
->fd
, FD_ACCEPT
);
200 if (flags
& FD_CONNECT
)
201 ::PostMessage(d
->hEvtWin
, d
->msgnumber
,d
->fd
, FD_CONNECT
);
202 if (flags
& FD_CLOSE
)
203 ::PostMessage(d
->hEvtWin
, d
->msgnumber
,d
->fd
, FD_CLOSE
);
206 gs_WSAEventSelect(d
->fd
, NetworkEvent
, 0);
213 bool GSocketGUIFunctionsTableConcrete::CanUseEventLoop()
218 /* Global initializers */
220 bool GSocketGUIFunctionsTableConcrete::OnInit()
222 static LPCTSTR pclassname
= NULL
;
225 /* Create internal window for event notifications */
226 hWin
= wxCreateHiddenWindow(&pclassname
, CLASSNAME
, _GSocket_Internal_WinProc
);
230 /* Initialize socket list */
231 InitializeCriticalSection(&critical
);
233 for (i
= 0; i
< MAXSOCKETS
; i
++)
235 socketList
[i
] = NULL
;
239 /* Load WSAAsyncSelect from wsock32.dll (we don't link against it
240 statically to avoid dependency on wsock32.dll for apps that don't use
243 gs_wsock32dll
= LoadLibrary(wxT("wsock32.dll"));
246 gs_WSAAsyncSelect
=(WSAAsyncSelectFunc
)GetProcAddress(gs_wsock32dll
,
248 if (!gs_WSAAsyncSelect
)
251 /* On WinCE we load ws2.dll which will provide the needed functions.
253 gs_wsock32dll
= LoadLibrary(wxT("ws2.dll"));
256 gs_WSAEventSelect
=(WSAEventSelectFunc
)GetProcAddress(gs_wsock32dll
,
257 wxT("WSAEventSelect"));
258 if (!gs_WSAEventSelect
)
261 gs_WSACreateEvent
=(WSACreateEventFunc
)GetProcAddress(gs_wsock32dll
,
262 wxT("WSACreateEvent"));
263 if (!gs_WSACreateEvent
)
266 gs_WSAWaitForMultipleEvents
=(WSAWaitForMultipleEventsFunc
)GetProcAddress(gs_wsock32dll
,
267 wxT("WSAWaitForMultipleEvents"));
268 if (!gs_WSAWaitForMultipleEvents
)
271 gs_WSAEnumNetworkEvents
=(WSAEnumNetworkEventsFunc
)GetProcAddress(gs_wsock32dll
,
272 wxT("WSAEnumNetworkEvents"));
273 if (!gs_WSAEnumNetworkEvents
)
282 void GSocketGUIFunctionsTableConcrete::OnExit()
285 /* Delete the threads here */
286 for(unsigned int i
=0; i
< currSocket
; i
++)
287 CloseHandle(hThread
[i
]);
289 /* Destroy internal window */
291 UnregisterClass(CLASSNAME
, INSTANCE
);
293 /* Unlock wsock32.dll */
296 FreeLibrary(gs_wsock32dll
);
300 /* Delete critical section */
301 DeleteCriticalSection(&critical
);
304 /* Per-socket GUI initialization / cleanup */
306 bool GSocketGUIFunctionsTableConcrete::Init_Socket(GSocket
*socket
)
310 /* Allocate a new message number for this socket */
311 EnterCriticalSection(&critical
);
314 while (socketList
[i
] != NULL
)
316 i
= (i
+ 1) % MAXSOCKETS
;
318 if (i
== firstAvailable
) /* abort! */
320 LeaveCriticalSection(&critical
);
324 socketList
[i
] = socket
;
325 firstAvailable
= (i
+ 1) % MAXSOCKETS
;
326 socket
->m_msgnumber
= (i
+ WM_USER
);
328 LeaveCriticalSection(&critical
);
333 void GSocketGUIFunctionsTableConcrete::Destroy_Socket(GSocket
*socket
)
335 /* Remove the socket from the list */
336 EnterCriticalSection(&critical
);
337 socketList
[(socket
->m_msgnumber
- WM_USER
)] = NULL
;
338 LeaveCriticalSection(&critical
);
341 /* Windows proc for asynchronous event handling */
343 LRESULT CALLBACK
_GSocket_Internal_WinProc(HWND hWnd
,
350 GSocketCallback cback
;
353 if (uMsg
>= WM_USER
&& uMsg
<= (WM_USER
+ MAXSOCKETS
- 1))
355 EnterCriticalSection(&critical
);
356 socket
= socketList
[(uMsg
- WM_USER
)];
357 event
= (GSocketEvent
) -1;
361 /* Check that the socket still exists (it has not been
362 * destroyed) and for safety, check that the m_fd field
363 * is what we expect it to be.
365 if ((socket
!= NULL
) && (socket
->m_fd
== wParam
))
367 switch WSAGETSELECTEVENT(lParam
)
369 case FD_READ
: event
= GSOCK_INPUT
; break;
370 case FD_WRITE
: event
= GSOCK_OUTPUT
; break;
371 case FD_ACCEPT
: event
= GSOCK_CONNECTION
; break;
374 if (WSAGETSELECTERROR(lParam
) != 0)
377 event
= GSOCK_CONNECTION
;
380 case FD_CLOSE
: event
= GSOCK_LOST
; break;
385 cback
= socket
->m_cbacks
[event
];
386 data
= socket
->m_data
[event
];
388 if (event
== GSOCK_LOST
)
389 socket
->m_detected
= GSOCK_LOST_FLAG
;
391 socket
->m_detected
|= (1 << event
);
395 /* OK, we can now leave the critical section because we have
396 * already obtained the callback address (we make no further
397 * accesses to socket->whatever). However, the app should
398 * be prepared to handle events from a socket that has just
401 LeaveCriticalSection(&critical
);
404 (cback
)(socket
, event
, data
);
409 return DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
412 /* _GSocket_Enable_Events:
413 * Enable all event notifications; we need to be notified of all
414 * events for internal processing, but we will only notify users
415 * when an appropiate callback function has been installed.
417 void GSocketGUIFunctionsTableConcrete::Enable_Events(GSocket
*socket
)
419 assert (socket
!= NULL
);
421 if (socket
->m_fd
!= INVALID_SOCKET
)
423 /* We could probably just subscribe to all events regardless
424 * of the socket type, but MS recommends to do it this way.
426 long lEvent
= socket
->m_server
?
427 FD_ACCEPT
: (FD_READ
| FD_WRITE
| FD_CONNECT
| FD_CLOSE
);
429 gs_WSAAsyncSelect(socket
->m_fd
, hWin
, socket
->m_msgnumber
, lEvent
);
432 * WinCE creates a thread for socket event handling.
433 * All needed parameters get passed through the thread_data structure.
436 thread_data
* d
= new thread_data
;
439 d
->msgnumber
= socket
->m_msgnumber
;
440 d
->fd
= socket
->m_fd
;
441 socketHash
[socket
->m_fd
] = true;
442 hThread
[currSocket
++] = CreateThread(NULL
, 0, &SocketThread
,(LPVOID
)d
, 0, NULL
);
447 /* _GSocket_Disable_Events:
448 * Disable event notifications (when shutdowning the socket)
450 void GSocketGUIFunctionsTableConcrete::Disable_Events(GSocket
*socket
)
452 assert (socket
!= NULL
);
454 if (socket
->m_fd
!= INVALID_SOCKET
)
457 gs_WSAAsyncSelect(socket
->m_fd
, hWin
, socket
->m_msgnumber
, 0);
460 socketHash
[socket
->m_fd
] = false;
465 #else /* !wxUSE_SOCKETS */
468 * Translation unit shouldn't be empty, so include this typedef to make the
469 * compiler (VC++ 6.0, for example) happy
471 typedef void (*wxDummy
)();
473 #endif /* wxUSE_SOCKETS || defined(__GSOCKET_STANDALONE__) */