Brian Hart
In the April 1999 issue, Jim Marshall wrote about FTP with WinInet (see "Writing Internet Applications Using MFC"). He pointed out some things that still needed work, and one was Dial-Up Networking (DUN). How do you connect the user to an Internet provider automatically? Jim pointed out that Dial-Up Networking usually (but not always) kicks in automatically when you try to connect to a server. However, the user can still click on things in your window while the connection is going through and potentially muck things up. In this article, Brian Hart explores the Internet Explorer 5 version of WinInet, and the control it offers over Dial-Up Networking.
The version of WinInet that comes with Internet Explorer 5 has some neat functions for dealing with DUN. Your Internet program can share the same dial-up user interface with IE. This goes a long way toward providing a consistent interface to Windows services such as DUN and saving you from prompting the user to connect to the Internet manually (which is a pain for the user), or rolling your own user interface for connecting and showing the progress of the operation.
The WinInet API provides six functions that support dialing the modem and hanging up. These functions are shown in Table 1. (There's a seventh function, InternetSetDialState(), but it's obsolete and shouldn't be used.)
Table 1. The six WinInet functions that support dialing the modem and hanging up.
Function | Description |
InternetAutodial() | Initiates an unattended dial-up connection. |
InternetAutodialHangup() | Disconnects a modem connection initiated by InternetAutodial. |
InternetDial() | Initiates a dial-up connection. |
InternetGetConnectedState() | Retrieves the current state of the Internet connection. |
InternetHangUp() | Disconnects a modem connection initiated by InternetDial. |
InternetGoOnline() | Prompts the user for permission to initiate a dial-up connection to the given URL. |
InternetSetDialState() | Sets the current state of the Internet connection. |
Of these six functions, the three most important are InternetDial(), InternetGetConnectedState(), and InternetHangUp(). InternetDial() displays a modal dialog box and starts a connection to the Internet. It doesn't return until the connection goes through, which prevents the user from doing anything while the connection goes through (a big plus).
InternetGetConnectedState() detects whether the user uses a modem to connect to the Internet or not. Unfortunately, my tests have shown that InternetGetConnectedState()'s behavior is unpredictable; it has said I use a LAN when in fact I dial in to a provider. It's probably confused because I have both a modem and a network adapter in my computer. Hopefully, Microsoft will refine it in later versions. I wouldn't recommend depending solely on what InternetGetConnectedState() has to say, but it can help out, for example, in knowing how to initialize an option.
InternetHangUp() is the third in this trio of functions, and it does what its name implies: hangs up the connection and returns. You could use it to implement an
"AutoDisconnect" feature in your application, perhaps prompting the user to disconnect in your application's ExitInstance() function.
Let's see these functions in action; we'll start by looking at them in depth. Here's the prototype for InternetDial():
DWORD InternetDial(/*in*/ HWND hWndParent,
/*in*/ LPCTSTR lpszConnectoid,
/*in*/ DWORD dwFlags,
/*out*/ LPDWORD lpdwConnection,
/*in*/ DWORD dwReserved);
Looks simple, doesn't it? Notice that its return type is DWORD-it returns ERROR_SUCCESS, indicating success or a Win32 system error code. This is perfect for use with Jim Marshall's technique for turning error codes into human-readable error messages (see "Getting Human Readable Error Messages from Windows" in the May 1999 issue of Visual C++ Developer).
The hWndParent argument specifies the parent window of the dialer UI, shown in Figure 1. Set this to the handle of the main window of your application (use AfxGetMainWnd()->GetSafeHwnd()) to make the UI modal. This prevents the user from clicking in the window and mucking things up while the connection is in progress.
lpszConnectoid specifies the name of the connectoid you want to connect for the user. (Ever wondered what the name is for those entries under the Dial-Up Networking folder you reach from My Computer? They're called connectoids.) Use " and the default IE setting will be used. This is handy, since most users tell IE what connection they prefer to use. If your users don't, adjust your code accordingly.
The last input parameter is a flag, chosen from those presented in Table 2.
Table 2. Flags for the last input parameter.
Flag | Meaning |
INTERNET_AUTODIAL_FORCE_ONLINE | Forces an online connection. If the user clicks Cancel, InternetDial() fails. |
INTERNET_DIAL_UNATTENDED | Lets you dial the connection behind the user's back. The UI will still be shown if the user hasn't selected to connect automatically. Not recommended unless it's part of an automatic operation. |
INTERNET_AUTODIAL_FORCE_UNATTENDED | Lets you dial the connection behind the user's back with no regard for their wishes. The UI isn't shown under any circumstances. Not recommended unless it's part of an automatic operation. |
When this connection is dialed, Windows gives you a tag to keep track of it. This is provided by the out parameter lpdwConnection. Simply declare a DWORD member variable m_dwConnection in your application class and pass the address of it in this parameter. One place you'll use it is when you hang up, later.
The dwReserved argument, of course, should be left alone; it's reserved (hmmm...). Just set it to zero.
Now, let's move on to the second function in our little trio, InternetGetConnectedState(). As I mentioned earlier, InternetGetConnectedState() lets you detect whether the user is using a modem to connect to the Internet or not. Here's what you pass in and get back:
BOOL InternetGetConnectedState(
/*out*/ LPDWORD lpdwFlags,
/*in*/ DWORD dwReserved);
What's significant here is the lpdwFlags parameter, the address of a variable that receives info: it gets the value that tells you how the user connects to the Internet. The values for lpdwFlags are shown in Table 3.
Table 3. Values for lpdwFlags.
Flag | Meaning |
INTERNET_CONNECTION_MODEM | Local system uses a modem to connect to the Internet |
INTERNET_CONNECTION_LAN | Local system uses a local area network to connect to the Internet |
INTERNET_CONNECTION_PROXY | Local system uses a proxy server to connect to the Internet |
INTERNET_CONNECTION_MODEM_BUSY | Local system's modem is busy with a non-Internet (i.e. to a remote network drive) connection |
And finally, there's InternetHangUp(). This function is simple and straightforward:
DWORD InternetHangUp(/*in*/DWORD dwConnection,
/*in*/ DWORD dwReserved);
Pass in the tag you got from InternetDial(). This identifies the connection you want to hang up.
Now, let's look at a sample of how you can put this to use in a program. I used AppWizard to make a little MDI program that doesn't have any document types. I added a member variable, m_dwConnection, to my application class to hold the connection tag. I also added an m_bConnected BOOL variable to hold the success or failure of the connection. Next, I used WizardBar to insert the DoInternetConnect() helper member function.
I added WinInet support to the project by including the WININET.H header file from STDAFX.H:
#include <wininet.h> // Windows Internet functions
I also added a #pragma line that brings in WININET.LIB to STDAFX.H. The linker prefers this to your explictly specifying libraries in the project settings. To link with WININET.LIB, add this line to STDAFX.H:
#pragma comment(lib, "wininet.lib")
Now the code will build properly.
I call DoInternetConnect() in InitInstance():
void CInternetAutoDialApp::InitInstance()
{
//...
// the usual
//...
pMainFrame->ShowWindow(m_nCmdShow)
pMainFrame->UpdateWindow();
// Connect to the Internet
m_bConnected = DoInternetConnect();
return TRUE;
}
You might want to consider adding private member functions to the application class that DoInternetConnect() can call to handle processing that depends on whether the connection was successful or not.
DoInternetConnect() returns TRUE if the connection was successful and FALSE if it wasn't (see Listing 1).
Listing 1. Making the Internet connection.
BOOL CInternetAutoDialApp::DoInternetConnect()
{
DWORD dwResult;
BOOL bResult;
// Dial the user's modem
dwResult = ::InternetDial(AfxGetMainWnd()->GetSafeHwnd(),
", INTERNET_AUTODIAL_FORCE_ONLINE,
&m_dwConnection, 0);
// If the connection went through, dwResult is
// equal to ERROR_SUCCESS
bResult = (dwResult == ERROR_SUCCESS);
return bResult;
}
You could carry on to do your specific processing after a connection is made in this function rather than in InitInstance(), if you prefer.
Connecting to the Internet is only half the task. What about hanging up the modem on exit? I overrode ExitInstance() and called InternetHangUp(), as shown in Listing 2.
Listing 2. Hang up as you leave, if the user agrees.
int CInternetAutoDialApp::ExitInstance()
{
int nResult = CWinApp::ExitInstance();
if (!m_bConnected)
return nResult;
int nChoice = AfxMessageBox("Would you like to disconnect?",
MB_YESNO|MB_ICONEXCLAMATION);
if (nChoice == IDYES)
::InternetHangUp(m_dwConnection, 0);
return nResult;
}
That message box (see Figure 2) is really important. Can you imagine the frustration of a new user who loses the Internet connection after checking mail and doesn't know why? I've seen it, and it's not pretty. Don't copy Outlook's decisions-ask the user before you blow their connection away.
Notes from the rear
And voilą! Remember, this new WinInet functionality only comes with IE 5, which you can download from the Microsoft site. Download it, grab this sample, and take WinInet for a test drive!
Download WINDIAL.exe.
Brian Hart has been programming since age 14 and is the founder of WnDBSoft Software International. His latest project is Internet Navigator 98, a multi-purpose Internet access tool. He's a full-time, first-year physics and mathematics double major at Hamline University, located in Saint Paul, MN. www.wndbsoft.com, brian@wndbsoft.com.