1. Win9x support + async dialing + many more for wxDialUpManager
[wxWidgets.git] / src / msw / dialup.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/dialup.cpp
3 // Purpose: MSW implementation of network/dialup classes and functions
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 07.07.99
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 // these functions require Win32
28 #if defined(__WIN16__) && wxUSE_DIALUP_MANAGER
29 #undef wxUSE_DIALUP_MANAGER
30 #define wxUSE_DIALUP_MANAGER 0
31 #endif // wxUSE_DIALUP_MANAGER && Win16
32
33 #if wxUSE_DIALUP_MANAGER
34
35 #ifndef WX_PRECOMP
36 #include "wx/log.h"
37 #endif
38
39 #include "wx/dynlib.h"
40
41 #include "wx/net.h"
42
43 #include <ras.h>
44 #include <raserror.h>
45
46 #include "wx/msw/private.h"
47
48 // ----------------------------------------------------------------------------
49 // constants
50 // ----------------------------------------------------------------------------
51
52 // this message is sent by the secondary thread when RAS status changes
53 #define wxWM_RAS_STATUS_CHANGED (WM_USER + 10010)
54
55 // ----------------------------------------------------------------------------
56 // types
57 // ----------------------------------------------------------------------------
58
59 // the signatures of RAS functions: all this is quite heavy, but we must do it
60 // to allow running wxWin programs on machine which don't have RAS installed
61 // (this does exist) - if we link with rasapi32.lib, the program will fail on
62 // startup because of the missing DLL...
63
64 #ifndef UNICODE
65 typedef DWORD (* RASDIAL)( LPRASDIALEXTENSIONS, LPCSTR, LPRASDIALPARAMSA, DWORD, LPVOID, LPHRASCONN );
66 typedef DWORD (* RASENUMCONNECTIONS)( LPRASCONNA, LPDWORD, LPDWORD );
67 typedef DWORD (* RASENUMENTRIES)( LPCSTR, LPCSTR, LPRASENTRYNAMEA, LPDWORD, LPDWORD );
68 typedef DWORD (* RASGETCONNECTSTATUS)( HRASCONN, LPRASCONNSTATUSA );
69 typedef DWORD (* RASGETERRORSTRING)( UINT, LPSTR, DWORD );
70 typedef DWORD (* RASHANGUP)( HRASCONN );
71 typedef DWORD (* RASGETPROJECTIONINFO)( HRASCONN, RASPROJECTION, LPVOID, LPDWORD );
72 typedef DWORD (* RASCREATEPHONEBOOKENTRY)( HWND, LPCSTR );
73 typedef DWORD (* RASEDITPHONEBOOKENTRY)( HWND, LPCSTR, LPCSTR );
74 typedef DWORD (* RASSETENTRYDIALPARAMS)( LPCSTR, LPRASDIALPARAMSA, BOOL );
75 typedef DWORD (* RASGETENTRYDIALPARAMS)( LPCSTR, LPRASDIALPARAMSA, LPBOOL );
76 typedef DWORD (* RASENUMDEVICES)( LPRASDEVINFOA, LPDWORD, LPDWORD );
77 typedef DWORD (* RASGETCOUNTRYINFO)( LPRASCTRYINFOA, LPDWORD );
78 typedef DWORD (* RASGETENTRYPROPERTIES)( LPCSTR, LPCSTR, LPRASENTRYA, LPDWORD, LPBYTE, LPDWORD );
79 typedef DWORD (* RASSETENTRYPROPERTIES)( LPCSTR, LPCSTR, LPRASENTRYA, DWORD, LPBYTE, DWORD );
80 typedef DWORD (* RASRENAMEENTRY)( LPCSTR, LPCSTR, LPCSTR );
81 typedef DWORD (* RASDELETEENTRY)( LPCSTR, LPCSTR );
82 typedef DWORD (* RASVALIDATEENTRYNAME)( LPCSTR, LPCSTR );
83 typedef DWORD (* RASCONNECTIONNOTIFICATION)( HRASCONN, HANDLE, DWORD );
84
85 static const char gs_funcSuffix = 'A';
86 #else // Unicode
87 typedef DWORD (* RASDIAL)( LPRASDIALEXTENSIONS, LPCWSTR, LPRASDIALPARAMSW, DWORD, LPVOID, LPHRASCONN );
88 typedef DWORD (* RASENUMCONNECTIONS)( LPRASCONNW, LPDWORD, LPDWORD );
89 typedef DWORD (* RASENUMENTRIES)( LPCWSTR, LPCWSTR, LPRASENTRYNAMEW, LPDWORD, LPDWORD );
90 typedef DWORD (* RASGETCONNECTSTATUS)( HRASCONN, LPRASCONNSTATUSW );
91 typedef DWORD (* RASGETERRORSTRING)( UINT, LPWSTR, DWORD );
92 typedef DWORD (* RASHANGUP)( HRASCONN );
93 typedef DWORD (* RASGETPROJECTIONINFO)( HRASCONN, RASPROJECTION, LPVOID, LPDWORD );
94 typedef DWORD (* RASCREATEPHONEBOOKENTRY)( HWND, LPCWSTR );
95 typedef DWORD (* RASEDITPHONEBOOKENTRY)( HWND, LPCWSTR, LPCWSTR );
96 typedef DWORD (* RASSETENTRYDIALPARAMS)( LPCWSTR, LPRASDIALPARAMSW, BOOL );
97 typedef DWORD (* RASGETENTRYDIALPARAMS)( LPCWSTR, LPRASDIALPARAMSW, LPBOOL );
98 typedef DWORD (* RASENUMDEVICES)( LPRASDEVINFOW, LPDWORD, LPDWORD );
99 typedef DWORD (* RASGETCOUNTRYINFO)( LPRASCTRYINFOW, LPDWORD );
100 typedef DWORD (* RASGETENTRYPROPERTIES)( LPCWSTR, LPCWSTR, LPRASENTRYW, LPDWORD, LPBYTE, LPDWORD );
101 typedef DWORD (* RASSETENTRYPROPERTIES)( LPCWSTR, LPCWSTR, LPRASENTRYW, DWORD, LPBYTE, DWORD );
102 typedef DWORD (* RASRENAMEENTRY)( LPCWSTR, LPCWSTR, LPCWSTR );
103 typedef DWORD (* RASDELETEENTRY)( LPCWSTR, LPCWSTR );
104 typedef DWORD (* RASVALIDATEENTRYNAME)( LPCWSTR, LPCWSTR );
105 typedef DWORD (* RASCONNECTIONNOTIFICATION)( HRASCONN, HANDLE, DWORD );
106
107 static const char gs_funcSuffix = 'W';
108 #endif // ASCII/Unicode
109
110 // structure passed to the secondary thread
111 struct wxRasThreadData
112 {
113 wxRasThreadData()
114 {
115 hWnd = 0;
116 hEventRas = hEventQuit = INVALID_HANDLE_VALUE;
117 dialUpManager = NULL;
118 }
119
120 HWND hWnd; // window to send notifications to
121 HANDLE hEventRas, // event which RAS signals when status changes
122 hEventQuit; // event which we signal when we terminate
123
124 class WXDLLEXPORT wxDialUpManagerMSW *dialUpManager; // the owner
125 };
126
127 // ----------------------------------------------------------------------------
128 // wxDialUpManager class for MSW
129 // ----------------------------------------------------------------------------
130
131 class WXDLLEXPORT wxDialUpManagerMSW : public wxDialUpManager
132 {
133 public:
134 // ctor & dtor
135 wxDialUpManagerMSW();
136 virtual ~wxDialUpManagerMSW();
137
138 // implement base class pure virtuals
139 virtual bool IsOk() const;
140 virtual bool Dial(const wxString& nameOfISP,
141 const wxString& username,
142 const wxString& password,
143 bool async);
144 virtual bool IsDialing() const;
145 virtual bool CancelDialing();
146 virtual bool HangUp();
147 virtual bool IsOnline() const;
148 virtual void SetOnlineStatus(bool isOnline = TRUE);
149 virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds);
150 virtual void DisableAutoCheckOnlineStatus();
151 virtual void SetWellKnownHost(const wxString& hostname, int port);
152 virtual void SetConnectCommand(const wxString& commandDial,
153 const wxString& commandHangup);
154
155 // for RasTimer
156 void CheckRasStatus();
157
158 // for wxRasStatusWindowProc
159 void OnConnectStatusChange();
160
161 // for wxRasDialFunc
162 void OnDialProgress(RASCONNSTATE rasconnstate, DWORD dwError);
163 static wxDialUpManagerMSW *GetDialer() { return ms_dialer; }
164
165 private:
166 // return the error string for the given RAS error code
167 static wxString GetErrorString(DWORD error);
168
169 // find the (first) handle of the active connection
170 static HRASCONN FindActiveConnection();
171
172 // notify the application about status change
173 void NotifyApp(bool connected, bool fromOurselves = FALSE) const;
174
175 // destroy the thread data and the thread itself
176 void CleanUpThreadData();
177
178 // timer used for polling RAS status
179 class RasTimer : public wxTimer
180 {
181 public:
182 RasTimer(wxDialUpManagerMSW *dialUpManager)
183 { m_dialUpManager = dialUpManager; }
184
185 virtual void Notify() { m_dialUpManager->CheckRasStatus(); }
186
187 private:
188 wxDialUpManagerMSW *m_dialUpManager;
189 } m_timerStatusPolling;
190
191 // thread handle for the thread sitting on connection change event
192 HANDLE m_hThread;
193
194 // data used by this thread and our hidden window to send messages between
195 // each other
196 wxRasThreadData m_data;
197
198 // the handle of the connection we initiated or 0 if none
199 static HRASCONN ms_hRasConnection;
200
201 // the use count of rasapi32.dll
202 static int ms_nDllCount;
203
204 // the handle of rasapi32.dll when it's loaded
205 static wxDllType ms_dllRas;
206
207 // the pointers to RAS functions
208 static RASDIAL ms_pfnRasDial;
209 static RASENUMCONNECTIONS ms_pfnRasEnumConnections;
210 static RASENUMENTRIES ms_pfnRasEnumEntries;
211 static RASGETCONNECTSTATUS ms_pfnRasGetConnectStatus;
212 static RASGETERRORSTRING ms_pfnRasGetErrorString;
213 static RASHANGUP ms_pfnRasHangUp;
214 static RASGETPROJECTIONINFO ms_pfnRasGetProjectionInfo;
215 static RASCREATEPHONEBOOKENTRY ms_pfnRasCreatePhonebookEntry;
216 static RASEDITPHONEBOOKENTRY ms_pfnRasEditPhonebookEntry;
217 static RASSETENTRYDIALPARAMS ms_pfnRasSetEntryDialParams;
218 static RASGETENTRYDIALPARAMS ms_pfnRasGetEntryDialParams;
219 static RASENUMDEVICES ms_pfnRasEnumDevices;
220 static RASGETCOUNTRYINFO ms_pfnRasGetCountryInfo;
221 static RASGETENTRYPROPERTIES ms_pfnRasGetEntryProperties;
222 static RASSETENTRYPROPERTIES ms_pfnRasSetEntryProperties;
223 static RASRENAMEENTRY ms_pfnRasRenameEntry;
224 static RASDELETEENTRY ms_pfnRasDeleteEntry;
225 static RASVALIDATEENTRYNAME ms_pfnRasValidateEntryName;
226
227 // this function is not supported by Win95
228 static RASCONNECTIONNOTIFICATION ms_pfnRasConnectionNotification;
229
230 // if this flag is different from -1, it overrides IsOnline()
231 static int ms_userSpecifiedOnlineStatus;
232
233 // this flag tells us if we're online
234 static int ms_isConnected;
235
236 // this flag tells us whether a call to RasDial() is in progress
237 static wxDialUpManagerMSW *ms_dialer;
238 };
239
240 // ----------------------------------------------------------------------------
241 // private functions
242 // ----------------------------------------------------------------------------
243
244 static LRESULT WINAPI wxRasStatusWindowProc(HWND hWnd, UINT message,
245 WPARAM wParam, LPARAM lParam);
246
247 static DWORD wxRasMonitorThread(wxRasThreadData *data);
248
249 static void WINAPI wxRasDialFunc(UINT unMsg,
250 RASCONNSTATE rasconnstate,
251 DWORD dwError);
252
253 // ============================================================================
254 // implementation
255 // ============================================================================
256
257 // ----------------------------------------------------------------------------
258 // init the static variables
259 // ----------------------------------------------------------------------------
260
261 HRASCONN wxDialUpManagerMSW::ms_hRasConnection = 0;
262
263 int wxDialUpManagerMSW::ms_nDllCount = 0;
264 wxDllType wxDialUpManagerMSW::ms_dllRas = 0;
265
266 RASDIAL wxDialUpManagerMSW::ms_pfnRasDial = 0;
267 RASENUMCONNECTIONS wxDialUpManagerMSW::ms_pfnRasEnumConnections = 0;
268 RASENUMENTRIES wxDialUpManagerMSW::ms_pfnRasEnumEntries = 0;
269 RASGETCONNECTSTATUS wxDialUpManagerMSW::ms_pfnRasGetConnectStatus = 0;
270 RASGETERRORSTRING wxDialUpManagerMSW::ms_pfnRasGetErrorString = 0;
271 RASHANGUP wxDialUpManagerMSW::ms_pfnRasHangUp = 0;
272 RASGETPROJECTIONINFO wxDialUpManagerMSW::ms_pfnRasGetProjectionInfo = 0;
273 RASCREATEPHONEBOOKENTRY wxDialUpManagerMSW::ms_pfnRasCreatePhonebookEntry = 0;
274 RASEDITPHONEBOOKENTRY wxDialUpManagerMSW::ms_pfnRasEditPhonebookEntry = 0;
275 RASSETENTRYDIALPARAMS wxDialUpManagerMSW::ms_pfnRasSetEntryDialParams = 0;
276 RASGETENTRYDIALPARAMS wxDialUpManagerMSW::ms_pfnRasGetEntryDialParams = 0;
277 RASENUMDEVICES wxDialUpManagerMSW::ms_pfnRasEnumDevices = 0;
278 RASGETCOUNTRYINFO wxDialUpManagerMSW::ms_pfnRasGetCountryInfo = 0;
279 RASGETENTRYPROPERTIES wxDialUpManagerMSW::ms_pfnRasGetEntryProperties = 0;
280 RASSETENTRYPROPERTIES wxDialUpManagerMSW::ms_pfnRasSetEntryProperties = 0;
281 RASRENAMEENTRY wxDialUpManagerMSW::ms_pfnRasRenameEntry = 0;
282 RASDELETEENTRY wxDialUpManagerMSW::ms_pfnRasDeleteEntry = 0;
283 RASVALIDATEENTRYNAME wxDialUpManagerMSW::ms_pfnRasValidateEntryName = 0;
284 RASCONNECTIONNOTIFICATION wxDialUpManagerMSW::ms_pfnRasConnectionNotification = 0;
285
286 int wxDialUpManagerMSW::ms_userSpecifiedOnlineStatus = -1;
287 int wxDialUpManagerMSW::ms_isConnected = -1;
288 wxDialUpManagerMSW *wxDialUpManagerMSW::ms_dialer = NULL;
289
290 // ----------------------------------------------------------------------------
291 // ctor and dtor: the dynamic linking happens here
292 // ----------------------------------------------------------------------------
293
294 // the static creator function is implemented here
295 wxDialUpManager *wxDialUpManager::Create()
296 {
297 return new wxDialUpManagerMSW;
298 }
299
300 #ifdef __VISUALC__
301 // warning about "'this' : used in base member initializer list" - so what?
302 #pragma warning(disable:4355)
303 #endif // VC++
304
305 wxDialUpManagerMSW::wxDialUpManagerMSW()
306 : m_timerStatusPolling(this)
307 {
308 // initialize our data
309 m_hThread = 0;
310
311 if ( !ms_nDllCount++ )
312 {
313 // load the RAS library
314 ms_dllRas = wxDllLoader::LoadLibrary("RASAPI32");
315 if ( !ms_dllRas )
316 {
317 wxLogError(_("Dial up functions are unavailable because the "
318 "remote access service (RAS) is not installed "
319 "on this machine. Please install it."));
320 }
321 else
322 {
323 // resolve the functions we need
324
325 // this will contain the name of the function we failed to resolve
326 // if any at the end
327 const char *funcName = NULL;
328
329 // get the function from rasapi32.dll and abort if it's not found
330 #define RESOLVE_RAS_FUNCTION(type, name) \
331 ms_pfn##name = (type)wxDllLoader::GetSymbol(ms_dllRas, \
332 wxString(#name) + gs_funcSuffix); \
333 if ( !ms_pfn##name ) \
334 { \
335 funcName = #name; \
336 goto exit; \
337 }
338
339 // a variant of above macro which doesn't abort if the function is
340 // not found in the DLL
341 #define RESOLVE_OPTIONAL_RAS_FUNCTION(type, name) \
342 ms_pfn##name = (type)wxDllLoader::GetSymbol(ms_dllRas, \
343 wxString(#name) + gs_funcSuffix);
344
345 RESOLVE_RAS_FUNCTION(RASDIAL, RasDial);
346 RESOLVE_RAS_FUNCTION(RASENUMCONNECTIONS, RasEnumConnections);
347 RESOLVE_RAS_FUNCTION(RASENUMENTRIES, RasEnumEntries);
348 RESOLVE_RAS_FUNCTION(RASGETCONNECTSTATUS, RasGetConnectStatus);
349 RESOLVE_RAS_FUNCTION(RASGETERRORSTRING, RasGetErrorString);
350 RESOLVE_RAS_FUNCTION(RASHANGUP, RasHangUp);
351
352 // suppress wxDllLoader messages about missing (non essential)
353 // functions
354 {
355 wxLogNull noLog;
356
357 RESOLVE_OPTIONAL_RAS_FUNCTION(RASGETPROJECTIONINFO, RasGetProjectionInfo);
358 RESOLVE_OPTIONAL_RAS_FUNCTION(RASCREATEPHONEBOOKENTRY, RasCreatePhonebookEntry);
359 RESOLVE_OPTIONAL_RAS_FUNCTION(RASEDITPHONEBOOKENTRY, RasEditPhonebookEntry);
360 RESOLVE_OPTIONAL_RAS_FUNCTION(RASSETENTRYDIALPARAMS, RasSetEntryDialParams);
361 RESOLVE_OPTIONAL_RAS_FUNCTION(RASGETENTRYDIALPARAMS, RasGetEntryDialParams);
362 RESOLVE_OPTIONAL_RAS_FUNCTION(RASGETENTRYPROPERTIES, RasGetEntryProperties);
363 RESOLVE_OPTIONAL_RAS_FUNCTION(RASSETENTRYPROPERTIES, RasSetEntryProperties);
364 RESOLVE_OPTIONAL_RAS_FUNCTION(RASRENAMEENTRY, RasRenameEntry);
365 RESOLVE_OPTIONAL_RAS_FUNCTION(RASDELETEENTRY, RasDeleteEntry);
366 RESOLVE_OPTIONAL_RAS_FUNCTION(RASVALIDATEENTRYNAME, RasValidateEntryName);
367 RESOLVE_OPTIONAL_RAS_FUNCTION(RASGETCOUNTRYINFO, RasGetCountryInfo);
368 RESOLVE_OPTIONAL_RAS_FUNCTION(RASENUMDEVICES, RasEnumDevices);
369 RESOLVE_OPTIONAL_RAS_FUNCTION(RASCONNECTIONNOTIFICATION, RasConnectionNotification);
370 }
371
372 // keep your preprocessor name space clean
373 #undef RESOLVE_RAS_FUNCTION
374 #undef RESOLVE_OPTIONAL_RAS_FUNCTION
375
376 exit:
377 if ( funcName )
378 {
379 wxLogError(_("The version of remote access service (RAS) "
380 "installed on this machine is too old, please "
381 "upgrade (the following required function is "
382 "missing: %s)."), funcName);
383
384 wxDllLoader::UnloadLibrary(ms_dllRas);
385 ms_dllRas = 0;
386 ms_nDllCount = 0;
387
388 return;
389 }
390 }
391 }
392
393 // enable auto check by default
394 EnableAutoCheckOnlineStatus(0);
395 }
396
397 wxDialUpManagerMSW::~wxDialUpManagerMSW()
398 {
399 CleanUpThreadData();
400
401 if ( !--ms_nDllCount )
402 {
403 // unload the RAS library
404 wxDllLoader::UnloadLibrary(ms_dllRas);
405 ms_dllRas = 0;
406 }
407 }
408
409 // ----------------------------------------------------------------------------
410 // helper functions
411 // ----------------------------------------------------------------------------
412
413 wxString wxDialUpManagerMSW::GetErrorString(DWORD error)
414 {
415 wxChar buffer[512]; // this should be more than enough according to MS docs
416 DWORD dwRet = ms_pfnRasGetErrorString(error, buffer, WXSIZEOF(buffer));
417 switch ( dwRet )
418 {
419 case ERROR_INVALID_PARAMETER:
420 // this was a standard Win32 error probably
421 return wxString(wxSysErrorMsg(error));
422
423 default:
424 {
425 wxLogSysError(dwRet, _("Failed to retrieve text of RAS "
426 "error message"));
427
428 wxString msg;
429 msg.Printf(_("unknown error (error code %08x)."), error);
430 return msg;
431 }
432
433 case 0:
434 // we want the error message to start from a lower case letter
435 buffer[0] = wxTolower(buffer[0]);
436
437 return wxString(buffer);
438 }
439 }
440
441 HRASCONN wxDialUpManagerMSW::FindActiveConnection()
442 {
443 // enumerate connections
444 DWORD cbBuf = sizeof(RASCONN);
445 LPRASCONN lpRasConn = (LPRASCONN)malloc(cbBuf);
446 if ( !lpRasConn )
447 {
448 // out of memory
449 return 0;
450 }
451
452 lpRasConn->dwSize = sizeof(RASCONN);
453
454 DWORD nConnections = 0;
455 DWORD dwRet = ERROR_BUFFER_TOO_SMALL;
456
457 while ( dwRet == ERROR_BUFFER_TOO_SMALL )
458 {
459 dwRet = ms_pfnRasEnumConnections(lpRasConn, &cbBuf, &nConnections);
460
461 if ( dwRet == ERROR_BUFFER_TOO_SMALL )
462 {
463 LPRASCONN lpRasConnOld = lpRasConn;
464 lpRasConn = (LPRASCONN)realloc(lpRasConn, cbBuf);
465 if ( !lpRasConn )
466 {
467 // out of memory
468 free(lpRasConnOld);
469
470 return 0;
471 }
472 }
473 else if ( dwRet == 0 )
474 {
475 // ok, success
476 break;
477 }
478 else
479 {
480 // an error occured
481 wxLogError(_("Cannot find active dialup connection: %s"),
482 GetErrorString(dwRet));
483 return 0;
484 }
485 }
486
487 HRASCONN hrasconn;
488
489 switch ( nConnections )
490 {
491 case 0:
492 // no connections
493 hrasconn = 0;
494 break;
495
496 default:
497 // more than 1 connection - we don't know what to do with this
498 // case, so give a warning but continue (taking the first
499 // connection) - the warning is really needed because this function
500 // is used, for example, to select the connection to hang up and so
501 // we may hang up the wrong connection here...
502 wxLogWarning(_("Several active dialup connections found, "
503 "choosing one randomly."));
504 // fall through
505
506 case 1:
507 // exactly 1 connection, great
508 hrasconn = lpRasConn->hrasconn;
509 }
510
511 free(lpRasConn);
512
513 return hrasconn;
514 }
515
516 void wxDialUpManagerMSW::CleanUpThreadData()
517 {
518 if ( m_hThread )
519 {
520 if ( !SetEvent(m_data.hEventQuit) )
521 {
522 wxLogLastError("SetEvent(RasThreadQuit)");
523 }
524
525 CloseHandle(m_hThread);
526
527 m_hThread = 0;
528 }
529
530 if ( m_data.hWnd )
531 {
532 DestroyWindow(m_data.hWnd);
533
534 m_data.hWnd = 0;
535 }
536
537 if ( m_data.hEventQuit )
538 {
539 CloseHandle(m_data.hEventQuit);
540
541 m_data.hEventQuit = 0;
542 }
543
544 if ( m_data.hEventRas )
545 {
546 CloseHandle(m_data.hEventRas);
547
548 m_data.hEventRas = 0;
549 }
550 }
551
552 // ----------------------------------------------------------------------------
553 // connection status
554 // ----------------------------------------------------------------------------
555
556 void wxDialUpManagerMSW::CheckRasStatus()
557 {
558 // use int, not bool to compare with -1
559 int isConnected = FindActiveConnection() != 0;
560 if ( isConnected != ms_isConnected )
561 {
562 if ( ms_isConnected != -1 )
563 {
564 // notify the program
565 NotifyApp(isConnected != 0);
566 }
567 // else: it's the first time we're called, just update the flag
568
569 ms_isConnected = isConnected;
570 }
571 }
572
573 void wxDialUpManagerMSW::NotifyApp(bool connected, bool fromOurselves) const
574 {
575 wxDialUpEvent event(connected, fromOurselves);
576 (void)wxTheApp->ProcessEvent(event);
577 }
578
579 // this function is called whenever the status of any RAS connection on this
580 // machine changes by RAS itself
581 void wxDialUpManagerMSW::OnConnectStatusChange()
582 {
583 // we know that status changed, but we don't know whether we're connected
584 // or not - so find it out
585 CheckRasStatus();
586 }
587
588 // this function is called by our callback which we give to RasDial() when
589 // calling it asynchronously
590 void wxDialUpManagerMSW::OnDialProgress(RASCONNSTATE rasconnstate,
591 DWORD dwError)
592 {
593 if ( !GetDialer() )
594 {
595 // this probably means that CancelDialing() was called and we get
596 // "disconnected" notification
597 return;
598 }
599
600 // we're only interested in 2 events: connected and disconnected
601 if ( dwError )
602 {
603 wxLogError(_("Failed to establish dialup connection: %s"),
604 GetErrorString(dwError));
605
606 // we should still call RasHangUp() if we got a non 0 connection
607 if ( ms_hRasConnection )
608 {
609 ms_pfnRasHangUp(ms_hRasConnection);
610 ms_hRasConnection = 0;
611 }
612
613 ms_dialer = NULL;
614
615 NotifyApp(FALSE /* !connected */, TRUE /* we dialed ourselves */);
616 }
617 else if ( rasconnstate == RASCS_Connected )
618 {
619 ms_isConnected = TRUE;
620 ms_dialer = NULL;
621
622 NotifyApp(TRUE /* connected */, TRUE /* we dialed ourselves */);
623 }
624 }
625
626 // ----------------------------------------------------------------------------
627 // implementation of wxDialUpManager functions
628 // ----------------------------------------------------------------------------
629
630 bool wxDialUpManagerMSW::IsOk() const
631 {
632 return ms_dllRas != 0;
633 }
634
635 bool wxDialUpManagerMSW::Dial(const wxString& nameOfISP,
636 const wxString& username,
637 const wxString& password,
638 bool async)
639 {
640 wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") );
641
642 if ( ms_hRasConnection )
643 {
644 wxFAIL_MSG(_T("there is already an active connection"));
645
646 return TRUE;
647 }
648
649 RASDIALPARAMS rasDialParams;
650 rasDialParams.dwSize = sizeof(rasDialParams);
651 strncpy(rasDialParams.szEntryName, nameOfISP, RAS_MaxEntryName);
652 rasDialParams.szPhoneNumber[0] = '\0';
653 rasDialParams.szCallbackNumber[0] = '\0';
654 rasDialParams.szCallbackNumber[0] = '\0';
655
656 strncpy(rasDialParams.szUserName, username, UNLEN);
657 strncpy(rasDialParams.szPassword, password, PWLEN);
658
659 rasDialParams.szDomain[0] = '*';
660 rasDialParams.szDomain[1] = '\0';
661
662 // apparently, this is not really necessary - passing NULL instead of the
663 // phone book has the same effect
664 #if 0
665 wxString phoneBook;
666 if ( wxGetOsVersion() == wxWINDOWS_NT )
667 {
668 // first get the length
669 UINT nLen = ::GetSystemDirectory(NULL, 0);
670 nLen++;
671
672 if ( !::GetSystemDirectory(phoneBook.GetWriteBuf(nLen), nLen) )
673 {
674 wxLogSysError(_("Cannot find the location of address book file"));
675 }
676
677 phoneBook.UngetWriteBuf();
678
679 // this is the default phone book
680 phoneBook << "\\ras\\rasphone.pbk";
681 }
682 #endif // 0
683
684 // TODO may be we should disable auto check while async dialing is in
685 // progress?
686
687 ms_dialer = this;
688
689 DWORD dwRet = ms_pfnRasDial
690 (
691 (LPRASDIALEXTENSIONS)NULL, // no extended features
692 NULL, // default phone book file (NT only)
693 &rasDialParams,
694 0, // use callback for notifications
695 async ? wxRasDialFunc // the callback
696 : 0, // no notifications - sync operation
697 &ms_hRasConnection
698 );
699
700 if ( dwRet != 0 )
701 {
702 wxLogError(_("Failed to %s dialup connection: %s"),
703 async ? _("initiate") : _("establish"),
704 GetErrorString(dwRet));
705
706 // we should still call RasHangUp() if we got a non 0 connection
707 if ( ms_hRasConnection )
708 {
709 ms_pfnRasHangUp(ms_hRasConnection);
710 ms_hRasConnection = 0;
711 }
712
713 ms_dialer = NULL;
714
715 return FALSE;
716 }
717
718 // for async dialing, we're not yet connected
719 if ( !async )
720 {
721 ms_isConnected = TRUE;
722 }
723
724 return TRUE;
725 }
726
727 bool wxDialUpManagerMSW::IsDialing() const
728 {
729 return GetDialer() != NULL;
730 }
731
732 bool wxDialUpManagerMSW::CancelDialing()
733 {
734 if ( !GetDialer() )
735 {
736 // silently ignore
737 return FALSE;
738 }
739
740 wxASSERT_MSG( ms_hRasConnection, _T("dialing but no connection?") );
741
742 ms_dialer = NULL;
743
744 return HangUp();
745 }
746
747 bool wxDialUpManagerMSW::HangUp()
748 {
749 wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") );
750
751 // we may terminate either the connection we initiated or another one which
752 // is active now
753 HRASCONN hRasConn;
754 if ( ms_hRasConnection )
755 {
756 hRasConn = ms_hRasConnection;
757
758 ms_hRasConnection = 0;
759 }
760 else
761 {
762 hRasConn = FindActiveConnection();
763 }
764
765 if ( !hRasConn )
766 {
767 wxLogError(_("Cannot hang up - no active dialup connection."));
768
769 return FALSE;
770 }
771
772 DWORD dwRet = ms_pfnRasHangUp(hRasConn);
773 if ( dwRet != 0 )
774 {
775 wxLogError(_("Failed to terminate the dialup connection: %s"),
776 GetErrorString(dwRet));
777 }
778
779 ms_isConnected = FALSE;
780
781 return TRUE;
782 }
783
784 bool wxDialUpManagerMSW::IsOnline() const
785 {
786 wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") );
787
788 if ( ms_userSpecifiedOnlineStatus != -1 )
789 {
790 // user specified flag overrides our logic
791 return ms_userSpecifiedOnlineStatus != 0;
792 }
793 else
794 {
795 // return TRUE if there is at least one active connection
796 return FindActiveConnection() != 0;
797 }
798 }
799
800 void wxDialUpManagerMSW::SetOnlineStatus(bool isOnline)
801 {
802 wxCHECK_RET( IsOk(), _T("using uninitialized wxDialUpManager") );
803
804 ms_userSpecifiedOnlineStatus = isOnline;
805 }
806
807 bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
808 {
809 wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") );
810
811 bool ok = ms_pfnRasConnectionNotification != 0;
812
813 if ( ok )
814 {
815 // we're running under NT 4.0, Windows 98 or later and can use
816 // RasConnectionNotification() to be notified by a secondary thread
817
818 // first, see if we don't have this thread already running
819 if ( m_hThread != 0 )
820 {
821 DWORD dwSuspendCount = 2;
822 while ( dwSuspendCount > 1 )
823 {
824 dwSuspendCount = ResumeThread(m_hThread);
825 if ( dwSuspendCount == (DWORD)-1 )
826 {
827 wxLogLastError("ResumeThread(RasThread)");
828
829 ok = FALSE;
830 }
831 }
832
833 if ( ok )
834 {
835 return TRUE;
836 }
837 }
838 }
839
840 // create all the stuff we need to be notified about RAS connection
841 // status change
842
843 if ( ok )
844 {
845 // first create an event to wait on
846 m_data.hEventRas = CreateEvent
847 (
848 NULL, // security attribute (default)
849 FALSE, // manual reset (not)
850 FALSE, // initial state (not signaled)
851 NULL // name (no)
852 );
853 if ( !m_data.hEventRas )
854 {
855 wxLogLastError("CreateEvent(RasStatus)");
856
857 ok = FALSE;
858 }
859 }
860
861 if ( ok )
862 {
863 // create the event we use to quit the thread
864 m_data.hEventQuit = CreateEvent(NULL, FALSE, FALSE, NULL);
865 if ( !m_data.hEventQuit )
866 {
867 wxLogLastError("CreateEvent(RasThreadQuit)");
868
869 CleanUpThreadData();
870
871 ok = FALSE;
872 }
873 }
874
875 if ( ok )
876 {
877 // create a hidden window to receive notification about connections
878 // status change
879 extern wxChar wxPanelClassName[];
880 m_data.hWnd = ::CreateWindow(wxPanelClassName, NULL,
881 0, 0, 0, 0,
882 0, NULL,
883 (HMENU)NULL, wxGetInstance(), 0);
884 if ( !m_data.hWnd )
885 {
886 wxLogLastError("CreateWindow(RasHiddenWindow)");
887
888 CleanUpThreadData();
889
890 ok = FALSE;
891 }
892
893 // and subclass it
894 FARPROC windowProc = MakeProcInstance
895 (
896 (FARPROC)wxRasStatusWindowProc,
897 wxGetInstance()
898 );
899
900 ::SetWindowLong(m_data.hWnd, GWL_WNDPROC, (LONG) windowProc);
901 }
902
903 if ( ok )
904 {
905 // start the secondary thread
906 m_data.dialUpManager = this;
907
908 DWORD tid;
909 m_hThread = CreateThread
910 (
911 NULL,
912 0,
913 (LPTHREAD_START_ROUTINE)wxRasMonitorThread,
914 (void *)&m_data,
915 0,
916 &tid
917 );
918
919 if ( !m_hThread )
920 {
921 wxLogLastError("CreateThread(RasStatusThread)");
922
923 CleanUpThreadData();
924 }
925 }
926
927 if ( ok )
928 {
929 // start receiving RAS notifications
930 DWORD dwRet = ms_pfnRasConnectionNotification
931 (
932 (HRASCONN)INVALID_HANDLE_VALUE,
933 m_data.hEventRas,
934 3 /* RASCN_Connection | RASCN_Disconnection */
935 );
936
937 if ( dwRet != 0 )
938 {
939 wxLogDebug(_T("RasConnectionNotification() failed: %s"),
940 GetErrorString(dwRet));
941
942 CleanUpThreadData();
943 }
944 else
945 {
946 return TRUE;
947 }
948 }
949
950 // we're running under Windows 95 and have to poll ourselves
951 // (or, alternatively, the code above for NT/98 failed)
952 m_timerStatusPolling.Stop();
953 if ( nSeconds == 0 )
954 {
955 // default value
956 nSeconds = 60;
957 }
958 m_timerStatusPolling.Start(nSeconds * 1000);
959
960 return TRUE;
961 }
962
963 void wxDialUpManagerMSW::DisableAutoCheckOnlineStatus()
964 {
965 wxCHECK_RET( IsOk(), _T("using uninitialized wxDialUpManager") );
966
967 if ( m_hThread )
968 {
969 // we have running secondary thread, it's just enough to suspend it
970 if ( SuspendThread(m_hThread) == (DWORD)-1 )
971 {
972 wxLogLastError("SuspendThread(RasThread)");
973 }
974 }
975 else
976 {
977 // even simpler - just stop the timer
978 m_timerStatusPolling.Stop();
979 }
980 }
981
982 // ----------------------------------------------------------------------------
983 // stubs which don't do anything in MSW version
984 // ----------------------------------------------------------------------------
985
986 void wxDialUpManagerMSW::SetWellKnownHost(const wxString& WXUNUSED(hostname),
987 int WXUNUSED(port))
988 {
989 wxCHECK_RET( IsOk(), _T("using uninitialized wxDialUpManager") );
990
991 // nothing to do - we don't use this
992 }
993
994 void wxDialUpManagerMSW::SetConnectCommand(const wxString& WXUNUSED(dial),
995 const wxString& WXUNUSED(hangup))
996 {
997 wxCHECK_RET( IsOk(), _T("using uninitialized wxDialUpManager") );
998
999 // nothing to do - we don't use this
1000 }
1001
1002 // ----------------------------------------------------------------------------
1003 // callbacks
1004 // ----------------------------------------------------------------------------
1005
1006 static DWORD wxRasMonitorThread(wxRasThreadData *data)
1007 {
1008 HANDLE handles[2];
1009 handles[0] = data->hEventRas;
1010 handles[1] = data->hEventQuit;
1011
1012 bool cont = TRUE;
1013 while ( cont )
1014 {
1015 DWORD dwRet = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
1016
1017 switch ( dwRet )
1018 {
1019 case WAIT_OBJECT_0:
1020 // RAS connection status changed
1021 SendMessage(data->hWnd, wxWM_RAS_STATUS_CHANGED,
1022 0, (LPARAM)data);
1023 break;
1024
1025 case WAIT_OBJECT_0 + 1:
1026 cont = FALSE;
1027 break;
1028
1029 case WAIT_FAILED:
1030 wxLogLastError("WaitForMultipleObjects(RasMonitor)");
1031 break;
1032 }
1033 }
1034
1035 return 0;
1036 }
1037
1038 static LRESULT APIENTRY wxRasStatusWindowProc(HWND hWnd, UINT message,
1039 WPARAM wParam, LPARAM lParam)
1040 {
1041 if ( message == wxWM_RAS_STATUS_CHANGED )
1042 {
1043 wxRasThreadData *data = (wxRasThreadData *)lParam;
1044 data->dialUpManager->OnConnectStatusChange();
1045 }
1046
1047 return 0;
1048 }
1049
1050 static void WINAPI wxRasDialFunc(UINT unMsg,
1051 RASCONNSTATE rasconnstate,
1052 DWORD dwError)
1053 {
1054 wxDialUpManagerMSW *dialUpManager = wxDialUpManagerMSW::GetDialer();
1055
1056 wxCHECK_RET( dialUpManager, _T("who started to dial then?") );
1057
1058 dialUpManager->OnDialProgress(rasconnstate, dwError);
1059 }
1060
1061 #endif // wxUSE_DIALUP_MANAGER