Sunday, September 25, 2005

Supporting ActiveSync 4.0

The technology behind the products I've been developing was initially based on ActiveSync and RAPI (Remote API). Now, some of them support TCP/IP connections to a device server using the same component but, for the most part, ActiveSync is still the connection method most customers choose.

My first approach to consuming RAPI was quite simple: a static link to rapi.lib. This meant that all the products were tied to a particular version of ActiveSync and would not run in another. By linking to a specific version's lib file we are actually precluding the possibility of linking to other versions, both older or newer.

It so happens that util recently all applciations were statically linked to ActiveSync 3.8, meaning that you had to install this particular version in order for the applications to run. Incidentally, I got a request from two different customers, one asking for ActiveSync 4.0 support, and the other was asking for version 3.6 support. Now what?

Publishing different versions of the applications was obviously out of the question. There had to be a better way of supporting mutiple versions of RAPI. Then I recalled something I read in one of Doug Boling's books: use dynamic linking. The idea is to dynamically load the RAPI.DLL file and retrieve the entry points to the functions you need by using GetProcAddress. To make things easier, I wrote a very simple helper class, of which I'm highlighting a few snippets.

First, the class declaration:

#pragma once

#include <rapi.h>

class CRemoteAPI
{
public:
CRemoteAPI(void);
~CRemoteAPI(void);

HRESULT InitEx(RAPIINIT* pRapiInit);
HRESULT Uninit();
HRESULT Invoke(LPCWSTR pszDLL,
LPCWSTR pszFUnction,
DWORD cbInput,
BYTE *pInput,
DWORD *pcbOutput,
BYTE **ppOutput,
IRAPIStream **ppIRAPIStream,
DWORD dwReserved);

protected:
HMODULE m_hDll;
};

I'm only including a few methods to illustrate the technique. You should be able to extend your own version with the calls you need.

Let's look at the constructor and destructor:

CRemoteAPI::CRemoteAPI(void)
: m_hDll(NULL)
{
m_hDll = LoadLibrary(_T("RAPI.DLL"));
}


CRemoteAPI::~CRemoteAPI(void)
{
if(m_hDll)
FreeLibrary(m_hDll);
}

As you can see, we dynamically load and unload the RAPI.DLL on the constructor and destructor. You should have no problems in nesting these - you will only be incrementing or decrementing the module load count. Now, let's see how to implement one of the methods:

typedef HRESULT (__stdcall *CERAPIINITEX)(RAPIINIT*);

HRESULT CRemoteAPI::InitEx(RAPIINIT *pRapiInit)
{
HRESULT hr = E_NOTIMPL;
CERAPIINITEX pfnCeRapiInitEx;

if(!m_hDll)
return hr;

pfnCeRapiInitEx =
(CERAPIINITEX)GetProcAddress(m_hDll,
"CeRapiInitEx");
if(pfnCeRapiInitEx)
hr = pfnCeRapiInitEx(pRapiInit);
return hr;
}

You now get the idea on how to implement all other methods. The beauty of this is that by using this approach, you can support all versions of ActiveSync (provided that the target version does implement the function call you need...).

1 comment:

Unknown said...

nice entry!