July 28, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Keeping Your PocketPC Application's Communications Working

  • June 1, 2004
  • By Alex Gusev
  • Send Email »
  • More Articles »

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. Table 1 is a short description of each of its key functions:

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).



Click here for a larger image.

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.

Sample project: RasDemo2002

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.






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel