]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/dialup.cpp
don't use strcpy to copy between overlapping strings, it's undefined operation
[wxWidgets.git] / src / msw / dialup.cpp
index cbdd14594e7f6855a8dd8e203e7afd755c375b9f..3ef963a5f5ab6f04570dc341761fe0f3ad186d74 100644 (file)
     #pragma hdrstop
 #endif
 
-// these functions require Win32
-#if defined(__WIN16__) && wxUSE_DIALUP_MANAGER
-    #undef wxUSE_DIALUP_MANAGER
-    #define wxUSE_DIALUP_MANAGER 0
-#endif // wxUSE_DIALUP_MANAGER && Win16
-
 #if wxUSE_DIALUP_MANAGER
 
 #ifndef WX_PRECOMP
 DEFINE_EVENT_TYPE(wxEVT_DIALUP_CONNECTED)
 DEFINE_EVENT_TYPE(wxEVT_DIALUP_DISCONNECTED)
 
-// Doesn't yet compile under VC++ 4, BC++, Watcom C++: no wininet.h
-#if !defined(__BORLANDC__) && \
+// Doesn't yet compile under VC++ 4, BC++, Watcom C++,
+// Wine: no wininet.h
+#if (!defined(__BORLANDC__) || (__BORLANDC__>=0x550)) && \
     (!defined(__GNUWIN32__) || wxCHECK_W32API_VERSION(0, 5)) && \
     !defined(__GNUWIN32_OLD__) && \
     !defined(__WATCOMC__) && \
+    !defined(__WINE__) && \
     (!defined(__VISUALC__) || (__VISUALC__ >= 1020))
 
 #include <ras.h>
@@ -137,13 +133,26 @@ struct WXDLLEXPORT wxRasThreadData
     wxRasThreadData()
     {
         hWnd = 0;
-        hEventRas = hEventQuit = INVALID_HANDLE_VALUE;
+        hEventRas =
+        hEventQuit = 0;
         dialUpManager = NULL;
     }
 
+    ~wxRasThreadData()
+    {
+        if ( hWnd )
+            DestroyWindow(hWnd);
+
+        if ( hEventQuit )
+            CloseHandle(hEventQuit);
+
+        if ( hEventRas )
+            CloseHandle(hEventRas);
+    }
+
     HWND    hWnd;       // window to send notifications to
-    HANDLE  hEventRas,  // event which RAS signals when status changes
-            hEventQuit; // event which we signal when we terminate
+    HANDLE  hEventRas,  // automatic event which RAS signals when status changes
+            hEventQuit; // manual event which we signal when we terminate
 
     class WXDLLEXPORT wxDialUpManagerMSW *dialUpManager;  // the owner
 };
@@ -202,6 +211,10 @@ private:
     // destroy the thread data and the thread itself
     void CleanUpThreadData();
 
+    // number of times EnableAutoCheckOnlineStatus() had been called minus the
+    // number of times DisableAutoCheckOnlineStatus() had been called
+    int m_autoCheckLevel;
+
     // timer used for polling RAS status
     class WXDLLEXPORT RasTimer : public wxTimer
     {
@@ -213,6 +226,8 @@ private:
 
     private:
         wxDialUpManagerMSW *m_dialUpManager;
+
+        DECLARE_NO_COPY_CLASS(RasTimer)
     } m_timerStatusPolling;
 
     // thread handle for the thread sitting on connection change event
@@ -220,10 +235,10 @@ private:
 
     // data used by this thread and our hidden window to send messages between
     // each other
-    wxRasThreadData m_data;
+    wxRasThreadData *m_data;
 
     // the handle of rasapi32.dll when it's loaded
-    wxPluginManager m_dllRas;
+    wxDynamicLibrary m_dllRas;
 
     // the hidden window we use for passing messages between threads
     static HWND ms_hwndRas;
@@ -231,12 +246,6 @@ private:
     // the handle of the connection we initiated or 0 if none
     static HRASCONN ms_hRasConnection;
 
-    // FIXME: There is probably no reason these really need to
-    //        be static anymore since the dll refcounting is
-    //        handled by wxPluginManager now.  Whether or not
-    //        we still _want_ them to be static is another
-    //        issue entirely..
-
     // the pointers to RAS functions
     static RASDIAL ms_pfnRasDial;
     static RASENUMCONNECTIONS ms_pfnRasEnumConnections;
@@ -266,12 +275,10 @@ private:
     // this flag tells us if we're online
     static int ms_isConnected;
 
-    // this flag is the result of the call to IsAlwaysOnline() (-1 if not
-    // called yet)
-    static int ms_isAlwaysOnline;
-
     // this flag tells us whether a call to RasDial() is in progress
     static wxDialUpManagerMSW *ms_dialer;
+
+    DECLARE_NO_COPY_CLASS(wxDialUpManagerMSW)
 };
 
 // ----------------------------------------------------------------------------
@@ -321,7 +328,6 @@ RASCONNECTIONNOTIFICATION wxDialUpManagerMSW::ms_pfnRasConnectionNotification =
 
 int wxDialUpManagerMSW::ms_userSpecifiedOnlineStatus = -1;
 int wxDialUpManagerMSW::ms_isConnected = -1;
-int wxDialUpManagerMSW::ms_isAlwaysOnline = -1;
 wxDialUpManagerMSW *wxDialUpManagerMSW::ms_dialer = NULL;
 
 // ----------------------------------------------------------------------------
@@ -340,17 +346,19 @@ wxDialUpManager *wxDialUpManager::Create()
 #endif // VC++
 
 wxDialUpManagerMSW::wxDialUpManagerMSW()
-                  : m_timerStatusPolling(this)
-                  , m_dllRas(_T("RASAPI32"))
+                  : m_timerStatusPolling(this),
+                    m_dllRas(_T("RASAPI32"))
 {
     // initialize our data
+    m_autoCheckLevel = 0;
     m_hThread = 0;
+    m_data = new wxRasThreadData;
 
     if ( !m_dllRas.IsLoaded() )
     {
         wxLogError(_("Dial up functions are unavailable because the remote access service (RAS) is not installed on this machine. Please install it."));
     }
-    else if( ms_pfnRasDial == 0 )
+    else if ( !ms_pfnRasDial )
     {
         // resolve the functions we need
 
@@ -537,35 +545,26 @@ void wxDialUpManagerMSW::CleanUpThreadData()
 {
     if ( m_hThread )
     {
-        if ( !SetEvent(m_data.hEventQuit) )
+        if ( !SetEvent(m_data->hEventQuit) )
         {
             wxLogLastError(_T("SetEvent(RasThreadQuit)"));
         }
+        else // sent quit request to the background thread
+        {
+            // the thread still needs m_data so we can't free it here, rather
+            // let the thread do it itself
+            m_data = NULL;
+        }
 
         CloseHandle(m_hThread);
 
         m_hThread = 0;
     }
 
-    if ( m_data.hWnd )
-    {
-        DestroyWindow(m_data.hWnd);
-
-        m_data.hWnd = 0;
-    }
-
-    if ( m_data.hEventQuit )
+    if ( m_data )
     {
-        CloseHandle(m_data.hEventQuit);
-
-        m_data.hEventQuit = 0;
-    }
-
-    if ( m_data.hEventRas )
-    {
-        CloseHandle(m_data.hEventRas);
-
-        m_data.hEventRas = 0;
+        delete m_data;
+        m_data = NULL;
     }
 }
 
@@ -830,12 +829,12 @@ bool wxDialUpManagerMSW::Dial(const wxString& nameOfISP,
 
     DWORD dwRet = ms_pfnRasDial
                   (
-                   (LPRASDIALEXTENSIONS)NULL,   // no extended features
-                   NULL,                  // default phone book file (NT only)
+                   NULL,                    // no extended features
+                   NULL,                    // default phone book file (NT only)
                    &rasDialParams,
-                   0,                     // use callback for notifications
-                   async ? wxRasDialFunc  // the callback
-                         : 0,             // no notifications - sync operation
+                   0,                       // use callback for notifications
+                   async ? (void *)wxRasDialFunc  // cast needed for gcc 3.1
+                         : 0,               // no notifications, sync operation
                    &ms_hRasConnection
                   );
 
@@ -926,20 +925,16 @@ bool wxDialUpManagerMSW::HangUp()
 
 bool wxDialUpManagerMSW::IsAlwaysOnline() const
 {
-    // we cache the result (presumably this won't change while the program is
-    // running!)
-    if ( ms_isAlwaysOnline != -1 )
-    {
-        return ms_isAlwaysOnline != 0;
-    }
+    // assume no permanent connection by default
+    bool isAlwaysOnline = FALSE;
 
-    // try to use WinInet function first
+    // try to use WinInet functions
 
     // NB: we could probably use wxDynamicLibrary here just as well,
     //     but we allow multiple instances of wxDialUpManagerMSW so
     //     we might as well use the ref counted version here too.
 
-    wxPluginManager hDll(_T("WININET"));
+    wxDynamicLibrary hDll(_T("WININET"));
     if ( hDll.IsLoaded() )
     {
         typedef BOOL (WINAPI *INTERNETGETCONNECTEDSTATE)(LPDWORD, DWORD);
@@ -956,45 +951,26 @@ bool wxDialUpManagerMSW::IsAlwaysOnline() const
             if ( pfnInternetGetConnectedState(&flags, 0 /* reserved */) )
             {
                 // there is some connection to the net, see of which type
-                ms_isAlwaysOnline = (flags & INTERNET_CONNECTION_LAN != 0) ||
-                                    (flags & INTERNET_CONNECTION_PROXY != 0);
-            }
-            else
-            {
-                // no Internet connection at all
-                ms_isAlwaysOnline = FALSE;
+                isAlwaysOnline = (flags & (INTERNET_CONNECTION_LAN |
+                                           INTERNET_CONNECTION_PROXY)) != 0;
             }
+            //else: no Internet connection at all
         }
     }
 
-    // did we succeed with WinInet? if not, try something else
-    if ( ms_isAlwaysOnline == -1 )
-    {
-        if ( !IsOnline() )
-        {
-            // definitely no permanent connection because we are not connected
-            // now
-            ms_isAlwaysOnline = FALSE;
-        }
-        else
-        {
-            // of course, having a modem doesn't prevent us from having a
-            // permanent connection as well, but we have to guess somehow and
-            // it's probably more common that a system connected via a modem
-            // doesn't have any other net access, so:
-            ms_isAlwaysOnline = FALSE;
-        }
-    }
-
-    wxASSERT_MSG( ms_isAlwaysOnline != -1, wxT("logic error") );
-
-    return ms_isAlwaysOnline != 0;
+    return isAlwaysOnline;
 }
 
 bool wxDialUpManagerMSW::IsOnline() const
 {
     wxCHECK_MSG( IsOk(), FALSE, wxT("using uninitialized wxDialUpManager") );
 
+    if ( IsAlwaysOnline() )
+    {
+        // always => now
+        return true;
+    }
+
     if ( ms_userSpecifiedOnlineStatus != -1 )
     {
         // user specified flag overrides our logic
@@ -1018,6 +994,12 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
 {
     wxCHECK_MSG( IsOk(), FALSE, wxT("using uninitialized wxDialUpManager") );
 
+    if ( m_autoCheckLevel++ )
+    {
+        // already checking
+        return TRUE;
+    }
+
     bool ok = ms_pfnRasConnectionNotification != 0;
 
     if ( ok )
@@ -1028,22 +1010,13 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
         // first, see if we don't have this thread already running
         if ( m_hThread != 0 )
         {
-            DWORD dwSuspendCount = 2;
-            while ( dwSuspendCount > 1 )
-            {
-                dwSuspendCount = ResumeThread(m_hThread);
-                if ( dwSuspendCount == (DWORD)-1 )
-                {
-                    wxLogLastError(wxT("ResumeThread(RasThread)"));
+            if ( ::ResumeThread(m_hThread) != (DWORD)-1 )
+                return TRUE;
 
-                    ok = FALSE;
-                }
-            }
+            // we're leaving a zombie thread... but what else can we do?
+            wxLogLastError(wxT("ResumeThread(RasThread)"));
 
-            if ( ok )
-            {
-                return TRUE;
-            }
+            ok = FALSE;
         }
     }
 
@@ -1053,14 +1026,14 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
     if ( ok )
     {
         // first create an event to wait on
-        m_data.hEventRas = CreateEvent
-                           (
+        m_data->hEventRas = CreateEvent
+                            (
                              NULL,      // security attribute (default)
-                             FALSE,     // manual reset (not)
+                             FALSE,     // manual reset (no, it is automatic)
                              FALSE,     // initial state (not signaled)
                              NULL       // name (no)
-                           );
-        if ( !m_data.hEventRas )
+                            );
+        if ( !m_data->hEventRas )
         {
             wxLogLastError(wxT("CreateEvent(RasStatus)"));
 
@@ -1070,9 +1043,18 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
 
     if ( ok )
     {
-        // create the event we use to quit the thread
-        m_data.hEventQuit = CreateEvent(NULL, FALSE, FALSE, NULL);
-        if ( !m_data.hEventQuit )
+        // create the event we use to quit the thread: using a manual event
+        // here avoids problems with missing the event if wxDialUpManagerMSW
+        // is created and destroyed immediately, before wxRasStatusWindowProc
+        // starts waiting on the event
+        m_data->hEventQuit = CreateEvent
+                             (
+                                NULL,   // default security
+                                TRUE,   // manual event
+                                FALSE,  // initially non signalled
+                                NULL    // nameless
+                             );
+        if ( !m_data->hEventQuit )
         {
             wxLogLastError(wxT("CreateEvent(RasThreadQuit)"));
 
@@ -1086,8 +1068,8 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
     {
         // create a hidden window to receive notification about connections
         // status change
-        extern const wxChar *wxPanelClassName;
-        ms_hwndRas = ::CreateWindow(wxPanelClassName, NULL,
+        extern const wxChar *wxCanvasClassName;
+        ms_hwndRas = ::CreateWindow(wxCanvasClassName, NULL,
                                     0, 0, 0, 0,
                                     0, NULL,
                                     (HMENU)NULL, wxGetInstance(), 0);
@@ -1101,21 +1083,15 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
         }
 
         // and subclass it
-        FARPROC windowProc = MakeProcInstance
-                             (
-                              (FARPROC)wxRasStatusWindowProc,
-                              wxGetInstance()
-                             );
-
-        ::SetWindowLong(ms_hwndRas, GWL_WNDPROC, (LONG) windowProc);
+        wxSetWindowProc(ms_hwndRas, wxRasStatusWindowProc);
     }
 
-    m_data.hWnd = ms_hwndRas;
+    m_data->hWnd = ms_hwndRas;
 
     if ( ok )
     {
         // start the secondary thread
-        m_data.dialUpManager = this;
+        m_data->dialUpManager = this;
 
         DWORD tid;
         m_hThread = CreateThread
@@ -1123,7 +1099,7 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
                      NULL,
                      0,
                      (LPTHREAD_START_ROUTINE)wxRasMonitorThread,
-                     (void *)&m_data,
+                     (void *)m_data,
                      0,
                      &tid
                     );
@@ -1142,7 +1118,7 @@ bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds)
         DWORD dwRet = ms_pfnRasConnectionNotification
                       (
                         (HRASCONN)INVALID_HANDLE_VALUE,
-                        m_data.hEventRas,
+                        m_data->hEventRas,
                         3 /* RASCN_Connection | RASCN_Disconnection */
                       );
 
@@ -1176,6 +1152,12 @@ void wxDialUpManagerMSW::DisableAutoCheckOnlineStatus()
 {
     wxCHECK_RET( IsOk(), wxT("using uninitialized wxDialUpManager") );
 
+    if ( --m_autoCheckLevel )
+    {
+        // still checking
+        return;
+    }
+
     if ( m_hThread )
     {
         // we have running secondary thread, it's just enough to suspend it
@@ -1238,34 +1220,64 @@ static DWORD wxRasMonitorThread(wxRasThreadData *data)
                 cont = FALSE;
                 break;
 
+            default:
+                wxFAIL_MSG( _T("unexpected return of WaitForMultipleObjects()") );
+                // fall through
+
             case WAIT_FAILED:
-                wxLogLastError(wxT("WaitForMultipleObjects(RasMonitor)"));
-                break;
+#ifdef __WXDEBUG__
+                // using wxLogLastError() from here is dangerous: we risk to
+                // deadlock the main thread if wxLog sends output to GUI
+                DWORD err = GetLastError();
+                wxMessageOutputDebug().Printf
+                (
+                    wxT("WaitForMultipleObjects(RasMonitor) failed: 0x%08lx (%s)"),
+                    err,
+                    wxSysErrorMsg(err)
+                );
+#endif // __WXDEBUG__
+
+                // no sense in continuing, who knows if the handles we're
+                // waiting for even exist yet...
+                return (DWORD)-1;
         }
     }
 
+    // we don't need it any more now and if this thread ran, it is our
+    // responsability to free the data
+    delete data;
+
     return 0;
 }
 
 static LRESULT APIENTRY wxRasStatusWindowProc(HWND hWnd, UINT message,
                                               WPARAM wParam, LPARAM lParam)
 {
-    if ( message == wxWM_RAS_STATUS_CHANGED )
+    switch ( message )
     {
-        wxRasThreadData *data = (wxRasThreadData *)lParam;
-        data->dialUpManager->OnConnectStatusChange();
-    }
-    else if ( message == wxWM_RAS_DIALING_PROGRESS )
-    {
-        wxDialUpManagerMSW *dialUpManager = wxDialUpManagerMSW::GetDialer();
+        case wxWM_RAS_STATUS_CHANGED:
+            {
+                wxRasThreadData *data = (wxRasThreadData *)lParam;
+                data->dialUpManager->OnConnectStatusChange();
+            }
+            break;
 
-        dialUpManager->OnDialProgress((RASCONNSTATE)wParam, lParam);
+        case wxWM_RAS_DIALING_PROGRESS:
+            {
+                wxDialUpManagerMSW *dialMan = wxDialUpManagerMSW::GetDialer();
+
+                dialMan->OnDialProgress((RASCONNSTATE)wParam, lParam);
+            }
+            break;
+
+        default:
+            return ::DefWindowProc(hWnd, message, wParam, lParam);
     }
 
     return 0;
 }
 
-static void WINAPI wxRasDialFunc(UINT unMsg,
+static void WINAPI wxRasDialFunc(UINT WXUNUSED(unMsg),
                                  RASCONNSTATE rasconnstate,
                                  DWORD dwError)
 {
@@ -1277,8 +1289,7 @@ static void WINAPI wxRasDialFunc(UINT unMsg,
                 rasconnstate, dwError);
 }
 
-#endif
-  // __BORLANDC__
+#endif // __BORLANDC__
+
 #endif // wxUSE_DIALUP_MANAGER
 
-// vi:sts=4:sw=4:et