From 09884325fc1f1d582402d736ee33e66fb0e98211 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 1 Oct 1999 22:42:17 +0000 Subject: [PATCH] wxDialUpManager class (first draft, works only under Windows NT for now) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@3784 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/net.h | 161 +++++++ samples/nettest/nettest.cpp | 250 ++++++++++ src/msw/net.cpp | 914 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1325 insertions(+) create mode 100644 include/wx/net.h create mode 100644 samples/nettest/nettest.cpp create mode 100644 src/msw/net.cpp diff --git a/include/wx/net.h b/include/wx/net.h new file mode 100644 index 0000000000..b052796766 --- /dev/null +++ b/include/wx/net.h @@ -0,0 +1,161 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/net.h +// Purpose: Network related wxWindows classes and functions +// Author: Vadim Zeitlin +// Modified by: +// Created: 07.07.99 +// RCS-ID: $Id$ +// Copyright: (c) Vadim Zeitlin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_NET_H +#define _WX_NET_H + +#if wxUSE_DIALUP_MANAGER + +// ---------------------------------------------------------------------------- +// A class which groups functions dealing with connecting to the network from a +// workstation using dial-up access to the net. There is at most one instance +// of this class in the program accessed via GetDialUpManager(). +// ---------------------------------------------------------------------------- + +/* TODO + * + * 1. more configurability for Unix: i.e. how to initiate the connection, how + * to check for online status, &c. + * 2. add a "long Dial(long connectionId = -1)" function which asks the user + * about which connection to dial (this may be done using native dialogs + * under NT, need generic dialogs for all others) and returns the identifier + * of the selected connection (it's opaque to the application) - it may be + * reused later to dial the same connection later (or use strings instead of + * longs may be?) + * 3. add an async version of dialing functions which notify the caller about + * the progress (or may be even start another thread to monitor it) + * 4. the static creation/accessor functions are not MT-safe - but is this + * really crucial? I think we may suppose they're always called from the + * main thread? + */ + +class WXDLLEXPORT wxDialUpManager +{ +public: + // get the pointer to the global wxDialUpManager object + static wxDialUpManager *Get() + { + if ( !ms_dialUpManager ) + { + ms_dialUpManager = Create(); + } + + return ms_dialUpManager; + } + + // could the dialup manager be initialized correctly? If this function + // returns FALSE, no other functions will work neither, so it's a good idea + // to call this function and check its result before calling any other + // wxDialUpManager methods + virtual bool IsOk() const = 0; + + // virtual dtor for any base class + virtual ~wxDialUpManager() { } + + // operations + // ---------- + + // the simplest way to initiate a dial up: this function dials the given + // ISP (exact meaning of the parameter depends on the platform), returns + // TRUE on success or FALSE on failure and logs the appropriate error + // message in the latter case. + virtual bool Dial(const wxString& nameOfISP, + const wxString& username, + const wxString& password) = 0; + + // hang up the currently active dial up connection + virtual bool HangUp() = 0; + + // online status + // ------------- + + // returns TRUE if the computer is connected to the network: under Windows, + // this just means that a RAS connection exists, under Unix we check that + // the "well-known host" (as specified by SetWellKnownHost) is reachable + virtual bool IsOnline() = 0; + + // sometimes the built-in logic for determining the online status may fail, + // so, in general, the user should be allowed to override it. This function + // allows to forcefully set the online status - whatever our internal + // algorithm may think about it. + virtual void SetOnlineStatus(bool isOnline = TRUE) = 0; + + // set misc wxDialUpManager options + // -------------------------------- + + // enable automatical checks for the connection status and sending of + // wxEVT_DIALUP_CONNECTED/wxEVT_DIALUP_DISCONNECTED events. The interval + // parameter is only for Unix where we do the check manually: under + // Windows, the notification about the change of connection status is + // instantenous. + // + // Returns FALSE if couldn't set up automatic check for online status. + virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds = 60) = 0; + + // disable automatic check for connection status change - notice that the + // wxEVT_DIALUP_XXX events won't be sent any more neither. + virtual void DisableAutoCheckOnlineStatus() = 0; + + // under Unix, the value of well-known host is used to check whether we're + // connected to the internet. It's unused under Windows, but this function + // is always safe to call. The default value is www.yahoo.com. + virtual void SetWellKnownHost(const wxString& hostname) = 0; + +private: + friend class WXDLLEXPORT wxApp; + + // only for wxApp usage: clean up. + static void Delete() + { + if ( ms_dialUpManager ) + { + delete ms_dialUpManager; + ms_dialUpManager = (wxDialUpManager *)NULL; + } + } + + // this function should create and return the object of the + // platform-specific class derived from wxDialUpManager. It's implemented + // in the platform-specific source files. + static wxDialUpManager *Create(); + + // the unique instance of this class + static wxDialUpManager *ms_dialUpManager; +}; + +// ---------------------------------------------------------------------------- +// DIALUP events processing +// ---------------------------------------------------------------------------- + +// the event class for the dialup events +class WXDLLEXPORT wxDialUpEvent : public wxEvent +{ +public: + wxDialUpEvent(bool isConnected) : wxEvent(isConnected) + { + SetEventType(isConnected ? wxEVT_DIALUP_CONNECTED + : wxEVT_DIALUP_DISCONNECTED); + } + + // is this a CONNECTED or DISCONNECTED event? + bool IsConnectedEvent() const { return m_id != 0; } +}; + +// the type of dialup event handler function +typedef void (wxObject::*wxDialUpEventFunction)(wxDialUpEvent&); + +// macros to catch dialup events +#define EVT_DIALUP_CONNECTED(func) { wxEVT_DIALUP_CONNECTED, -1, -1, (wxObjectEventFunction) (wxEventFunction) (wxDialUpEventFunction) & func, NULL}, +#define EVT_DIALUP_DISCONNECTED(func) { wxEVT_DIALUP_DISCONNECTED, -1, -1, (wxObjectEventFunction) (wxEventFunction) (wxDialUpEventFunction) & func, NULL}, + +#endif // wxUSE_DIALUP_MANAGER + +#endif // _WX_NET_H diff --git a/samples/nettest/nettest.cpp b/samples/nettest/nettest.cpp new file mode 100644 index 0000000000..b8f8335191 --- /dev/null +++ b/samples/nettest/nettest.cpp @@ -0,0 +1,250 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: net.cpp +// Purpose: wxWindows sample demonstrating network-related functions +// Author: Vadim Zeitlin +// Modified by: +// Created: 07.07.99 +// RCS-ID: $Id$ +// Copyright: (c) Vadim Zeitlin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#ifdef __GNUG__ + #pragma implementation "nettest.cpp" + #pragma interface "nettest.cpp" +#endif + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +// for all others, include the necessary headers (this file is usually all you +// need because it includes almost all "standard" wxWindows headers +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wx/net.h" + +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- + +// Define a new application type, each program should derive a class from wxApp +class MyApp : public wxApp +{ +public: + // override base class virtuals + // ---------------------------- + + // this one is called on application startup and is a good place for the app + // initialization (doing it here and not in the ctor allows to have an error + // return: if OnInit() returns false, the application terminates) + virtual bool OnInit(); + + // event handlers + void OnConnected(wxDialUpEvent& event); + +private: + DECLARE_EVENT_TABLE(); +}; + +// Define a new frame type: this is going to be our main frame +class MyFrame : public wxFrame +{ +public: + // ctor(s) + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); + + // event handlers (these functions should _not_ be virtual) + void OnQuit(wxCommandEvent& event); + void OnAbout(wxCommandEvent& event); + void OnHangUp(wxCommandEvent& event); + void OnDial(wxCommandEvent& event); + + void OnIdle(wxIdleEvent& event); + +private: + // any class wishing to process wxWindows events must use this macro + DECLARE_EVENT_TABLE() +}; + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// IDs for the controls and the menu commands +enum +{ + // menu items + NetTest_Quit = 1, + NetTest_About, + NetTest_HangUp, + NetTest_Dial +}; + +// ---------------------------------------------------------------------------- +// event tables and other macros for wxWindows +// ---------------------------------------------------------------------------- + +BEGIN_EVENT_TABLE(MyApp, wxApp) + EVT_DIALUP_CONNECTED(MyApp::OnConnected) + EVT_DIALUP_DISCONNECTED(MyApp::OnConnected) +END_EVENT_TABLE() + +// the event tables connect the wxWindows events with the functions (event +// handlers) which process them. It can be also done at run-time, but for the +// simple menu events like this the static method is much simpler. +BEGIN_EVENT_TABLE(MyFrame, wxFrame) + EVT_MENU(NetTest_Quit, MyFrame::OnQuit) + EVT_MENU(NetTest_About, MyFrame::OnAbout) + EVT_MENU(NetTest_HangUp, MyFrame::OnHangUp) + EVT_MENU(NetTest_Dial, MyFrame::OnDial) + + EVT_IDLE(MyFrame::OnIdle) +END_EVENT_TABLE() + +// Create a new application object: this macro will allow wxWindows to create +// the application object during program execution (it's better than using a +// static object for many reasons) and also declares the accessor function +// wxGetApp() which will return the reference of the right type (i.e. MyApp and +// not wxApp) +IMPLEMENT_APP(MyApp) + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// the application class +// ---------------------------------------------------------------------------- + +// `Main program' equivalent: the program execution "starts" here +bool MyApp::OnInit() +{ + // Create the main application window + MyFrame *frame = new MyFrame("Minimal wxWindows App", + wxPoint(50, 50), wxSize(450, 340)); + + // Show it and tell the application that it's our main window + frame->Show(TRUE); + SetTopWindow(frame); + + // success: wxApp::OnRun() will be called which will enter the main message + // loop and the application will run. If we returned FALSE here, the + // application would exit immediately. + return TRUE; +} + +void MyApp::OnConnected(wxDialUpEvent& event) +{ + wxMessageBox(event.IsConnectedEvent() ? "Just connected!" + : "Disconnected", + "Dial Up Manager Notification", + wxOK | wxICON_INFORMATION, + GetTopWindow()); +} + +// ---------------------------------------------------------------------------- +// main frame +// ---------------------------------------------------------------------------- + +// frame constructor +MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) + : wxFrame((wxFrame *)NULL, -1, title, pos, size) +{ + // set the frame icon + SetIcon(wxICON(mondrian)); + + // create a menu bar + wxMenu *menuFile = new wxMenu; + + menuFile->Append(NetTest_Dial, "&Dial\tCtrl-D", "Dial default ISP"); + menuFile->Append(NetTest_HangUp, "&HangUp\tCtrl-H", "Hang up modem"); + menuFile->AppendSeparator(); + menuFile->Append(NetTest_About, "&About...\tCtrl-A", "Show about dialog"); + menuFile->AppendSeparator(); + menuFile->Append(NetTest_Quit, "E&xit\tAlt-X", "Quit this program"); + + // now append the freshly created menu to the menu bar... + wxMenuBar *menuBar = new wxMenuBar; + menuBar->Append(menuFile, "&File"); + + // ... and attach this menu bar to the frame + SetMenuBar(menuBar); + + CreateStatusBar(2); +} + + +// event handlers + +void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) +{ + // TRUE is to force the frame to close + Close(TRUE); +} + +void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) +{ + wxString msg; + msg.Printf(_T("This is the network functions test sample.\n" + "© 1999 Vadim Zeitlin")); + + wxMessageBox(msg, _T("About NetTest"), wxOK | wxICON_INFORMATION, this); +} + +void MyFrame::OnHangUp(wxCommandEvent& WXUNUSED(event)) +{ + if ( wxDialUpManager::Get()->HangUp() ) + { + wxLogStatus(this, "Connection was succesfully terminated."); + } + else + { + wxLogStatus(this, "Failed to hang up."); + } +} + +void MyFrame::OnDial(wxCommandEvent& WXUNUSED(event)) +{ + wxLogStatus(this, "Dialing..."); + wxYield(); + wxBeginBusyCursor(); + + if ( wxDialUpManager::Get()->Dial("Free", + "zeitlin", "2ecj86ij") ) + { + wxLogStatus(this, "Connection was succesfully established."); + } + else + { + wxLogStatus(this, "Dialing attempt failed."); + } + + wxEndBusyCursor(); +} + +void MyFrame::OnIdle(wxIdleEvent& WXUNUSED(event)) +{ + static int s_isOnline = -1; // not TRUE nor FALSE + + bool isOnline = wxDialUpManager::Get()->IsOnline(); + if ( s_isOnline != (int)isOnline ) + { + s_isOnline = isOnline; + + SetStatusText(isOnline ? "Online" : "Offline", 1); + } +} diff --git a/src/msw/net.cpp b/src/msw/net.cpp new file mode 100644 index 0000000000..8f10252db7 --- /dev/null +++ b/src/msw/net.cpp @@ -0,0 +1,914 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: msw/net.cpp +// Purpose: MSW implementation of network/dialup classes and functions +// Author: Vadim Zeitlin +// Modified by: +// Created: 07.07.99 +// RCS-ID: $Id$ +// Copyright: (c) Vadim Zeitlin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// for compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #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 + #include "wx/log.h" +#endif + +#include "wx/dynlib.h" + +#include "wx/net.h" + +#include +#include + +#include "wx/msw/private.h" + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// this message is sent by the secondary thread when RAS status changes +#define wxWM_RAS_STATUS_CHANGED (WM_USER + 10010) + +// ---------------------------------------------------------------------------- +// types +// ---------------------------------------------------------------------------- + +// the signatures of RAS functions: all this is quite heavy, but we must do it +// to allow running wxWin programs on machine which don't have RAS installed +// (this does exist) - if we link with rasapi32.lib, the program will fail on +// startup because of the missing DLL... + +#ifndef UNICODE + typedef DWORD (* RASDIAL)( LPRASDIALEXTENSIONS, LPCSTR, LPRASDIALPARAMSA, DWORD, LPVOID, LPHRASCONN ); + typedef DWORD (* RASENUMCONNECTIONS)( LPRASCONNA, LPDWORD, LPDWORD ); + typedef DWORD (* RASENUMENTRIES)( LPCSTR, LPCSTR, LPRASENTRYNAMEA, LPDWORD, LPDWORD ); + typedef DWORD (* RASGETCONNECTSTATUS)( HRASCONN, LPRASCONNSTATUSA ); + typedef DWORD (* RASGETERRORSTRING)( UINT, LPSTR, DWORD ); + typedef DWORD (* RASHANGUP)( HRASCONN ); + typedef DWORD (* RASGETPROJECTIONINFO)( HRASCONN, RASPROJECTION, LPVOID, LPDWORD ); + typedef DWORD (* RASCREATEPHONEBOOKENTRY)( HWND, LPCSTR ); + typedef DWORD (* RASEDITPHONEBOOKENTRY)( HWND, LPCSTR, LPCSTR ); + typedef DWORD (* RASSETENTRYDIALPARAMS)( LPCSTR, LPRASDIALPARAMSA, BOOL ); + typedef DWORD (* RASGETENTRYDIALPARAMS)( LPCSTR, LPRASDIALPARAMSA, LPBOOL ); + typedef DWORD (* RASENUMDEVICES)( LPRASDEVINFOA, LPDWORD, LPDWORD ); + typedef DWORD (* RASGETCOUNTRYINFO)( LPRASCTRYINFOA, LPDWORD ); + typedef DWORD (* RASGETENTRYPROPERTIES)( LPCSTR, LPCSTR, LPRASENTRYA, LPDWORD, LPBYTE, LPDWORD ); + typedef DWORD (* RASSETENTRYPROPERTIES)( LPCSTR, LPCSTR, LPRASENTRYA, DWORD, LPBYTE, DWORD ); + typedef DWORD (* RASRENAMEENTRY)( LPCSTR, LPCSTR, LPCSTR ); + typedef DWORD (* RASDELETEENTRY)( LPCSTR, LPCSTR ); + typedef DWORD (* RASVALIDATEENTRYNAME)( LPCSTR, LPCSTR ); + typedef DWORD (* RASCONNECTIONNOTIFICATION)( HRASCONN, HANDLE, DWORD ); + + static const char gs_funcSuffix = 'A'; +#else // Unicode + typedef DWORD (* RASDIAL)( LPRASDIALEXTENSIONS, LPCWSTR, LPRASDIALPARAMSW, DWORD, LPVOID, LPHRASCONN ); + typedef DWORD (* RASENUMCONNECTIONS)( LPRASCONNW, LPDWORD, LPDWORD ); + typedef DWORD (* RASENUMENTRIES)( LPCWSTR, LPCWSTR, LPRASENTRYNAMEW, LPDWORD, LPDWORD ); + typedef DWORD (* RASGETCONNECTSTATUS)( HRASCONN, LPRASCONNSTATUSW ); + typedef DWORD (* RASGETERRORSTRING)( UINT, LPWSTR, DWORD ); + typedef DWORD (* RASHANGUP)( HRASCONN ); + typedef DWORD (* RASGETPROJECTIONINFO)( HRASCONN, RASPROJECTION, LPVOID, LPDWORD ); + typedef DWORD (* RASCREATEPHONEBOOKENTRY)( HWND, LPCWSTR ); + typedef DWORD (* RASEDITPHONEBOOKENTRY)( HWND, LPCWSTR, LPCWSTR ); + typedef DWORD (* RASSETENTRYDIALPARAMS)( LPCWSTR, LPRASDIALPARAMSW, BOOL ); + typedef DWORD (* RASGETENTRYDIALPARAMS)( LPCWSTR, LPRASDIALPARAMSW, LPBOOL ); + typedef DWORD (* RASENUMDEVICES)( LPRASDEVINFOW, LPDWORD, LPDWORD ); + typedef DWORD (* RASGETCOUNTRYINFO)( LPRASCTRYINFOW, LPDWORD ); + typedef DWORD (* RASGETENTRYPROPERTIES)( LPCWSTR, LPCWSTR, LPRASENTRYW, LPDWORD, LPBYTE, LPDWORD ); + typedef DWORD (* RASSETENTRYPROPERTIES)( LPCWSTR, LPCWSTR, LPRASENTRYW, DWORD, LPBYTE, DWORD ); + typedef DWORD (* RASRENAMEENTRY)( LPCWSTR, LPCWSTR, LPCWSTR ); + typedef DWORD (* RASDELETEENTRY)( LPCWSTR, LPCWSTR ); + typedef DWORD (* RASVALIDATEENTRYNAME)( LPCWSTR, LPCWSTR ); + typedef DWORD (* RASCONNECTIONNOTIFICATION)( HRASCONN, HANDLE, DWORD ); + + static const char gs_funcSuffix = 'W'; +#endif // ASCII/Unicode + +// structure passed to the secondary thread +struct wxRasThreadData +{ + wxRasThreadData() + { + hWnd = 0; + hEventRas = hEventQuit = INVALID_HANDLE_VALUE; + dialUpManager = NULL; + } + + HWND hWnd; // window to send notifications to + HANDLE hEventRas, // event which RAS signals when status changes + hEventQuit; // event which we signal when we terminate + + class WXDLLEXPORT wxDialUpManagerMSW *dialUpManager; // the owner +}; + +// ---------------------------------------------------------------------------- +// wxDialUpManager class for MSW +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxDialUpManagerMSW : public wxDialUpManager +{ +public: + // ctor & dtor + wxDialUpManagerMSW(); + virtual ~wxDialUpManagerMSW(); + + // implement base class pure virtuals + virtual bool IsOk() const; + virtual bool Dial(const wxString& nameOfISP, + const wxString& username, + const wxString& password); + virtual bool HangUp(); + virtual bool IsOnline(); + virtual void SetOnlineStatus(bool isOnline = TRUE); + virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds); + virtual void DisableAutoCheckOnlineStatus(); + virtual void SetWellKnownHost(const wxString& hostname); + + // for RasTimer + void CheckRasStatus(bool firstTime = FALSE); + + // for wxRasStatusWindowProc + void OnConnectStatusChange(); + +private: + // return the error string for the given RAS error code + wxString GetErrorString(DWORD error); + + // find the (first) handle of the active connection + HRASCONN FindActiveConnection(); + + // notify the application about status change + void NotifyApp(bool connected) const; + + // destroy the thread data and the thread itself + void CleanUpThreadData(); + + // timer used for polling RAS status + class RasTimer : public wxTimer + { + public: + RasTimer(wxDialUpManagerMSW *dialUpManager) + { m_dialUpManager = dialUpManager; } + + virtual void Notify() { m_dialUpManager->CheckRasStatus(); } + + private: + wxDialUpManagerMSW *m_dialUpManager; + } m_timerStatusPolling; + + // thread handle for the thread sitting on connection change event + HANDLE m_hThread; + + // data used by this thread and our hidden window to send messages between + // each other + wxRasThreadData m_data; + + // the handle of the connection we initiated or 0 if none + static HRASCONN ms_hRasConnection; + + // the use count of rasapi32.dll + static int ms_nDllCount; + + // the handle of rasapi32.dll when it's loaded + static wxDllType ms_dllRas; + + // the pointers to RAS functions + static RASDIAL ms_pfnRasDial; + static RASENUMCONNECTIONS ms_pfnRasEnumConnections; + static RASENUMENTRIES ms_pfnRasEnumEntries; + static RASGETCONNECTSTATUS ms_pfnRasGetConnectStatus; + static RASGETERRORSTRING ms_pfnRasGetErrorString; + static RASHANGUP ms_pfnRasHangUp; + static RASGETPROJECTIONINFO ms_pfnRasGetProjectionInfo; + static RASCREATEPHONEBOOKENTRY ms_pfnRasCreatePhonebookEntry; + static RASEDITPHONEBOOKENTRY ms_pfnRasEditPhonebookEntry; + static RASSETENTRYDIALPARAMS ms_pfnRasSetEntryDialParams; + static RASGETENTRYDIALPARAMS ms_pfnRasGetEntryDialParams; + static RASENUMDEVICES ms_pfnRasEnumDevices; + static RASGETCOUNTRYINFO ms_pfnRasGetCountryInfo; + static RASGETENTRYPROPERTIES ms_pfnRasGetEntryProperties; + static RASSETENTRYPROPERTIES ms_pfnRasSetEntryProperties; + static RASRENAMEENTRY ms_pfnRasRenameEntry; + static RASDELETEENTRY ms_pfnRasDeleteEntry; + static RASVALIDATEENTRYNAME ms_pfnRasValidateEntryName; + + // this function is not supported by Win95 + static RASCONNECTIONNOTIFICATION ms_pfnRasConnectionNotification; + + // if this flag is different from -1, it overrides IsOnline() + static int ms_userSpecifiedOnlineStatus; +}; + +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +static LRESULT APIENTRY wxRasStatusWindowProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam); + +static DWORD wxRasMonitorThread(wxRasThreadData *data); + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// init the static variables +// ---------------------------------------------------------------------------- + +wxDialUpManager *wxDialUpManager::ms_dialUpManager = NULL; + +HRASCONN wxDialUpManagerMSW::ms_hRasConnection = 0; + +int wxDialUpManagerMSW::ms_nDllCount = 0; +wxDllType wxDialUpManagerMSW::ms_dllRas = 0; + +RASDIAL wxDialUpManagerMSW::ms_pfnRasDial = 0; +RASENUMCONNECTIONS wxDialUpManagerMSW::ms_pfnRasEnumConnections = 0; +RASENUMENTRIES wxDialUpManagerMSW::ms_pfnRasEnumEntries = 0; +RASGETCONNECTSTATUS wxDialUpManagerMSW::ms_pfnRasGetConnectStatus = 0; +RASGETERRORSTRING wxDialUpManagerMSW::ms_pfnRasGetErrorString = 0; +RASHANGUP wxDialUpManagerMSW::ms_pfnRasHangUp = 0; +RASGETPROJECTIONINFO wxDialUpManagerMSW::ms_pfnRasGetProjectionInfo = 0; +RASCREATEPHONEBOOKENTRY wxDialUpManagerMSW::ms_pfnRasCreatePhonebookEntry = 0; +RASEDITPHONEBOOKENTRY wxDialUpManagerMSW::ms_pfnRasEditPhonebookEntry = 0; +RASSETENTRYDIALPARAMS wxDialUpManagerMSW::ms_pfnRasSetEntryDialParams = 0; +RASGETENTRYDIALPARAMS wxDialUpManagerMSW::ms_pfnRasGetEntryDialParams = 0; +RASENUMDEVICES wxDialUpManagerMSW::ms_pfnRasEnumDevices = 0; +RASGETCOUNTRYINFO wxDialUpManagerMSW::ms_pfnRasGetCountryInfo = 0; +RASGETENTRYPROPERTIES wxDialUpManagerMSW::ms_pfnRasGetEntryProperties = 0; +RASSETENTRYPROPERTIES wxDialUpManagerMSW::ms_pfnRasSetEntryProperties = 0; +RASRENAMEENTRY wxDialUpManagerMSW::ms_pfnRasRenameEntry = 0; +RASDELETEENTRY wxDialUpManagerMSW::ms_pfnRasDeleteEntry = 0; +RASVALIDATEENTRYNAME wxDialUpManagerMSW::ms_pfnRasValidateEntryName = 0; +RASCONNECTIONNOTIFICATION wxDialUpManagerMSW::ms_pfnRasConnectionNotification = 0; + +int wxDialUpManagerMSW::ms_userSpecifiedOnlineStatus = -1; + +// ---------------------------------------------------------------------------- +// ctor and dtor: the dynamic linking happens here +// ---------------------------------------------------------------------------- + +// the static creator function is implemented here +wxDialUpManager *wxDialUpManager::Create() +{ + return new wxDialUpManagerMSW; +} + +#ifdef __VISUALC__ + // warning about "'this' : used in base member initializer list" - so what? + #pragma warning(disable:4355) +#endif // VC++ + +wxDialUpManagerMSW::wxDialUpManagerMSW() + : m_timerStatusPolling(this) +{ + if ( !ms_nDllCount++ ) + { + // load the RAS library + ms_dllRas = wxDllLoader::LoadLibrary("RASAPI32"); + if ( !ms_dllRas ) + { + wxLogError(_("Dial up functions are unavailable because the " + "remote access service (RAS) is not installed " + "on this machine. Please install it.")); + } + else + { + // resolve the functions we need + + // this will contain the name of the function we failed to resolve + // if any at the end + const char *funcName = NULL; + + // get the function from rasapi32.dll and abort if it's not found + #define RESOLVE_RAS_FUNCTION(type, name) \ + ms_pfn##name = (type)wxDllLoader::GetSymbol(ms_dllRas, \ + wxString(#name) + gs_funcSuffix); \ + if ( !ms_pfn##name ) \ + { \ + funcName = #name; \ + goto exit; \ + } + + // a variant of above macro which doesn't abort if the function is + // not found in the DLL + #define RESOLVE_OPTIONAL_RAS_FUNCTION(type, name) \ + ms_pfn##name = (type)wxDllLoader::GetSymbol(ms_dllRas, \ + wxString(#name) + gs_funcSuffix); + + RESOLVE_RAS_FUNCTION(RASDIAL, RasDial); + RESOLVE_RAS_FUNCTION(RASENUMCONNECTIONS, RasEnumConnections); + RESOLVE_RAS_FUNCTION(RASENUMENTRIES, RasEnumEntries); + RESOLVE_RAS_FUNCTION(RASGETCONNECTSTATUS, RasGetConnectStatus); + RESOLVE_RAS_FUNCTION(RASGETERRORSTRING, RasGetErrorString); + RESOLVE_RAS_FUNCTION(RASHANGUP, RasHangUp); + RESOLVE_RAS_FUNCTION(RASGETPROJECTIONINFO, RasGetProjectionInfo); + RESOLVE_RAS_FUNCTION(RASCREATEPHONEBOOKENTRY, RasCreatePhonebookEntry); + RESOLVE_RAS_FUNCTION(RASEDITPHONEBOOKENTRY, RasEditPhonebookEntry); + RESOLVE_RAS_FUNCTION(RASSETENTRYDIALPARAMS, RasSetEntryDialParams); + RESOLVE_RAS_FUNCTION(RASGETENTRYDIALPARAMS, RasGetEntryDialParams); + RESOLVE_RAS_FUNCTION(RASENUMDEVICES, RasEnumDevices); + RESOLVE_RAS_FUNCTION(RASGETCOUNTRYINFO, RasGetCountryInfo); + RESOLVE_RAS_FUNCTION(RASGETENTRYPROPERTIES, RasGetEntryProperties); + RESOLVE_RAS_FUNCTION(RASSETENTRYPROPERTIES, RasSetEntryProperties); + RESOLVE_RAS_FUNCTION(RASRENAMEENTRY, RasRenameEntry); + RESOLVE_RAS_FUNCTION(RASDELETEENTRY, RasDeleteEntry); + RESOLVE_RAS_FUNCTION(RASVALIDATEENTRYNAME, RasValidateEntryName); + + RESOLVE_OPTIONAL_RAS_FUNCTION(RASCONNECTIONNOTIFICATION, RasConnectionNotification); + + // keep your preprocessor name space clean + #undef RESOLVE_RAS_FUNCTION + #undef RESOLVE_OPTIONAL_RAS_FUNCTION + +exit: + if ( funcName ) + { + wxLogError(_("The version of remote access service (RAS) " + "installed on this machine is too old, please " + "upgrade (the following required function is " + "missing: %s)."), funcName); + + wxDllLoader::UnloadLibrary(ms_dllRas); + ms_dllRas = 0; + } + } + } + + // initialize our data + m_hThread = 0; + + // enable auto check by default + EnableAutoCheckOnlineStatus(0); +} + +wxDialUpManagerMSW::~wxDialUpManagerMSW() +{ + CleanUpThreadData(); + + if ( !--ms_nDllCount ) + { + // unload the RAS library + wxDllLoader::UnloadLibrary(ms_dllRas); + ms_dllRas = 0; + } +} + +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +wxString wxDialUpManagerMSW::GetErrorString(DWORD error) +{ + wxCHECK_MSG( IsOk(), "", _T("using uninitialized wxDialUpManager") ); + + wxChar buffer[512]; // this should be more than enough according to MS docs + DWORD dwRet = ms_pfnRasGetErrorString(error, buffer, WXSIZEOF(buffer)); + switch ( dwRet ) + { + case ERROR_INVALID_PARAMETER: + // this was a standard Win32 error probably + return wxString(wxSysErrorMsg(error)); + + default: + { + wxLogSysError(dwRet, _("Failed to retrieve text of RAS " + "error message")); + + wxString msg; + msg.Printf(_("unknown error (error code %08x)."), error); + return msg; + } + + case 0: + // we want the error message to start from a lower case letter + buffer[0] = wxTolower(buffer[0]); + + return wxString(buffer); + } +} + +HRASCONN wxDialUpManagerMSW::FindActiveConnection() +{ + wxCHECK_MSG( IsOk(), 0, _T("using uninitialized wxDialUpManager") ); + + // enumerate connections + DWORD cbBuf = sizeof(RASCONN); + LPRASCONN lpRasConn = (LPRASCONN)malloc(cbBuf); + if ( !lpRasConn ) + { + // out of memory + return 0; + } + + lpRasConn->dwSize = sizeof(RASCONN); + + DWORD nConnections = 0; + DWORD dwRet = ERROR_BUFFER_TOO_SMALL; + + while ( dwRet == ERROR_BUFFER_TOO_SMALL ) + { + dwRet = ms_pfnRasEnumConnections(lpRasConn, &cbBuf, &nConnections); + + if ( dwRet == ERROR_BUFFER_TOO_SMALL ) + { + LPRASCONN lpRasConnOld = lpRasConn; + lpRasConn = (LPRASCONN)realloc(lpRasConn, cbBuf); + if ( !lpRasConn ) + { + // out of memory + free(lpRasConnOld); + + return 0; + } + } + else if ( dwRet == 0 ) + { + // ok, success + break; + } + else + { + // an error occured + wxLogError(_("Cannot find active dialup connection: %s"), + GetErrorString(dwRet)); + return 0; + } + } + + HRASCONN hrasconn; + + switch ( nConnections ) + { + case 0: + // no connections + hrasconn = 0; + break; + + default: + // more than 1 connection - we don't know what to do with this + // case, so give a warning but continue (taking the first + // connection) - the warning is really needed because this function + // is used, for example, to select the connection to hang up and so + // we may hang up the wrong connection here... + wxLogWarning(_("Several active dialup connections found, " + "choosing one randomly.")); + // fall through + + case 1: + // exactly 1 connection, great + hrasconn = lpRasConn->hrasconn; + } + + free(lpRasConn); + + return hrasconn; +} + +void wxDialUpManagerMSW::CleanUpThreadData() +{ + if ( m_hThread ) + { + if ( !SetEvent(m_data.hEventQuit) ) + { + wxLogLastError("SetEvent(RasThreadQuit)"); + } + + CloseHandle(m_hThread); + + m_hThread = 0; + } + + if ( m_data.hWnd ) + { + DestroyWindow(m_data.hWnd); + + m_data.hWnd = 0; + } + + if ( m_data.hEventQuit ) + { + CloseHandle(m_data.hEventQuit); + + m_data.hEventQuit = 0; + } + + if ( m_data.hEventRas ) + { + CloseHandle(m_data.hEventRas); + + m_data.hEventRas = 0; + } +} + +// ---------------------------------------------------------------------------- +// connection status +// ---------------------------------------------------------------------------- + +void wxDialUpManagerMSW::CheckRasStatus(bool firstTime) +{ + static int s_connected = -1; + + // use int, not bool to compare with -1 + int connected = FindActiveConnection() != 0; + if ( connected != s_connected ) + { + if ( (s_connected == -1) || firstTime ) + { + // it's the first time we're called, just update the flag + } + else + { + // notify the program + NotifyApp(connected != 0); + } + + s_connected = connected; + } +} + +void wxDialUpManagerMSW::NotifyApp(bool connected) const +{ + wxDialUpEvent event(connected); + (void)wxTheApp->ProcessEvent(event); +} + +void wxDialUpManagerMSW::OnConnectStatusChange() +{ + // we know that status changed, but we don't know whether we're connected + // or not - so find it out + NotifyApp(FindActiveConnection() != 0); +} + +// ---------------------------------------------------------------------------- +// implementation of wxDialUpManager functions +// ---------------------------------------------------------------------------- + +bool wxDialUpManagerMSW::IsOk() const +{ + return ms_nDllCount != 0; +} + +bool wxDialUpManagerMSW::Dial(const wxString& nameOfISP, + const wxString& username, + const wxString& password) +{ + wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") ); + + RASDIALPARAMS rasDialParams; + rasDialParams.dwSize = sizeof(rasDialParams); + strncpy(rasDialParams.szEntryName, nameOfISP, RAS_MaxEntryName); + rasDialParams.szPhoneNumber[0] = '\0'; + rasDialParams.szCallbackNumber[0] = '*'; + rasDialParams.szCallbackNumber[0] = '\0'; + + rasDialParams.szUserName[0] = '\0'; + rasDialParams.szPassword[0] = '\0'; + rasDialParams.szDomain[0] = '*'; + rasDialParams.szDomain[1] = '\0'; + + wxString phoneBook; + if ( wxGetOsVersion() == wxWINDOWS_NT ) + { + // first get the length + UINT nLen = ::GetSystemDirectory(NULL, 0); + nLen++; + + if ( !::GetSystemDirectory(phoneBook.GetWriteBuf(nLen), nLen) ) + { + wxLogSysError(_("Cannot find the location of address book file")); + } + + phoneBook.UngetWriteBuf(); + + // this is the default phone book + phoneBook << "\\ras\\rasphone.pbk"; + } + + ms_hRasConnection = 0; + DWORD dwRet = ms_pfnRasDial + ( + (LPRASDIALEXTENSIONS)NULL, + phoneBook, // phone book file (NT only) + &rasDialParams, + 0, // ignored because of next entry + NULL, // no notifications - synchronous operations + &ms_hRasConnection + ); + + if ( dwRet != 0 ) + { + wxLogError(_("Failed to establish dialup connection: %s"), + GetErrorString(dwRet)); + + return FALSE; + } + + return TRUE; +} + +bool wxDialUpManagerMSW::HangUp() +{ + wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") ); + + // we may terminate either the connection we initiated or another one which + // is active now + HRASCONN hRasConn = ms_hRasConnection ? ms_hRasConnection + : FindActiveConnection(); + + if ( !hRasConn ) + { + wxLogError(_("Cannot hang up - no active dialup connection.")); + + return FALSE; + } + + DWORD dwRet = ms_pfnRasHangUp(hRasConn); + if ( dwRet != 0 ) + { + wxLogError(_("Failed to terminate the dialup connection: %s"), + GetErrorString(dwRet)); + } + + return TRUE; +} + +bool wxDialUpManagerMSW::IsOnline() +{ + wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") ); + + if ( ms_userSpecifiedOnlineStatus != -1 ) + { + // user specified flag overrides our logic + return ms_userSpecifiedOnlineStatus != 0; + } + else + { + // return TRUE if there is at least one active connection + return FindActiveConnection() != 0; + } +} + +void wxDialUpManagerMSW::SetOnlineStatus(bool isOnline) +{ + wxCHECK_RET( IsOk(), _T("using uninitialized wxDialUpManager") ); + + ms_userSpecifiedOnlineStatus = isOnline; +} + +bool wxDialUpManagerMSW::EnableAutoCheckOnlineStatus(size_t nSeconds) +{ + wxCHECK_MSG( IsOk(), FALSE, _T("using uninitialized wxDialUpManager") ); + + bool ok = ms_pfnRasConnectionNotification != 0; + + if ( ok ) + { + // we're running under NT 4.0, Windows 98 or later and can use + // RasConnectionNotification() to be notified by a secondary thread + + // 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("ResumeThread(RasThread)"); + + ok = FALSE; + } + } + + if ( ok ) + { + return TRUE; + } + } + } + + // create all the stuff we need to be notified about RAS connection + // status change + + if ( ok ) + { + // first create an event to wait on + m_data.hEventRas = CreateEvent + ( + NULL, // security attribute (default) + FALSE, // manual reset (not) + FALSE, // initial state (not signaled) + NULL // name (no) + ); + if ( !m_data.hEventRas ) + { + wxLogLastError("CreateEvent(RasStatus)"); + + ok = FALSE; + } + } + + if ( ok ) + { + // create the event we use to quit the thread + m_data.hEventQuit = CreateEvent(NULL, FALSE, FALSE, NULL); + if ( !m_data.hEventQuit ) + { + wxLogLastError("CreateEvent(RasThreadQuit)"); + + CleanUpThreadData(); + + ok = FALSE; + } + } + + if ( ok ) + { + // create a hidden window to receive notification about connections + // status change + extern wxChar wxPanelClassName[]; + m_data.hWnd = ::CreateWindow(wxPanelClassName, NULL, + 0, 0, 0, 0, + 0, NULL, + (HMENU)NULL, wxGetInstance(), 0); + if ( !m_data.hWnd ) + { + wxLogLastError("CreateWindow(RasHiddenWindow)"); + + CleanUpThreadData(); + + ok = FALSE; + } + + // and subclass it + FARPROC windowProc = MakeProcInstance + ( + (FARPROC)wxRasStatusWindowProc, + wxGetInstance() + ); + + ::SetWindowLong(m_data.hWnd, GWL_WNDPROC, (LONG) windowProc); + } + + if ( ok ) + { + // start the secondary thread + m_data.dialUpManager = this; + + DWORD tid; + m_hThread = CreateThread + ( + NULL, + 0, + (LPTHREAD_START_ROUTINE)wxRasMonitorThread, + (void *)&m_data, + 0, + &tid + ); + + if ( !m_hThread ) + { + wxLogLastError("CreateThread(RasStatusThread)"); + + CleanUpThreadData(); + } + } + + if ( ok ) + { + // start receiving RAS notifications + DWORD dwRet = ms_pfnRasConnectionNotification + ( + (HRASCONN)INVALID_HANDLE_VALUE, + m_data.hEventRas, + 3 /* RASCN_Connection | RASCN_Disconnection */ + ); + + if ( dwRet != 0 ) + { + wxLogDebug(_T("RasConnectionNotification() failed: %s"), + GetErrorString(dwRet)); + + CleanUpThreadData(); + } + else + { + return TRUE; + } + } + + // we're running under Windows 95 and have to poll ourselves + // (or, alternatively, the code above for NT/98 failed) + CheckRasStatus(TRUE /* first time */); + m_timerStatusPolling.Stop(); + if ( nSeconds == 0 ) + { + // default value + nSeconds = 60; + } + m_timerStatusPolling.Start(nSeconds * 1000); + + return TRUE; +} + +void wxDialUpManagerMSW::DisableAutoCheckOnlineStatus() +{ + wxCHECK_RET( IsOk(), _T("using uninitialized wxDialUpManager") ); + + if ( m_hThread ) + { + // we have running secondary thread, it's just enough to suspend it + if ( SuspendThread(m_hThread) == (DWORD)-1 ) + { + wxLogLastError("SuspendThread(RasThread)"); + } + } + else + { + // even simpler - just stop the timer + m_timerStatusPolling.Stop(); + } +} + +void wxDialUpManagerMSW::SetWellKnownHost(const wxString& WXUNUSED(hostname)) +{ + wxCHECK_RET( IsOk(), _T("using uninitialized wxDialUpManager") ); + + // nothing to do - we don't use this +} + +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +static DWORD wxRasMonitorThread(wxRasThreadData *data) +{ + HANDLE handles[2]; + handles[0] = data->hEventRas; + handles[1] = data->hEventQuit; + + bool cont = TRUE; + while ( cont ) + { + DWORD dwRet = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + + switch ( dwRet ) + { + case WAIT_OBJECT_0: + // RAS connection status changed + SendMessage(data->hWnd, wxWM_RAS_STATUS_CHANGED, + 0, (LPARAM)data); + break; + + case WAIT_OBJECT_0 + 1: + cont = FALSE; + break; + + case WAIT_FAILED: + wxLogLastError("WaitForMultipleObjects(RasMonitor)"); + break; + } + } + + return 0; +} + +static LRESULT APIENTRY wxRasStatusWindowProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + if ( message == wxWM_RAS_STATUS_CHANGED ) + { + wxRasThreadData *data = (wxRasThreadData *)lParam; + data->dialUpManager->OnConnectStatusChange(); + } + + return 0; +} + +#endif // wxUSE_DIALUP_MANAGER -- 2.47.2