Desktop Connection Notifications

The ActiveSync Manager provides the desktop with two different ways of receiving notifications when a Pocket PC device is either connected or disconnected from it:

  1. The desktop registry. You can use the registry on a desktop machine that has ActiveSync installed on it to set up which applications are to be run when a device is connected or disconnected.

  2. COM-based notification. You can implement a COM object that can be registered with ActiveSync; it will receive notifications when various device connection events occur.

Although using the registry to launch an application is the easiest way to handle a connection notification, no other information about the connection event is provided to you. You should create a COM object notification if more details are required.

Using the Desktop Registry for Connection Notifications

To have an application launch when a connection is made with the desktop, you need to create a new string value underneath the following registry entry:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services\
   AutoStartOnConnect

It is recommended that the key name for the new string value should uniquely represent the application you are launching, along with the company name. The data value should point to the full path and name, and include any command-line arguments for the application you want to launch. If you include any arguments, you must also wrap the full path in double quotes.

For example, to launch the application devicebackup.exe when a connection is established, the registry will look like the following:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE
  Services\AutoStartOnConnect]
  "BionicFrogDeviceBackup"="\"C:\\Program
  Files\\BionicFrog\\devicebackup.exe\""

To have an application launch when a device is disconnected from the desktop, you can place a new registry string value underneath the following key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services\
   AutoStartOnDisconnect

Like the AutoStartOnConnect key, the disconnect registry key also takes a string value with the application path in order to run.

COM Desktop Notifications

Two interfaces are used to perform notification of connection events when using COM:

  1. The IDccMan interface is already implemented by ActiveSync, and is used to register your IDccManSink object. It provides your application with some control of the communication aspects of ActiveSync.

  2. The IDccManSink interface is what you are required to implement in order to receive notifications from IDccMan. The object's functions are called by ActiveSync whenever connection events occur.

The interfaces that you will use to work with the notification objects are prototyped in the dccole.h header file.

Implementing IDccManSink

The IDccManSink interface is the only interface that your application needs to implement in order to receive notifications from the ActiveSync Manager. Table 9.27 describes the methods supported by IDccManSink that are called during the various states of a device's connection to the desktop.

All of the methods that IDccManSink supports are pretty self-explanatory?each is called by IDccMan whenever a particular event takes place. The only method that has any useful additional information is the IDccManSink::OnLogIpAddr(DWORD dwIpAddr) method, which will pass you the connecting device's IP address when a communications link has been established.

Table 9.27. IDccManSink Methods

Method

Description

OnLogActive()

Notification when a connection is established between a device and ActiveSync

OnLogAnswered()

Notification when ActiveSync has detected a device

OnLogDisconnection()

Notification when a connection has been terminated between the device and ActiveSync

OnLogError()

Notification when ActiveSync has failed to start communications between the desktop and the device

OnLogInActive()

Notification when ActiveSync is in a disconnected state

OnLogIpAddr()

Notification when an IP address has been established for communications between the device and the desktop

OnLogListen()

Notification that a connection is waiting to be established

OnLogTerminated()

Notification when ActiveSync has been shut down

The IDccMan Object

The IDccMan interface is implemented by the ActiveSync Manager, and enables you to register your own IDccManSink interface to receive notifications from it. Table 9.28 describes the methods that are implemented by the IDccMan object.

Table 9.28. IDccMan Methods

Method

Description

Advise()

Registers an IDccManSink object for notification messages

ShowCommSettings()

Shows the ActiveSync Communications Settings dialog box

Unadvise()

Prevents an IDccManSink object from receiving any further notification messages

To register a new notification object with ActiveSync, you must pass a pointer to the IDccManSink interface that you implemented to the IDccMan::Advise() function. It is defined as follows:

HRESULT IDccMan::Advise(IDccManSink *pDccSink,
  DWORD *pdwContext);

The first parameter is a pointer to the notification object that will be used by IDccMan to send notifications to. The pdwContext parameter will be filled in with a DWORD value that uniquely identifies the object you passed into pDccSink. The value that you are returned must be used to call the IDccMan::Unadvise() function when you are finished receiving notifications.

You can also tell ActiveSync to display the Communication Configuration dialog box by using the following function:

HRESULT IDccMan::ShowCommSettings();

The only other function that IDccMan provides is what you use to let ActiveSync know you are no longer interested in receiving notification. The IDccMan::Unadvise() function takes a single parameter, which is the context value that was returned from your call to IDccMan::Advise(). The function is defined as follows:

HRESULT IDccMan::Unadvise(DWORD dwContext);
Using COM-Based Notifications

The process for using COM-based notification is relatively simple. Your application needs to first call CoInitializeEx() and CoCreateInstance() in order to get a pointer to the IDccMan object that ActiveSync has implemented.

The code for getting the IDccMan interface pointer is as follows:

// Get the IDccMan Interface
IDccMan *pDccMan = NULL;
HRESULT hr = S_OK;

hr = CoCreateInstance(CLSID_DccMan, NULL, CLSCTX_SERVER,
  IID_IDccMan, (LPVOID *)&pDccMan);
if(FAILED(hr))
   return 0;

Once you have received the pointer, you can call into the IDccMan::Advise() function to register the IDccManSink you have implemented in your application.

For example, you can register your object for notification as follows:

// Hook up the notification
DWORD dwContext = 0;
hr = pDccMan->Advise(pDccManSink, &dwContext);

Once the application is ready to stop receiving notifications, remember to call the IDccMan::UnAdvise() function, as well as clean up the other COM objects you used:

// Clean up
if(pDccMan) {
   pDccMan->Unadvise(dwContext);
   pDccMan->Release();
}

The implementation for a basic IDccManSink interface is shown in the following example code:

// CeNotify.h
#include <windows.h>
#include <initguid.h>
#include <dccole.h>
#include "resource.h"

INT_PTR CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM
  wParam, LPARAM lParam);

// IDccManSink
class CDccSink:public IDccManSink {
private:
   long m_lRef;
public:
   CDccSink();
   ~CDccSink();

   // IUnknown
   STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv);
   STDMETHODIMP_(ULONG) AddRef();
   STDMETHODIMP_(ULONG) Release();

   // IDccManSink
   STDMETHODIMP OnLogIpAddr(DWORD dwIpAddr);
   STDMETHODIMP OnLogTerminated();
   STDMETHODIMP OnLogActive();
   STDMETHODIMP OnLogInactive();
   STDMETHODIMP OnLogAnswered();
   STDMETHODIMP OnLogListen();
   STDMETHODIMP OnLogDisconnection();
   STDMETHODIMP OnLogError();
};

/////////////////////////////////////////////////
// CeNotify.cpp
#include "cenotify.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE
  hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
   // Initalize COM
   CoInitialize(NULL);

   // Get the IDccMan interface
   IDccMan *pDccMan = NULL;
   HRESULT hr = S_OK;

   hr = CoCreateInstance(CLSID_DccMan, NULL, CLSCTX_SERVER,
      IID_IDccMan, (LPVOID *)&pDccMan);
   if(FAILED(hr))
      return 0;

   // Get an instance of our IDccManSink
   CDccSink *pDccSink = new CDccSink();
   IDccManSink *pDccManSink = NULL;
   hr = pDccSink->QueryInterface(IID_IDccManSink,
      (void **)&pDccManSink);
   if(FAILED(hr)) {
      pDccMan->Release();
      return 0;
   }

   // Hook up the notification
   DWORD dwContext = 0;
   hr = pDccMan->Advise(pDccManSink, &dwContext);

   // Do something while getting notifications
   DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG), NULL,
      (DLGPROC)DlgProc);

   // Clean up
   if(pDccMan) {
      pDccMan->Unadvise(dwContext);
      pDccMan->Release();
   }

   return 0;
}

INT_PTR CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM
  wParam, LPARAM lParam)
{
   switch (uMsg) {
      case WM_COMMAND:
      switch (LOWORD(wParam)) {
         case IDOK:
         case IDCANCEL:
            EndDialog(hwndDlg, wParam);
         return TRUE;
         }
      }
   return FALSE;
}

// DccSink object
CDccSink::CDccSink() {
   m_lRef = 1;
   return;
}

CDccSink::~CDccSink() {
   return;
}

// IDccManSink's IUnknown interface
STDMETHODIMP CDccSink::QueryInterface(REFIID riid,
  LPVOID *ppv) {
   if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid,
        IID_IDccManSink)) {

      *ppv = (IDccManSink *)this;
      AddRef();
      return NO_ERROR;
   }

   *ppv = NULL;
   return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CDccSink::AddRef() {
   return (ULONG)InterlockedIncrement(&m_lRef);
}

STDMETHODIMP_(ULONG) CDccSink::Release() {
   ULONG ulCount = (ULONG)InterlockedDecrement(&m_lRef);
   if(ulCount == 0)
      delete this;

   return ulCount;
}

// IDccManSink implementation
STDMETHODIMP CDccSink::OnLogIpAddr(DWORD dwIpAddr) {
   OutputDebugString(TEXT("Received a new IP Address\r\n"));
   return NO_ERROR;
}

STDMETHODIMP CDccSink::OnLogTerminated() {
   OutputDebugString(TEXT("On Log Terminated\r\n"));
   return NO_ERROR;
}

STDMETHODIMP CDccSink::OnLogActive() {
   OutputDebugString(TEXT("On Log Active\r\n"));
   return NO_ERROR;
}

STDMETHODIMP CDccSink::OnLogInactive() {
   OutputDebugString(TEXT("On Log InActive\r\n"));
   return NO_ERROR;
}

STDMETHODIMP CDccSink::OnLogAnswered() {
   OutputDebugString(TEXT("On Log Answered\r\n"));
   return NO_ERROR;
}

STDMETHODIMP CDccSink::OnLogListen() {
   OutputDebugString(TEXT("On Log Listen\r\n"));
   return NO_ERROR;
}

STDMETHODIMP CDccSink::OnLogDisconnection() {
   OutputDebugString(TEXT("On Log Disconnection\r\n"));
   return NO_ERROR;
}

STDMETHODIMP CDccSink::OnLogError() {
   OutputDebugString(TEXT("On Log Error\r\n"));
   return NO_ERROR;