Almost every Pocket PC device on the market today has an infrared port. This port is compatible with Infrared Data Association (IrDA) standards (for more information, check out for data communications and protocols over a short-range wireless connection.

Three mechanisms are available for developing applications over the infrared (IR) port on a Pocket PC: Raw IR, IrCOMM, and IrSock. Both Raw IR and IrCOMM treat the infrared connection as a virtual serial port, whereas IrSock uses Winsock to perform its communication tasks, such as handshaking and protocol negotiation.

Raw Infrared (Raw IR)

The most basic way to create an infrared connection between two devices is by using Raw IR. Raw IR mode provides you with nothing more than a simple way to get direct access to the infrared connector on your device?there is no mechanism for handling signal interruptions, data collisions, or any other type of error conditions that can occur. Although Raw IR is not considered IrDA-compliant?as it does not automatically handle handshaking and other protocol tasks?it is considered the best method for taking total control over the IR port. The important thing to remember when developing applications that use Raw IR is that you have to be prepared to handle everything, including the management and correction of error conditions.

Before you can use Raw IR, you must first determine what communications port has the IR transceiver attached to it. This can be done by examining the registry key HKEY_LOCAL_MACHINE\Comm\IrDA and looking at the Port sub-key value. This will contain the COM port number to use when opening the infrared connection.


On certain Pocket PC devices (such as the iPAQ), the Port sub-key value will not exist. If this is the case, you can check under the Linkage sub-key for the Bind value. You then use the Bind value to determine the port by looking under the HKEY_LOCAL_MACHINE\Comm key (followed by the Bind value), and examine its Parms sub-key for the Raw IR port.

For example, use the following to query a device for its Raw IR port:

// Find the Raw IR port to use
DWORD dwRawIRPort = 0;
TCHAR tchRawIRPort[10] = TEXT("\0");

// Step 1. Check under HKEY_LOCAL_MACHINE\Comm\IrDA for it
DWORD dwValType = 0, dwSize = sizeof(DWORD);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Comm\\IrDA"), 0,
   0, &hKey) == ERROR_SUCCESS) {
   if(RegQueryValueEx(hKey, TEXT("Port"), NULL, &dwValType,
      (LPBYTE)&dwRawIRPort, &dwSize) == ERROR_SUCCESS) {
         // Ok, we got a port, so set up the buffer so we can call
         // CreateFile()
         wsprintf(tchRawIRPort, TEXT("COM%d:"), dwRawIRPort);

// Step 2. Check under the linkage (if necessary)
if(dwRawIRPort == 0) {
   TCHAR tchBindKey[256] = TEXT("\0");
   HKEY hSubKey = NULL;
   dwSize = 256;
   dwValType = 0;

   // Step 2a. Get the bind
   if(RegOpenKeyEx(hKey, TEXT("Linkage"), 0, 0, &hSubKey) ==
           ERROR_SUCCESS) {
      if(RegQueryValueEx(hSubKey, TEXT("Bind"), NULL,
           &dwValType, (LPBYTE)&tchBindKey, &dwSize) !=
           ERROR_SUCCESS) {
         return FALSE;


   // Step 2b. Now that we have the bind, open it
   TCHAR tchIRBind[256] = TEXT("\0");
   wsprintf(tchIRBind, TEXT("Comm\\%s\\Parms"), tchBindKey);
   if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, tchIRBind, 0, 0, &hKey) ==
           ERROR_SUCCESS) {
      if(RegQueryValueEx(hKey, TEXT("Port"), NULL, &dwValType,
      (LPBYTE)&dwRawIRPort, &dwSize) == ERROR_SUCCESS) {
         // Ok, we got a port, so set up the buffer so we
         // can call CreateFile
         wsprintf(tchRawIRPort, TEXT("COM%d:"), dwRawIRPort);
// Step 3. Close up the registry

if(dwRawIRPort == 0)
   return FALSE;

On some Pocket PC devices, the serial hardware shares a hardware serial port with the infrared port. If this is the case, your application will need to "switch" the mode of the serial port to infrared mode by calling the EscapeCommFunction() described earlier in this chapter:

BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc);

The first parameter is the open device handle to the port for which you want to change to infrared mode. To turn on infrared mode, you can pass the flag SETIR for the dwFunc parameter. To toggle it back to standard serial mode, you can use the flag CLRIR.

Remember a few things when using Raw IR mode: First, you cannot use the serial port at the same time as the Raw IR port if they share the same port number. Second, you have to account for all dropped bytes, and you are responsible for ensuring the data integrity of the information you transfer over the wireless connection. Finally, be aware that your transfers are half-duplex, which means you should never have both devices transmitting in the same direction at the same time; otherwise, data corruption will occur.

To send and receive data over Raw IR, you can use the standard serial communications functions, such as ReadFile() and WriteFile().


The easiest way to use a serial infrared connection is by using IrCOMM. IrCOMM is essentially a "virtual" serial port that automatically handles tasks that Raw IR requires you to handle manually. Because IrCOMM is IrDA-compliant, it takes care of issues such as remote device detection, signal collisions, signal interruptions, and the queuing of data. In fact, the infrared connection created with IrCOMM is actually controlled and managed by IrSock internally.

As with Raw IR, there is no direct way to find the IrCOMM port, so you must manually determine the port by examining the registry. To find the actual port number, you need to look at the key HKEY_LOCAL_MACHINE\Drivers\Builtin\IrCOMM and the value under the Index sub-key.

The following example shows you how to determine the port to use for IrCOMM:

// Find the IrCOMM port to use
DWORD dwIrCOMMPort = 0;
TCHAR tchIrCOMMPort[10] = TEXT("\0");

// Step 1. Check under HKEY_LOCAL_MACHINE\Drivers\Builtin\IrCOMM
// for it
DWORD dwValType = 0, dwSize = sizeof(DWORD);
   TEXT("Drivers\\Builtin\\IrCOMM"), 0, 0,
   &hKey) == ERROR_SUCCESS) {
   if(RegQueryValueEx(hKey, TEXT("Index"), NULL, &dwValType,
      (LPBYTE)&dwIrCOMMPort, &dwSize) == ERROR_SUCCESS) {
      // Ok, we got a port, so set up the buffer so we can
      // call CreateFile()
      wsprintf(tchIrCOMMPort, TEXT("COM%d:"), dwIrCOMMPort);

// Step 2. Close up the registry

if(dwIrCOMMPort == 0)
   return FALSE;

Because IrSock manages the infrared port when in IrCOMM mode, you cannot manually configure any of the serial port parameters on the IrCOMM port. If you attempt to call the GetCommState() function on the port, the DCB structure returned will contain all zeros. In addition, be aware that an IrCOMM connection is a direct device-to-device link, and only two devices can be connected at any time.

Infrared Sockets (IrSock)

On Pocket PC devices, Infrared Socket (IrSock) support is what actually implements the abstraction of the IrDA protocol that Windows CE uses. Communicating between devices using IrSock is nearly identical to using the network Winsock API described in Chapter 1, except for a few minor differences:

  • You can only use IrSock for connection-oriented TCP sockets; it does not support UDP datagram packets.

  • IrSock clients must not call the Winsock function bind().

  • You cannot allocate over 80 IrSock sockets simultaneously; otherwise, memory failures will occur.

  • IrSock sockets do not support the creation of secure sockets.

  • IrSock sockets can browse for available network resources with the range of the device.

  • IrSock sockets do not use the Winsock name service functions. Instead, the name service is part of the communication stream.

  • The WSAENETDOWN error code is not supported.

  • There is no equivalent to INADDR_ANY when using IrSock sockets.

  • The address scheme used for IrSock sockets is different from a Winsock network; they use SOCKADDR_IRDA instead.

To use any of the IrSock infrared communication functions with your application, you need to include the header af_irda.h, and link with the winsock.lib library in your project.

Infrared Socket Options and Addressing

Because networking over infrared is very different from a standard network interface, there are several unique options that apply only to an IrSock socket (and, conversely, most of the standard Winsock options don't apply to IrSock sockets). Just like "normal" Winsock, you can use the getsockopt() and setsockopt() functions to query and set your socket options:

int getsockopt (SOCKET s, int level, int optname, char *optval,
   int *optlen);

int setsockopt (SOCKET s, int level, int optname, const char
   *optval, int optlen);

The parameters that both functions take are identical to those described previously for Winsock, with the exception of the level parameter, which is set to SOL_SOCKET or SOL_IRLMP when used with infrared sockets.

Table 5.18 describes the available socket options for IrSock sockets.

Table 5.18. Infrared Socket Options


Option Name








Enables or disables immediate return from closesocket()



struct LINGER


Enables or disables immediate return from closesocket()





Enumerates remote IrDA devices





Queries IAS attributes





Sets IAS attributes





Sets the IrDA protocol to IrLPT mode





Sets the IrDA protocol to standard 9-wire serial mode





Gets the maximum size of send packets for IrLPT mode





Sets the IrDA protocol to Sharp mode

Addressing an IrSock socket is also slightly different from a standard Winsock socket. Whereas a normal TCP/IP network adapter would use the SOCKADDR_IN structure for its address, an infrared socket uses SOCKADDR_IDRA instead. It is defined as follows:

typedef struct _SOCKADDR_IRDA {
   u_short irdaAddressFamily;
   u_char irdaDeviceID[4];
   char irdaServiceName[25];

The irdaAddressFamily field should be set to AF_IRDA (denoting that it is an infrared socket), and is followed by the device identifier irdaDeviceID. This is the same identifier that is returned when calling the getsockopt() function call with the SO_IRLMP_ENUMDEVICES flag. The last parameter is a 25-character, null-terminated string that contains the service name of the application using the socket.

Establishing IrSock Connections

Because IrSock does not use the conventional Winsock name service functions for finding remote hosts, it provides you with the capability to browse other IrDA devices that are nearby and within range. This is easily done by querying the socket options using the getsockopt() function with the IRLMP_ENUMDEVICES flag. When the function returns, the DEVICELIST structure will contain information about the entire list of devices currently in range. The structure is defined as follows:

typedef struct _DEVICELIST {
   ULONG numDevice;
   IRDA_DEVICE_INFO Device[1];

The numDevice field specifies the number of IRDA_DEVICE_INFO structures in the Device array field. Each IRDA_DEVICE_INFO structure looks like the following:

typedef struct _IRDA_DEVICE_INFO {
   u_char irdaDeviceID[4];
   char irdaDeviceName[22];
   u_char Reserved[2];

The remote device identifier is stored in the irdaDeviceID field, and is followed by the device name. The last field, Reserved, is currently unused.

The following example prints a list of all nearby devices:

// IrSock Device Locater

sIR = socket(AF_IRDA, SOCK_STREAM, 0);
   return FALSE;

// Locate all nearby IrDA devices
DWORD dwMaxDevices = 10;
int nSize = dwMaxDevices * sizeof(DEVICELIST);

pDevices = (DEVICELIST *)LocalAlloc(LPTR, nSize);
if(!pDevices) {
   return FALSE;

// Call getsockopt to find all devices
   (char *)pDevices, &nSize) == SOCKET_ERROR) {
   return FALSE;

// Walk through the array of nearby devices
for(ulong ulDeviceNum = 0; ulDeviceNum < pDevices-> numDevice;
   ulDeviceNum++) {
   TCHAR tchDevice[256] = TEXT("\0");

   wsprintf(tchDevice, TEXT("IrDA ID: %x:%x:%x:%x"),
   MessageBox(NULL, tchDevice, TEXT("IR Device Found"),

// Cleanup