Keeping Your PocketPC Application's Communications Working

It's odd to say that communications play an important role in a PDA's life cycle. Prior to PocketPC 2000, your applications might use a RAS API to connect to the external world.

Pocket PC 2002 and later brought a new communication model: Connection Manager API. While I believe that Microsoft had good intentions in changing this communication approach, for many existing applications using RAS to connect to Internet and other types of communication, this was a cut off. RAS just stopped working!

You are still able to connect via RAS, but you'll never see your connections from a Windows GUI. Additionally, there are no documented APIs to create RAS connections programmatically. For an end user, the communication processes has become much more transparent. Unfortunately, not all users want to configure their devices manually. In this article, you will get a short overview of the Connection Manager API and learn how to revive existing RAS applications.

The Connection Manager API

This Connection Manager API is compact enough. The following is a short description of each of its key functions:

Function Description
Helper Functions
ConnMgrApiReadyEvent Returns a handle to an event, which becomes signaled when the Connection Manager is ready to be used
ConnMgrEnumDestinations Enumerates the list of the available networks
ConnMgrProviderMessage Provides the means to exchange information with planners and providers
Connection Related Functions
ConnMgrConnectionStatus Returns the status of the current connection
ConnMgrEstablishConnection Creates a connection request
ConnMgrEstablishConnectionSync Creates a connection request but only returns information after the connection has been established or has failed
ConnMgrMapURL Maps a URL to the destination globally unique identifier (GUID) of the network to which the device will connect
ConnMgrReleaseConnection Deletes the specified connection request, an action that can potentially drop the physical connection
ConnMgrSetConnectionPriority Changes the priority of the current connection
Scheduled Connection Functions
ConnMgrRegisterScheduledConnection Registers a scheduled connection. This connection will stay active even after rebooting the device.
ConnMgrUnregisterScheduledConnection Unregisters a scheduled connection.

The SDK has good samples that show the usage of most of the listed functions. You can see CMHELPER for details. I believe that you will find these functions are suitable, intuitive, and simple.

Let's briefly overview a main concept and all these three function groups. First, the user has two ways to be connected: either to "Work" or to "Internet." Then, he may set up each target to use different available hardware resources. All this means that we may have several "sets" of such settings, and decide which one to use in each situation (see Figure 1 - settings/connections screen). These settings are named "destination network." Each destination has its own GUID, thus you'll be able to recognize them. In addition, there are so-called "providers" to support RAS, VPN, and proxy connections.

Here we're facing to the first group of functions. In some cases, you need to know when Connection Manager Layer is ready, so ConnMgrApiReadyEvent gives you an event for this purpose. Next, you may enumerate existing destinations to get their names and GUIDs:

// Enumerate Destinations
HRESULT hr = 0;
int nIndex = 0;
CONNMGR_DESTINATION_INFO DestInfo;
WCHAR wszDestGUIDStr[128];
while ( (hr = ConnMgrEnumDestinations(nIndex,&DestInfo)) == 0 )
{
   StringFromGUID2(DestInfo.guid, wszDestGUIDStr,
                   sizeof(wszDestGUIDStr));
   m_DestCombo.AddString(DestInfo.szDescription);
   m_DestCombo.SetItemData(nIndex,nIndex);
   nIndex++;
}

In other words, just increase an index until ConnMgrEnumDestinations returns a failure. And finally, you may interact with the connection service provider (CSP) to exchange additional information by calling ConnMgrProviderMessage. As you may see in CMHELPER sample, this funtion is used to retrieve proxy information:

#include <connmgr_proxy.h>

const GUID IID_ConnPrv_IProxyExtension =
      { 0xaf96b0bd, 0xa481, 0x482c, { 0xa0, 0x94, 0xa8, 0x44, 0x87,
        0x67, 0xa0, 0xc0 } };

// hConnection is the handle returned from ConnMgrEstablishConnection
PROXY_CONFIG pc = { 0 };
HRESULT Result = ;
        ConnMgrProviderMessage( hConnection,
            &IID_ConnPrv_IProxyExtension,
            NULL, 0, 0,(PBYTE)&pc, sizeof(pc));
if (Result==S_OK)
   TRACE(TEXT("Proxy: %s\n"),pc.szProxyServer);
else if (Result==E_NOINTERFACE)
   TRACETEXT("No Proxy\n"));
else
   TRACE(TEXT("Error querying proxy\n"));

Actually, you may define any valid GUID as IID_ConnPrv_IProxyExtension. Among the standard CSPs on PocketPC 2002, you will find CSPRAS.DLL, CSPPROXY.DLL, and CSPNET.DLL—exactly according to connection types available via the WinCE GUI. We will discuss the RAS provider later in this article.

Now, let's take a look at a connection establishing flow. The real surprise here is the ConnMgrMapURL function. It takes the URL to be connected to and returns the destination network GUID. Thus, the most suitable resource is selected.

HRESULT GetNetworkFromPath(LPCTSTR pszPath)
{
  if( pszPath )
  {
    if( m_pszPath )
      delete m_pszPath;
    m_pszPath = new TCHAR[lstrlen(pszPath)+1];
    if( m_pszPath == NULL )
      return E_OUTOFMEMORY;
    lstrcpy(m_pszPath, pszPath);
  }
  return ConnMgrMapURL(m_pszPath, &m_gNetwork, 0);
}

After detecting a destination GUID, the application is ready to actually connect. It may be done either synchronously or asynchronously (by using ConnMgrEstablishConnectionSync or ConnMgrEstablishConnection, respectively). The following code sample shows the most effective way to do it (refer to CMHELPER in SDK):

DWORD ConnectionThread()
{
    HANDLE hThisThread=m_hConnectionThread;
    CONNMGR_CONNECTIONINFO ConnInfo={0};
    ConnInfo.cbSize=sizeof(ConnInfo);
    ConnInfo.dwParams=CONNMGR_PARAM_GUIDDESTNET;
    ConnInfo.dwFlags=GetProxy() ? CONNMGR_FLAG_PROXY_HTTP: 0;
    ConnInfo.dwPriority=CONNMGR_PRIORITY_USERINTERACTIVE ;
    ConnInfo.guidDestNet = GetNetworkGuid();

    HRESULT hr = ConnMgrEstablishConnection(&ConnInfo,
                                            &m_hConnection);
    if( FAILED( hr ) )
    {
        DoConnectingError();
        SetCache(FALSE);
    }
    else
    {
        DoEstablishingConnection();

        HANDLE hObjects[2];
        hObjects[0]=m_hConnection;
        hObjects[1]=m_hThreadStop;
        BOOL    bStop=FALSE;

        ResetEvent(m_hThreadStop);

        while( bStop == FALSE )
        {
            DWORD dwResult = WaitForMultipleObjects( 2, hObjects,
                                                     FALSE,
                                                     INFINITE);

            if (dwResult == (WAIT_OBJECT_0))
            {
                HRESULT hr;
                DWORD   dwStatus;
                hr=ConnMgrConnectionStatus(m_hConnection,&dwStatus);
                m_dwStatus = dwStatus;
                if( SUCCEEDED(hr))
                {
                    if( DoStatusUpdate(m_dwStatus) != S_OK )
                        bStop=TRUE;
                }
                else
                {
                    m_dwStatus=hr;
                    bStop=TRUE;
                }
            }
            else    // failures, or signalled to stop.
            {
                bStop = TRUE;
                ResetEvent(m_hThreadStop);
            }
        }
    }

    DoReleaseConnection();

    // Release the connection, caching if we should.
    if( m_hConnection )
    {
        ConnMgrReleaseConnection(m_hConnection, GetCache() );
    }

    CloseHandle(hThisThread);

    return GetStatus();
}

This sample is self-documented enough, so just play with it to test underwater stones. In general, it's pretty similar to the RAS stuff.

And finally, the last group of functions deals with scheduled connections. Before Pocket PC 2002, you might use a CeRunAppAtTime call to start the desired application. Scheduled connections may be attractive when you need to run something automatically; for example, to download a large amount of data at night. All this story is managed by filling a SCHEDULEDCONNECTIONINFO structure and the corresponding Register/Unregister API calls. SCHEDULEDCONNECTIONINFO is defined as such:

typedef struct _SCHEDULEDCONNECTIONINFO
{
    GUID guidDest;               // @field Guid of network
    UINT64 uiStartTime;          // @field Starting time, same ref
                                 // as filetime
    UINT64 uiEndTime;            // @field Ending time, same ref
                                 // as filetime
    UINT64 uiPeriod;             // @field Period between schedule
                                 // attempts
    TCHAR szAppName[MAX_PATH];   // @field App name to execute when
                                 // scheduled
    TCHAR szCmdLine[MAX_PATH];   // @field Cmd line to execute when
                                 // scheduled
    TCHAR szToken[32];           // @field Unique token identifying
                                 // this scheduled connection
    BOOL bPiggyback;             // @field If true, execute app
                                 // whenever network is available
} SCHEDULEDCONNECTIONINFO;

All you need is only to fill it in by the desired values—and go forward! Next, a tiny sample demonstrates all the stuff:

   HRESULT ScheduledConnection(STSREMTIME& stStart,
                               STSREMTIME& stEnd)
    {
        FILETIME ftStart, ftEnd;
        SystemTimeToFileTime(&stStart,&ftStart);
        SystemTimeToFileTime(&stEnd,&ftEnd);
        
        SCHEDULEDCONNECTIONINFO sci;

        sci.guidDest    = GetNetworkGuid();
        sci.uiStartTime = ftStart;
        sci.uiEndTime   = ftEnd;
        sci.uiPeriod    = 10000000000;
        _tcscpy(sci.szAppName,"TestApp.exe");
        _tcscpy(sci.szCmdLine,"-i");
        sci.bPiggyback = TRUE;
        
        HRESULT hr = ConnMgrRegisterScheduledConnection(&sci);
        return hr;
    }

Well, obviously that's not all you will want to know about the Connection Manager API, but it's definitely enough to start using it. Now, let's see what is possible to do with existing RAS applications to give them a chance to work correctly.

Reviving RAS Stuff

As I've said before, one of the goals of this article is to show how to set up connection data programmatically. You are able to manage a RAS phonebook without any problems, so the only thing left (hopefully) is to let Connection Manager know about such connections. To do this, first we should investigate the device's (or emulator's) Registry.

After a short surf through the Registry keys, you will discover a "magic point." There is a key, HKLM\SOFTWARE\Microsoft\ConnMgr, that keeps all important parameters (see Figure 1).

This key contains information regarding destinations, providers, and planners. As you see, "Test Conenction" is put under the relevant CSP as:

HKLM\SOFTWARE\Microsoft\ConnMgr\Providers\<PROVIDER GUID>\
     Connections\Test Connection

If you check to whom this "PROVIDER GUID" belongs, you will find that that's CSPRAS.DLL. Therefore, a workaround is to put info about our own stuff at this place. Our "Test Connection" key contains in turn several values that define its behavior. You may find short info about it in the following table:

Value Description
EntryType 0 - 'modem', 1 - 'vpn'
DestId The destination network GUID is stored here
SrcId In case of a 'vpn' connection, put the GUID of "Default Internet Settings" here

You need to create the above keys and values manually when building your RAS connection. Such a workaround works both on Pocket PC 2002 and Pocket PC 2003.

The attached sample project illustrates the approaches described above. I believe the sample will give you ideas of what to do and how. For simplicity, this code is left as simple as possible so not all functionality for the application is fully supported in the code.

About the Author

Alex Gusev started to play with mainframes in the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.



Downloads

Comments

  • Similar problem on my Windows CE 5.0 machine.

    Posted by backyard on 03/28/2007 12:22pm

    ConnMgrEstablishConnectionsSync won't work because the following entries are not in the registry after a call to RasSetEntryProperties. HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\ConnectionGUID HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\SecureLevel HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\Secure HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\AlwaysOn HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\RequirePw HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\Enabled HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\EntryType HKEY_LOCAL_MACHINE\Comm\ConnMgr\Providers\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}\Connections\TEST\DestId I can add those without a problem, but what do I put in that key: ConnectionGUID?

    • re: Similar problem on my Windows CE 5.0 machine.

      Posted by alex_gusev on 03/28/2007 01:25pm

      use CoCreateGuid() to generate it

      Reply
    • re: Similar problem on my Windows CE 5.0 machine.

      Posted by backyard on 03/28/2007 01:02pm

      RasSetEntryProperties does not return a GUID. Can I find the GUID here? HKEY_LOCAL_USER\COMM\RASBOOK\TEST

      Reply
    • re: Similar problem on my Windows CE 5.0 machine.

      Posted by alex_gusev on 03/28/2007 12:38pm

      simply add generated (by your app) GUID for this connection

      Reply
    Reply
  • Rasdemo in C#

    Posted by centrino on 03/23/2005 07:40am

    Hello, is there some example of rasdemo for c# ?

    • The C# code would be greatly appreciated! :)

      Posted by chinatree on 03/16/2006 01:31am

      Since I've never done this before, could you publish the C# code so that I can also see how the C++ is used from managed code?

      Reply
    • RE: Rasdemo in C#

      Posted by alex_gusev on 03/23/2005 07:50am

      C# demo does not differ from C++, all calls to Connection Manager API can be wrapped via PInvoke, registry stuff is easy also

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • You probably have several goals for your patient portal of choice. Is "community" one of them? With a bevy of vendors offering portal solutions, it can be challenging for a hospital to know where to start. Fortunately, YourCareCommunity helps ease the decision-making process. Read this white paper to learn more. "3 Ways Clinicians can Leverage a Patient Portal to Craft a Healthcare Community" is a published document owned by www.medhost.com

  • It's time high-level executives and IT compliance officers recognize and acknowledge the danger of malicious insiders, an increased attack surface and the potential for breaches caused by employee error or negligence. See why there is extra emphasis on insider threats.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds