Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using the Connection Manager

0.00/5 (No votes)
30 Jul 2010 1  
How to effectively use the Connection Manager API to connect to an arbitrary network.

Connection Manager is connecting

Contents

Introduction

About once a month, somebody in the MSDN mobile development forums asks a variation on the question: "How can I choose which network adapter my mobile application uses?" The answer is Connection Manager. In this article, we will explore different methods of using the Connection Manager API to establish communications with arbitrary networks. The attached sample application will list all Connection Manager entries, and demonstrates connecting to any of them.

What is Connection Manager?

Your typical Windows Mobile device has lots of different connectivity options like WiFi, Cellular, Bluetooth, IrDa, etc... Microsoft realized early in the development of Pocket PC that this would produce a host of variables that any application wishing to communicate with the outside world would have to contend with:

  1. Each of them communicates at different speeds.
  2. At any given time, only a subset of them will be available. If you're on the highway, WiFi isn't likely to work.
  3. They can cost money. Some cellular data plans charge by the megabyte.
  4. Some may communicate with different networks. Your Bluetooth connection to a printer won't get you to the Internet.

Connection Manager was created to transparently manage all of these variables so that application developers can focus on what they want to send and not how they want to send it.

You can even see what state the Connection Manager is in by looking at the title bar. These screenshots show a Windows Mobile 6.1 device in the various stages of connecting to a WAP network.

Connection Manager is disconnected:

Connection Manager is disconnected

Connection Manager is connecting to a GPRS WAP network:

Connection Manager is connecting

Connection Manager is connected to an EDGE GPRS WAP network:

Connection Manager is connected

Establishing a Connection

There are three ways of defining the type of network connection you wish to make with Connection Manager:

  1. Connect to a Particular Network - You know what network you want to use (e.g., the cellular WAP network).
  2. Connect to a Particular URL - You know the URL of the host you wish to communicate with (e.g., http://www.codeproject.com).
  3. Connect to a Particular Interface - You know the network adapter you want to use (e.g., your WiFi adapter)

Connecting to a Particular Network

This is the simplest case as we can use one of the four pre-defined meta-networks:

  1. IID_DestNetInternet - The Internet! (requires fully-qualified domain names).
  2. IID_DestNetCorp - Same as the Internet, but also supports proxy servers, SOCKS, VPN, and WINS.
  3. IID_DestNetWAP - Your basic cellular data network.
  4. IID_DestNetSecureWAP - A cellular data network that requires authentication (PAP, CHAP, etc...).

Note: When using the pre-defined GUIDs, be sure to include InitGuid.h before you include connmgr.h, or you will get linker errors.

error LNK2001: unresolved external symbol IID_DestNetInternet

We have two choices of ways to establish our connection: ConnMgrEstablishConnectionSync() and ConnMgrEstablishConnection(). The synchronous function will block until the connection is made or the timeout is reached. The asynchronous version will post a message to a specified HWND when the connection is complete. For simplicity's sake, we will use ConnMgrEstablishConnectionSync() in this example.

CONNMGR_CONNECTIONINFO info = { 0 };
info.cbSize = sizeof( CONNMGR_CONNECTIONINFO );
info.dwParams = CONNMGR_PARAM_GUIDDESTNET;
info.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;

// allow connection manager to use any proxy it needs to make the connection
info.dwFlags = CONNMGR_FLAG_PROXY_HTTP | 
               CONNMGR_FLAG_PROXY_SOCKS4 | 
               CONNMGR_FLAG_PROXY_SOCKS5 | 
               CONNMGR_FLAG_PROXY_WAP;

// specify the connection destination GUID
info.guidDestNet = IID_DestNetInternet;

DWORD status = 0;
HANDLE connection = NULL;

// Establish the connection. hr will either be E_FAIL or S_OK. Extended error 
// information is stored in the status variable.
HRESULT hr = ::ConnMgrEstablishConnectionSync( &info, 
                                               &connection, 
                                               30000, 
                                               &status );
if( S_OK == hr )
{
    // use the connection wisely, then release it when you are finished
    ::ConnMgrReleaseConnection( connection, 1 );
}

Connecting to a Particular URL

This is a fairly common scenario: you know the host you want to connect to, and you don't care how you get there. For this, we will use ConnMgrMapURL() to ask the connection planner to compute the best route to a given URL. With the default connection manager settings (i.e., no specific routes configured), the following URL patterns will direct you to the given destination meta-networks:

Pattern Destination Example
*://*.*/* IID_DestNetInternet http://www.codeproject.com
*://*/* IID_DestNetCorp http://codeproject
wsp://*/* IID_DestNetWAP wap://codeproject
wsps://*/* IID_DestNetSecureWAP wsps://codeproject
Note: For a complete description of the Connection Manager's URL mapping process, see Using Connection Manager URL Mappings and CM_Mappings Configuration Service Provider on MSDN.

Let's revisit our previous example and alter it to support for URL mapping:

const wchar_t* url = L"http://www.codeproject.com";

CONNMGR_CONNECTIONINFO info = { 0 };
info.cbSize = sizeof( CONNMGR_CONNECTIONINFO );
info.dwParams = CONNMGR_PARAM_GUIDDESTNET;
info.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;

// allow connection manager to use any proxy it needs to make the connection
info.dwFlags = CONNMGR_FLAG_PROXY_HTTP | 
               CONNMGR_FLAG_PROXY_SOCKS4 | 
               CONNMGR_FLAG_PROXY_SOCKS5 | 
               CONNMGR_FLAG_PROXY_WAP;

// use the connection planner to map our URL to a destination network
if( S_OK == ::ConnMgrMapURL( url, &info.guidDestNet, NULL ) )
{
    DWORD status = 0;
    HANDLE connection = NULL;
    
    // Establish the connection. hr will either be E_FAIL or S_OK. Extended error 
    // information is stored in the status variable.
    HRESULT hr = ::ConnMgrEstablishConnectionSync( &info, 
                                                   &connection, 
                                                   30000, 
                                                   &status );
    if( S_OK == hr )
    {
        // use the connection wisely, then release it when you are finished
        ::ConnMgrReleaseConnection( connection, 1 );
    }
}

Connect with a Particular Interface

This method is useful when you or your user knows which interface they want to use. For example, you definitely want to communicate over the WiFi interface even though a desktop-passthrough interface is available. There are two methods to achieve this:

  1. Connect by interface name - If you want to give the user the option to select the connection interface, this would be the preferred method as "My AT&T Network" is much easier for a user to understand than the seemingly random string of hex characters that is a GUID.
  2. Connect by interface GUID - This method involves slightly less code, and so may be preferred if your application performs the network selection programmatically.

Connect by Interface Name

The name of the interface is given by the szAdapterName parameter of CONNMGR_CONNECTION_DETAILED_STATUS. For some interfaces, though, that parameter may be NULL. In that case, use the szDescription parameter. For tips on retrieving this structure, fast-forward to Reading the Status of All Connections.

Once you know the name of the interface you wish to connect with, you can use the ConnMgrMapConRef() API to map that name to a GUID, as follows:

// Get the interface you want to connect with. This can
// be a programmatic choice or a user selection.
const CONNMGR_CONNECTION_DETAILED_STATUS* status = GetDesiredInterface();

CONNMGR_CONNECTIONINFO info = { 0 };
info.cbSize = sizeof( CONNMGR_CONNECTIONINFO );
info.dwParams = CONNMGR_PARAM_GUIDDESTNET;
info.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;

// allow connection manager to use any proxy it needs to make the connection
info.dwFlags = CONNMGR_FLAG_PROXY_HTTP | 
               CONNMGR_FLAG_PROXY_SOCKS4 | 
               CONNMGR_FLAG_PROXY_SOCKS5 | 
               CONNMGR_FLAG_PROXY_WAP;

// Get the destination meta-network GUID for the adapter with the given name. If the
// adapter doesn't have a name, the description usually works. If the adapter is a proxy,
// be sure to set the correct reference type.
if( S_OK == ::ConnMgrMapConRef( 
    ( CM_CONNTYPE_PROXY == status->dwType ) ? ConRefType_PROXY : ConRefType_NAP, 
    ( NULL != status->szAdapterName ) ? status->szAdapterName : status->szDescription, 
    &info.guidDestNet ) )
{
    DWORD status = 0;
    HANDLE connection = NULL;
    
    // Establish the connection. hr will either be E_FAIL or S_OK. Extended error 
    // information is stored in the status variable.
    HRESULT hr = ::ConnMgrEstablishConnectionSync( &info, 
                                                   &connection, 
                                                   30000, 
                                                   &status );
    if( S_OK == hr )
    {
        // use the connection wisely, then release it when you are finished
        ::ConnMgrReleaseConnection( connection, 1 );
    }
}

Connect by Interface GUID

The destination network for a particular interface is given by the dwDestNet parameter of CONNMGR_CONNECTION_DETAILED_STATUS. Since it is already in GUID format, we don't need to use one of the mapping functions. We will use it directly:

// Get the interface you want to connect with.
// This can be a programmatic choice or a user selection.
const CONNMGR_CONNECTION_DETAILED_STATUS* status = GetDesiredInterface();

CONNMGR_CONNECTIONINFO info = { 0 };
info.cbSize = sizeof( CONNMGR_CONNECTIONINFO );
info.dwParams = CONNMGR_PARAM_GUIDDESTNET;
info.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;

// allow connection manager to use any proxy it needs to make the connection
info.dwFlags = CONNMGR_FLAG_PROXY_HTTP | 
               CONNMGR_FLAG_PROXY_SOCKS4 | 
               CONNMGR_FLAG_PROXY_SOCKS5 | 
               CONNMGR_FLAG_PROXY_WAP;
info.guidDestNet = status->dwDestNet;

DWORD status = 0;
HANDLE connection = NULL;

// Establish the connection. hr will either be E_FAIL or S_OK. Extended error 
// information is stored in the status variable.
HRESULT hr = ::ConnMgrEstablishConnectionSync( &info, 
                                               &connection, 
                                               30000, 
                                               &status );
if( S_OK == hr )
{
    // use the connection wisely, then release it when you are finished
    ::ConnMgrReleaseConnection( connection, 1 );
}

Reading the Status of All Connections

The status is stored in a linked list of CONNMGR_CONNECTION_DETAILED_STATUS structures that can be retrieved with the ConnMgrQueryDetailedStatus() function. I have a construct I like to use for this purpose that I will share. It provides the strong exception guarantee in that it will not leak memory or alter your program state if it fails. Note that at the end, rather than doing a full copy of the internal buffer to the supplied buffer, we just do a swap, which is far more efficient.

/// Populate the given buffer with the current status of all adapters registered
/// with connection manager.
/// Returns NULL on failure.
const CONNMGR_CONNECTION_DETAILED_STATUS* GetAdaptersStatus( std::vector< BYTE >* buffer )
{
    std::vector< BYTE > int_buffer( sizeof( CONNMGR_CONNECTION_DETAILED_STATUS ) );
    DWORD buffer_size = int_buffer.size();

    CONNMGR_CONNECTION_DETAILED_STATUS* status = 
        reinterpret_cast< CONNMGR_CONNECTION_DETAILED_STATUS* >( & int_buffer.front() );

    HRESULT hr = ::ConnMgrQueryDetailedStatus( status, &buffer_size );
    if( HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) == hr )
    {
        int_buffer.resize( buffer_size );
        status = 
          reinterpret_cast< CONNMGR_CONNECTION_DETAILED_STATUS* >( & int_buffer.front() );
        hr = ::ConnMgrQueryDetailedStatus( status, &buffer_size );
    }

    if( S_OK == hr )
    {
        buffer->swap( int_buffer );
        return status;
    }
    return NULL;
}

// retrieve the current status.
std::vector< BYTE > buffer;
const CONNMGR_CONNECTION_DETAILED_STATUS* status = GetAdaptersStatus( &buffer );

// iterate over the CONNMGR_CONNECTION_DETAILED_STATUS linked list
const CONNMGR_CONNECTION_DETAILED_STATUS* cur = status;
for( cur; NULL != cur; cur = cur->pNext )
{
    // ...
}

Further Reading

This article just scratches the surface of Connection Manager. If you're interested in other topics, I recommend the following articles:

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here