Accelerometer API


WEC7 defines a driver model and an API set for 3-axis accelerometers devices which can
be used for various purposes: orientation change, dead reckoning, image stabilization, etc.

The device driver follows the well known MDD/PDD stream interface model: the MDD code is %_WINCEROOT%\public\COMMON\oak\drivers\accelerometer\mdd while
the PDD code is typically HW/BSP dependent(see for example %_WINCEROOT%\platform\IMX313DS\SRC\DRIVERS\ACCELEROMETER).

MSFT provides fake PDD which you can use to experience with the device and its API set: %_WINCEROOT%\public\COMMON\oak\drivers\accelerometer\pdds\mock:
for example you can use the code to emulate the accelerometer in VirtualPC including the DLL in the OS design.

There’s no rule in the makefiles to build the fake accelerometer DLL so need to build it explicitly from the command line or in VS2008;
maybe you’d like to create a DLL subproject in your OS design and include the code (Notice that if you’re building a debug version of
the DLL you need to uncomment the declaration of the dpCurSettings structure to avoid compilation errors).

To include the driver and have it loaded you need to add the relevant entries in .bib and .reg files:

MODULES
fakeaccpdd.dll      $(_FLATRELEASEDIR)\fakeaccpdd.dll     NK  SHK

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\ACC]
“Prefix”=”ACC”
“Dll”=”fakeaccpdd.dll”
“Index”=dword:1
“Order”=dword:12
; PMCLASS_GENERIC_DEVICE, Power-manageable sensor, SENSOR_TYPE_ACCELEROMETER_3D
“IClass”=multi_sz:”{A32942B7-920C-486b-B0E6-92A702A99B35}”,
“{3267CC03-F391-4fb1-BADD-36FBB8815E86}”,
“{C2FB0F5F-E2D2-4C78-BCD0-352A9582819D}”

Accelerometer API are basically a wrapper of the accelerometer device driver IOCTL’s; in addition some functions
create a thread used to communicate between the driver and your application. This API is exported by coredll provided that
you include the ‘Accelerometer Driver’ catalog item.

The following sample code demonstrate the use of the accelerometer API:

// NOTE: no error checking, do not use this code in production environment
//
#include <windows.h>
#include <accapi.h>
#include <winioctl.h> //for CTL_CODE 
#include <acc_ioctl.h> //for FILE_DEVICE_ACCELEROMETER (which is not #define'd in winioctl.h)
//From %_WINCEROOT%\public\COMMON\oak\drivers\accelerometer\pdds\mock\accelerometer.h
#define IOCTL_ACC_SEND_SAMPLE    \
    CTL_CODE(FILE_DEVICE_ACCELEROMETER, 0x4000, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_ACC_SIMULATE_ROTATE \
        CTL_CODE(FILE_DEVICE_ACCELEROMETER, 0x4001, METHOD_BUFFERED, FILE_ANY_ACCESS) 
#define ACC_NAME _T("ACC1:")
#define MAX_QUEUE_MESSAGES 10 //arbitrary value
#define RESERVED_PARM NULL
//Check the following files for details about the various struct's and enum's
//%_WINCEROOT%\public\common\oak\inc\sensor.h
//%_WINCEROOT%\public\common\oak\inc\accapi.h
//%_WINCEROOT%\public\COMMON\oak\inc\acc_ioctl.h
DWORD AccCallback(ACC_DATA* pAccData, __in_opt LPVOID plvCallbackParam);
DWORD AccMsgCallback(SENSOR_MESSAGE_TYPE msgType, LPVOID pAccData, DWORD cbSizeAccData, __in_opt LPVOID plvCallbackParam);
int _tmain(int argc, TCHAR *argv[], TCHAR *envp[])
{
    LUID AccLuid;
    HSENSOR hSensor;
    MSGQUEUEOPTIONS MsgQueueopt;
 HANDLE hMsgQueue;
    ACC_DATA AccData;
    DWORD dwBytes, dwFlags, i;
    //Retrieve a handle which will be used in the other API calls
    hSensor = AccelerometerOpen(ACC_NAME, &AccLuid);
 
 /*
 Set accelerometer mode.
 Possible modes, according to the docs, are:
 ACC_CONFIG_STREAMING_DATA_MODE
 Default. Streaming data mode. The accelerometer data sample is acquired from accelerometer hardware and is constantly sent back to upper subscribers at a fixed frequency.
 ACC_CONFIG_ORIENTATION_MODE
 Orientation sensing mode. The accelerometer driver only signals the subscriber when an orientation change is detected.
 ACC_CONFIG_COMPASS_MODE
 Compass simulation mode. The accelerometer driver signals the subscriber when a small angle change is detected. This mode is designed for digital compass applications.
 ACC_CONFIG_CALIBRATION_MODE
 Device calibration mode. This mode is used for calibrating the accelerometer hardware.
 */
    AccelerometerSetMode(hSensor, ACC_CONFIG_STREAMING_DATA_MODE, RESERVED_PARM);
 /*
 This IOCTL is defined only for the fake PDD: calling it will result in a serie of sample which
 emulate a rotation on the z axis. If you do not call this IOCTL the driver will return always
    x = 0.7, y = 0.7, z = 0 (notice the '7'...). There's another custom IOCTL
 IOCTL_ACC_SEND_SAMPLE which allows you to inject specific values to the driver which will returned
 back as actual samples
 */
 DeviceIoControl(hSensor, IOCTL_ACC_SIMULATE_ROTATE, NULL, 0, NULL, 0, &dwBytes, NULL);
 //Method #1: 'manually' pass a message queue handle to the accelerometer driver and read
 //the samples from the queue
    MsgQueueopt.dwSize = sizeof(MSGQUEUEOPTIONS);
    MsgQueueopt.dwFlags = MSGQUEUE_ALLOW_BROKEN ;
    MsgQueueopt.dwMaxMessages = MAX_QUEUE_MESSAGES; 
    MsgQueueopt.cbMaxMessage = sizeof(ACC_DATA);
    MsgQueueopt.bReadAccess = TRUE;
    
    hMsgQueue = CreateMsgQueue(NULL, &MsgQueueopt);
 AccelerometerStart(hSensor, hMsgQueue);
    
    for(i=0; i<MAX_QUEUE_MESSAGES; i++)
    {
      WaitForSingleObject(hMsgQueue, INFINITE);
      
      ReadMsgQueue(hMsgQueue, &AccData, sizeof(ACC_DATA), &dwBytes, 0, &dwFlags);
   RETAILMSG(1, (_T("Accelerometer data received\r\n")));
    }
    AccelerometerStop(hSensor);
    
    CloseMsgQueue(hMsgQueue);
 //Method #2: Pass to the accelerometer driver a callback which will be called when new samples
 //are available
 AccelerometerCreateCallback(hSensor, AccCallback, NULL);
 /*Wait a bit so you can see the callback call*/
 Sleep(50);
 //According the docs, AccelerometerCreateCallback calls AccelerometerStart
 //Although it's not stated in the docs, AccelerometerCancelCallback simmetrically
 //calls AccelerometerStop
 //Note: in AccelerometerStop -called by AccelerometerCancelCallback- the message queue used
 //by the driver to sent samples and messages to the subscribers is closed: sometimes you may see 
 //'ERROR: DecodeAPI - invalid handle' due to a reader calling ReadMsgQueue on the queue which
 //has already been closed
 AccelerometerCancelCallback(hSensor);
 //Method #3:  Pass to the accelerometer driver a callback which will be called when new data
 //are available: data can be samples, orientation change, etc.
 AccelerometerCreateMsgCallback(hSensor, AccMsgCallback, NULL);
 /*Wait a bit so you can see the callback call*/
 Sleep(50);
 AccelerometerCancelCallback(hSensor);
    CloseHandle((HANDLE)hSensor);
    return 0;
}
DWORD AccCallback(ACC_DATA* pAccData, __in_opt LPVOID plvCallbackParam)
{
 RETAILMSG(1, (_T("AccCallback Callback\r\n")));
 return 0;
}
DWORD AccMsgCallback(SENSOR_MESSAGE_TYPE msgType, LPVOID pAccData, DWORD cbSizeAccData, __in_opt LPVOID plvCallbackParam)
{
 RETAILMSG(1, (_T("AccMsgCallback Callback\r\n")));
 return 0;
}

The output of a debug build will be something like:

10541593 PID:400002 TID:1bbcb2a +ACC_Open
10541599 PID:400002 TID:1bbcb2a +AccDeviceContext::AddSubscriber
10541599 PID:400002 TID:1bbcb2a -AccDeviceContext::AddSubscriber
10541599 PID:400002 TID:1bbcb2a -ACC_Open
10541600 PID:400002 TID:1bbcb2a +ACC_IOControl
10541600 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541600 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541600 PID:400002 TID:1bbcb2a -ACC_IOControl
10541600 PID:400002 TID:1bbcb2a +ACC_IOControl
10541601 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541601 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541601 PID:400002 TID:1bbcb2a -ACC_IOControl
10541601 PID:400002 TID:1bbcb2a +ACC_IOControl
10541601 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541601 PID:400002 TID:1bbcb2a +AccDeviceContext::Ioctl
10541601 PID:400002 TID:1bbcb2a fakeaccpdd PddIoctl: got IOCTL_ACC_SIM_ROTATE
10541601 PID:400002 TID:1bbcb2a -AccDeviceContext::Ioctl
10541601 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541602 PID:400002 TID:1bbcb2a -ACC_IOControl
10541602 PID:400002 TID:1bbcb2a +ACC_IOControl
10541603 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541604 PID:400002 TID:1bbcb2a +AccSubscriber::StartSensor
10541604 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatedSubscriber
10541604 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatePddMode
10541604 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatePddInterval
10541607 PID:400002 TID:141003e +AccMddProcessSample
10541607 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541608 PID:400002 TID:141003e AccSample: x=0.500000, y=0.500000, z=0.000000
10541608 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatedSubscriber
10541608 PID:400002 TID:1bbcb2a -AccSubscriber::StartSensor
10541608 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541608 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541609 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541609 PID:400002 TID:141003e -AccMddProcessSample
10541609 PID:400002 TID:141003e ACC Polling thread sleeping 11ms
10541609 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541609 PID:400002 TID:1bbcb2a -ACC_IOControl
10541609 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541630 PID:400002 TID:141003e +AccMddProcessSample
10541630 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541630 PID:400002 TID:141003e AccSample: x=1.000000, y=0.000000, z=0.000000
10541630 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541631 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541631 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541631 PID:400002 TID:141003e -AccMddProcessSample
10541632 PID:400002 TID:141003e ACC Polling thread sleeping 8ms
10541632 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541653 PID:400002 TID:141003e +AccMddProcessSample
10541653 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541654 PID:400002 TID:141003e AccSample: x=0.500000, y=-0.500000, z=0.000000
10541654 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541655 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541655 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541655 PID:400002 TID:141003e -AccMddProcessSample
10541655 PID:400002 TID:141003e ACC Polling thread sleeping 5ms
10541655 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541676 PID:400002 TID:141003e +AccMddProcessSample
10541676 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541676 PID:400002 TID:141003e AccSample: x=0.000000, y=-1.000000, z=0.000000
10541677 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541677 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541678 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541678 PID:400002 TID:141003e -AccMddProcessSample
10541678 PID:400002 TID:141003e ACC Polling thread sleeping 2ms
10541679 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541701 PID:400002 TID:141003e +AccMddProcessSample
10541701 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541702 PID:400002 TID:141003e AccSample: x=-0.500000, y=-0.500000, z=0.000000
10541702 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541702 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541702 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541703 PID:400002 TID:141003e -AccMddProcessSample
10541703 PID:400002 TID:141003e ACC Polling thread sleeping 17ms
10541704 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541725 PID:400002 TID:141003e +AccMddProcessSample
10541725 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541725 PID:400002 TID:141003e AccSample: x=-1.000000, y=0.000000, z=0.000000
10541728 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541728 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541728 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541728 PID:400002 TID:141003e -AccMddProcessSample
10541728 PID:400002 TID:141003e ACC Polling thread sleeping 12ms
10541729 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541750 PID:400002 TID:141003e +AccMddProcessSample
10541750 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541751 PID:400002 TID:141003e AccSample: x=-0.500000, y=0.500000, z=0.000000
10541751 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541752 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541752 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541753 PID:400002 TID:141003e -AccMddProcessSample
10541753 PID:400002 TID:141003e ACC Polling thread sleeping 7ms
10541755 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541776 PID:400002 TID:141003e +AccMddProcessSample
10541776 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541776 PID:400002 TID:141003e AccSample: x=0.000000, y=1.000000, z=0.000000
10541776 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541777 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541777 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541777 PID:400002 TID:141003e -AccMddProcessSample
10541778 PID:400002 TID:141003e ACC Polling thread sleeping 2ms
10541778 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541802 PID:400002 TID:141003e +AccMddProcessSample
10541802 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541803 PID:400002 TID:141003e AccSample: x=0.000000, y=0.000000, z=0.000000
10541803 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541804 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541804 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541804 PID:400002 TID:141003e -AccMddProcessSample
10541804 PID:400002 TID:141003e ACC Polling thread sleeping 16ms
10541805 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541826 PID:400002 TID:141003e +AccMddProcessSample
10541826 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541826 PID:400002 TID:141003e AccSample: x=0.000000, y=1.000000, z=0.000000
10541826 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541827 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541827 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541828 PID:400002 TID:141003e -AccMddProcessSample
10541828 PID:400002 TID:141003e ACC Polling thread sleeping 12ms
10541829 PID:1bac872 TID:1bbcb2a Accelerometer data received
10541829 PID:400002 TID:1bbcb2a +ACC_IOControl
10541830 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541830 PID:400002 TID:1bbcb2a +AccSubscriber::StopSensor
10541831 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatedSubscriber
10541831 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatePddMode
10541831 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatePddInterval
10541832 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatedSubscriber
10541832 PID:400002 TID:1bbcb2a -AccSubscriber::StopSensor
10541833 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541833 PID:400002 TID:1bbcb2a -ACC_IOControl
10541834 PID:400002 TID:1bbcb2a +ACC_IOControl
10541834 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541835 PID:400002 TID:1bbcb2a +AccSubscriber::StartSensor
10541835 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatedSubscriber
10541835 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatePddMode
10541836 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatePddInterval
10541836 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatedSubscriber
10541837 PID:400002 TID:1bbcb2a -AccSubscriber::StartSensor
10541837 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541837 PID:400002 TID:1bbcb2a -ACC_IOControl
10541855 PID:400002 TID:141003e +AccMddProcessSample
10541855 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541855 PID:400002 TID:141003e AccSample: x=0.500000, y=0.500000, z=0.000000
10541856 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541856 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541856 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541857 PID:400002 TID:141003e -AccMddProcessSample
10541857 PID:400002 TID:141003e ACC Polling thread sleeping 3ms
10541857 PID:1bac872 TID:10cd82a AccCallback Callback
10541878 PID:400002 TID:141003e +AccMddProcessSample
10541878 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541879 PID:400002 TID:141003e AccSample: x=1.000000, y=0.000000, z=0.000000
10541879 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541879 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541879 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541879 PID:400002 TID:141003e -AccMddProcessSample
10541879 PID:400002 TID:141003e ACC Polling thread sleeping 1ms
10541880 PID:1bac872 TID:10cd82a AccCallback Callback
10541888 PID:400002 TID:1bbcb2a +ACC_IOControl
10541889 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541891 PID:400002 TID:1bbcb2a +AccSubscriber::StopSensor
10541891 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatedSubscriber
10541892 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatePddMode
10541892 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatePddInterval
10541893 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatedSubscriber
10541893 PID:400002 TID:1bbcb2a -AccSubscriber::StopSensor
10541893 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541894 PID:400002 TID:1bbcb2a -ACC_IOControl
10541897 PID:400002 TID:1bbcb2a +ACC_IOControl
10541897 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541897 PID:400002 TID:1bbcb2a +AccSubscriber::StartSensor
10541897 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatedSubscriber
10541897 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatePddMode
10541897 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatePddInterval
10541898 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatedSubscriber
10541898 PID:400002 TID:1bbcb2a -AccSubscriber::StartSensor
10541898 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541898 PID:400002 TID:1bbcb2a -ACC_IOControl
10541905 PID:400002 TID:141003e +AccMddProcessSample
10541905 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541906 PID:400002 TID:141003e AccSample: x=0.500000, y=-0.500000, z=0.000000
10541906 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541907 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541907 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541907 PID:400002 TID:141003e -AccMddProcessSample
10541907 PID:400002 TID:141003e ACC Polling thread sleeping 13ms
10541908 PID:1bac872 TID:108d59e AccMsgCallback Callback
10541929 PID:400002 TID:141003e +AccMddProcessSample
10541929 PID:400002 TID:141003e +AccDeviceContext::ProcessSample
10541929 PID:400002 TID:141003e AccSample: x=0.000000, y=-1.000000, z=0.000000
10541930 PID:400002 TID:141003e +AccSubscriber::NotifySample
10541930 PID:400002 TID:141003e -AccSubscriber::NotifySample
10541930 PID:400002 TID:141003e -AccDeviceContext::ProcessSample
10541930 PID:400002 TID:141003e -AccMddProcessSample
10541930 PID:400002 TID:141003e ACC Polling thread sleeping 10ms
10541930 PID:1bac872 TID:10cd82a AccMsgCallback Callback
10541957 PID:400002 TID:1bbcb2a +ACC_IOControl
10541957 PID:400002 TID:1bbcb2a +AccSubscriber::Ioctl
10541957 PID:400002 TID:1bbcb2a +AccSubscriber::StopSensor
10541957 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatedSubscriber
10541958 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatePddMode
10541958 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatePddInterval
10541959 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatedSubscriber
10541959 PID:400002 TID:1bbcb2a -AccSubscriber::StopSensor
10541960 PID:400002 TID:1bbcb2a -AccSubscriber::Ioctl
10541960 PID:400002 TID:1bbcb2a -ACC_IOControl
10541961 PID:400002 TID:1bbcb2a +ACC_Close
10541961 PID:400002 TID:1bbcb2a +AccDeviceContext::RemoveSubscriber
10541962 PID:400002 TID:1bbcb2a -AccDeviceContext::RemoveSubscriber
10541962 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatedSubscriber
10541962 PID:400002 TID:1bbcb2a +AccDeviceContext::UpdatePddMode
10541962 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatePddInterval
10541963 PID:400002 TID:1bbcb2a -AccDeviceContext::UpdatedSubscriber
10541963 PID:400002 TID:1bbcb2a -ACC_Close
Advertisements
Posted in Windows Embedded Compact | Tagged , | Leave a comment

Implementing a secure socket


A recent post about SSL and Windows sockets in Windows Embedded Compact Platform Development forum reminded me that when I needed to use SSL in a Windows CE application it was very difficult to find information and sample code. On MSDN you can find basically only a page which summarize what you have to do. In this post you can see working code for estabilish a secure connection validating a certificate provided by the server. The sample assumes the certificate is installed on the device by the user using control panel rather than by the application itself (see the enrollment sample in %_WINCEROOT%\PUBLIC\COMMON\SDK\SAMPLES\ENROLL). The sample assumes several things and can undoubtly be improved: for example it does not use SO_SSL_GET_PROTOCOLS control code to determine the default protocols. 

Basically you have two functions: one ‘initializes’ SSL,  the other is the certificate validation callback which determines if the connection will be completed. Once you init the SSL related stuff and you call the connect function the callback will be invoked. If the function returns SSL_ERR_OKAY you can use send/recv functions which will automatically encode and decode data. The sample callback will return SSL_ERR_OKAY according to the options you specify in the SSL_Init:

  • do not check the server certificate at all and always return SSL_ERR_OKAY
  • succeds if in the certificate store a certificate from the same issuer as the server one can be found
  • succeds if in the certificate store the exact certificate from the server can be found
 
#include <windows.h>
#include <winsock2.h>
#include <sslsock.h>
#include <schnlsp.h>

#define SSL_OPT_NO_CHECK  0
#define SSL_OPT_ISSUER_CHECK 1
#define SSL_OPT_STRICT_CHECK 2

#define SSL_OPT_DEFAULT   SSL_OPT_STRICT_CHECK

#define DBG_ON 0

static int g_SslOptions = SSL_OPT_DEFAULT;

int
CALLBACK
SSLValidateCertHook(
   DWORD  dwType,          // in
   LPVOID pvArg,           // in
   DWORD  dwChainLen,      // in
   LPBLOB pCertChain,      // in
   DWORD dwFlags)         // in
{
 HCERTSTORE hCertStore = NULL;
 PCCERT_CONTEXT pCertInStore = NULL, pCertPassed = NULL, pPrevCertContext = NULL;
 const LPCTSTR SystemStore[] = {_T("CA"), _T("ROOT"), _T("MY")};
 unsigned int i;

 RETAILMSG(DBG_ON,(_T("+SSLValidateCertHook")));

 //The only thing we can handle
 if(dwType != SSL_CERT_X509)
  return SSL_ERR_CERT_UNKNOWN;

 //Do not check the certificate at all
 if(g_SslOptions == SSL_OPT_NO_CHECK)
  return SSL_ERR_OKAY;

 //Get a context struct from the certificate blob
 pCertPassed = CertCreateCertificateContext(X509_ASN_ENCODING,pCertChain->pBlobData,pCertChain->cbSize);

 if(!pCertPassed)
  return SSL_ERR_FAILED;

 //Check for a matching certificate in the stores
 for (i=0; i<(sizeof(SystemStore)/sizeof(SystemStore[0])); i++)
 {
  //open the store
  hCertStore = CertOpenSystemStore((HCRYPTPROV)NULL,SystemStore[i]);

  if(!hCertStore)
   continue;

  RETAILMSG(DBG_ON,(_T("Opened %s"), SystemStore[i]));

  pPrevCertContext = NULL;

  do
  {
   //Find a certificate from the same issuer
   pCertInStore = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING,
    0, CERT_FIND_ISSUER_NAME,&pCertPassed->pCertInfo->Issuer,pPrevCertContext);

   if(pCertInStore)
   {
    RETAILMSG(DBG_ON,(_T("Find certificate with the correct issuer")));

    //It's enough if we have a certificate from te same issuer
    if(g_SslOptions == SSL_OPT_ISSUER_CHECK)
    {
     CertFreeCertificateContext(pCertInStore);
     CertCloseStore(hCertStore,0);
     CertFreeCertificateContext(pCertPassed);
     return SSL_ERR_OKAY;
    }
    else if(g_SslOptions == SSL_OPT_STRICT_CHECK)
    {
     //Compare (exact match) the certificate with the one we have found
     if(CertCompareCertificate(X509_ASN_ENCODING,pCertInStore->pCertInfo,pCertPassed->pCertInfo))
     {
      RETAILMSG(DBG_ON,(_T("Find a certificate *exact match*")));
      CertFreeCertificateContext(pCertInStore);
      CertCloseStore(hCertStore,0);
      CertFreeCertificateContext(pCertPassed);
      return SSL_ERR_OKAY;
     }
    }
   }

   pPrevCertContext = pCertInStore;
  }
  while(pCertInStore);

  CertCloseStore(hCertStore,0);
 }

 if(pCertInStore)
  CertFreeCertificateContext(pCertInStore);

 if(hCertStore)
  CertCloseStore(hCertStore,0);

 if(pCertPassed)
  CertFreeCertificateContext(pCertPassed);

 RETAILMSG(DBG_ON,(_T("-SSLValidateCertHook")));

 return SSL_ERR_NO_CERT;
}

int
SSLInit(
 SOCKET s,
 int SslOptions
)
{
 DWORD optval = SO_SEC_SSL;
    SSLVALIDATECERTHOOK hook;
    SSLPROTOCOLS protocolsToUse;
    int ret;

 RETAILMSG(DBG_ON,(_T("+SSLInit (opt:%i)"),SslOptions));

 g_SslOptions = SslOptions;//WSAIoctl cannot handle the pointer copy, use a global variable

 if( setsockopt(s, SOL_SOCKET, SO_SECURE, (LPSTR)&optval,sizeof(optval)) ==
  SOCKET_ERROR)
 {
  RETAILMSG(DBG_ON,(_T("setsockopt SO_SECURE failed")));
  goto SSLInit_Error;
 }

 hook.HookFunc = SSLValidateCertHook;
    hook.pvArg = NULL; //WSAIoctl cannot handle the pointer copy, use a global variable

 if(WSAIoctl(s, SO_SSL_SET_VALIDATE_CERT_HOOK,
        &hook, sizeof(SSLVALIDATECERTHOOK),
        NULL, 0, NULL, NULL, NULL) == SOCKET_ERROR)
 {
  RETAILMSG(DBG_ON,(_T("SO_SSL_SET_VALIDATE_CERT_HOOK failed")));
  goto SSLInit_Error;
 }

 //specify TLS1 protocol

 protocolsToUse.dwCount = 1;
 protocolsToUse.ProtocolList[0].dwFlags = 0;
 protocolsToUse.ProtocolList[0].dwProtocol = SSL_PROTOCOL_TLS1;
 protocolsToUse.ProtocolList[0].dwVersion = 0;

 if( WSAIoctl( s, SO_SSL_SET_PROTOCOLS,
  (LPVOID)&protocolsToUse, sizeof(protocolsToUse),
  NULL, 0, NULL, NULL, NULL ) == SOCKET_ERROR)
 {
  RETAILMSG(DBG_ON,(_T("SO_SSL_SET_PROTOCOLS failed")));
  goto SSLInit_Error; 
 }

 return 0;

SSLInit_Error:

 ret = WSAGetLastError();

 RETAILMSG(DBG_ON,(_T("-SSLInit error %i"),ret));

    return ret;
}
Posted in Windows Embedded CE, Windows Embedded Compact | Tagged , , | Leave a comment

How to Edit Windows CE Binary Resource Files


Sometimes it is necessary to edit binary resources files (.res), for example to resize a dialog box, to disable a control, etc. Almost every MS IDE can open .res file and show all the resources in a graphic layout editor allowing the programmer to immediately check the result of the changes (there’s a good post about this topic on Bruce Eitman’s blog). Unfortunatly, the IDE resource formatting is slightly different from the one used in Windows CE resource script file (.rc) files and this can lead to subtle and annoying problems when you link the modified .res file to executable module: for example you click on a button and the dialog is not opened…
One possible solution is the following:

  • Open the .res file with the IDE
  • Modify it
  • Save the modified file as a resource script file (.rc)
  • Open the saved .rc file with a text editor
  • Remove the references to MFC and language, i.e.
    • At the beginning of the file:

 

#define APSTUDIO_READONLY_SYMBOLS///////////////////////////////////////////////////////////////////////////////// Generated from the TEXTINCLUDE 2 resource.//#include "afxres.h"/////////////////////////////////////////////////////////////////////////////#undef APSTUDIO_READONLY_SYMBOLS/////////////////////////////////////////////////////////////////////////////// English (U.S.) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US#pragma code_page(1252)#endif //_WIN32
  •  
    •  At the end of the file:

 

#endif // English (U.S.) resources//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// English (U.K.) resources#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK#pragma code_page(1252)#endif //_WIN32#ifdef APSTUDIO_INVOKED///////////////////////////////////////////////////////////////////////////////// TEXTINCLUDE//1 TEXTINCLUDEBEGIN"resource."END2 TEXTINCLUDEBEGIN"#include ""afxres.h""\r"END3 TEXTINCLUDEBEGIN"\r"END#endif // APSTUDIO_INVOKED#endif // English (U.K.) resources/////////////////////////////////////////////////////////////////////////////#ifndef APSTUDIO_INVOKED///////////////////////////////////////////////////////////////////////////////// Generated from the TEXTINCLUDE 3 resource.///////////////////////////////////////////////////////////////////////////////#endif // not APSTUDIO_INVOKED
  •  If necessary add the appropriate #include directives for resource definition header files (windows.h, commctrl.h, …) to the beginning of the .rc file
  • Compile it running the RC program from the command prompt opened by Plaform Builder. Remember the /r switch (emit .res file)
  • You would probably use even the /i switch (additional include path, typically %_WINCEROOT%\PUBLIC\COMMON\SDK\INC)
Posted in Windows Embedded CE | Tagged | 2 Comments

Thunks, filling the gap


Those of you who have ever debugged an application may have been frustrated by the lack of some source code; even if you installed the shared source (aka PRIVATE) code, when your application performed a system call  almost inevitably splashed into assembly code -which is not too bad after all, de gustibus non disputandum est. This happened because the coredll code didn’t include the thunks files: with WEC7 you finally have those files (%_WINCEROOT%\private\winceos\COREOS\core\thunks) so debugging is a bit more pleasant at least… But what are those thunks? When you call a system API (most of them are exported by coredll) you’re actually calling a thin wrapper while the actual implementation resides in a server process (the device manager for example). The thunks are those wrapper: for example, if you call CreateFile the code in coredll will be:

extern "C"
HANDLE
WINAPI
xxx_CreateFileW(
 LPCWSTR                 lpFileName,
 DWORD                   dwDesiredAccess,
 DWORD                   dwShareMode,
 LPSECURITY_ATTRIBUTES   lpsa,
 DWORD                   dwCreationDisposition,
 DWORD                   dwFlagsAndAttributes,
 HANDLE                  hTemplateFile
 )
{
 HANDLE h = CreateFileW_Trap(lpFileName,dwDesiredAccess,dwShareMode,lpsa,dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile);
return h;
}

 

CreateFileW_Trap is, as the name suggests, a trap: it’s defined as a macro (more than one actually, take a look to %_WINCEROOT%\public\COMMON\oak\inc\mwinbase.h) which at the end results in an invalid address (like 0xFFFF55EE): when you jump to the address an exception is triggered and the kernel handler will take care of it . The invalid address has a specific format so that the kernel not only will distinguish it from an ‘actual’ invalid address but it will decode it to detect which function you’re calling and which server process to forward the request to. This design ‘naturally’ allows the transition from user to kernel mode.

All I said is related to user code: the kernel calls the systems API directly instead.

Although it’s a bit dated I suggest reading a post by Sue Loh (who can tell if she’s still in MSFT?) on the ce_base blog: this blog was one of my favourite ones but, unfortunately,  it’s been a lot of time since someone wrote something on it…

Posted in Windows Embedded Compact | Tagged , | Leave a comment

Control Panel Applets


A control Panel application is a DLL that is named with a .cpl file extension and that exports the function CPlApplet to handle CPL_INIT, CPL_GETCOUNT, CPL_NEWINQURE, CPL_STOP, and CPL_EXIT messages (the definitions are in %_WINCEROOT%\PUBLIC\COMMON\OAK\INC\cpl.h)

The main control panel application is control.exe (%_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\CONTROL): it creates the control panel window, enumerates the .cpl files in the \\Windows directory and populates the list view with the applets included in the .cpl files.

When you double click on an icon, control.exe calls ShellExecuteEx to spawn an instance of ctlpnl.exe (%_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\CTLPNL)
passing on the command line the .cpl file name and the applet index (a .cpl file can contain more than an applet).

The online docs (http://msdn.microsoft.com/en-us/library/ee502286.aspx) describe how to create a control panel application in a very straighforward way but it’s worth taking a look to how MS programmers did their work. The main control panel application is cplmain.cpl (%_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\CPLMAIN)
which includes great part of the standard control panel applets (communication, keyboard, system etc). It’s built to be easily extended adding new applets and can easily adapted (cloning it) to create your ‘own’ main control panel.

The main file –cplmain.cpp– handles all the .cpl specific stuff like implementing the CPlApplet function, messages processing, running the applets etc.
This code can handle n applets that are expected to be a Property Sheet with a certain number of tabs, from one to  5 (this value is a #define so can be changed).

Every applet is described by the following structure (%_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\CPLMAIN\cplglobl.h):

typedef struct CPLAPPLETINFO
{
    LPCTSTR pszMutex;     
    LPCTSTR pszLaunchCplCallback;  
    BOOL fPwdProtect;   
    int  rcidIcon;    
    int  idsName;    
    int  idsDesc;    
    int  idsTitle;    
    int     cctrlFlags;    
    const CPLTABINFO* rgptab[MAX_TABS];
}
CPLAPPLETINFO, *PCPLAPPLETINFO;
  •  
    • pszMutex: the name of a mutex used to avoid more than one instance of the same applet
    • pszLaunchCplCallback: if this is not NULL cplmain will call this function which returns a BOOL value indicating if the applet has to be run or not. It’s useful to perform runtime operations like adding or removing a tab or change the layout according to the screen orientation
    • fPwdProtect: if this flag is set the user will be requested to authenticate before cplmain displays the applet
    • rcidIcon: the resource ID for the icon which is displayed for the applet in the control panel list view
    • idsName: the resource ID for the string displayed with the applet icon in the list view
    • idsDesc: the resource ID for the string describing the applet (you can see it when you choose the ‘details’ view)
    • idsTitle: the resource ID for the string displayed as the title of the applet
    • cctrlFlags: if the applet uses some specific common controls you can specify them there using the same flags of INITCOMMONCONTROLSEX structure: cplmain will call InitCommonControlsEx on behalf of the applet
    • rgptab: an array of structures describing each tab of the applet (see below)

Every tab is defined by the following structure:

typedef struct
{
    int  idsTitle;
    int  iddDlg;
    LPCTSTR pszDlg;
    const int* rgBoldStatics;
    int  iNumStatics;
    BOOL fSip;
    LPCTSTR pszHelp;
}
CPLTABINFO, *PCPLTABINFO;
  •  
    • idsTitle: the resource ID for the string displayed as the title of the tab
    • iddDlg: the resource ID for the dialog template used for the tab
    • pszDlg: a string with the name of the dialog procedure for the tab. It’s a string rather than a DLGPROC so, at runtime, the control panel can detect if a tab is present or not (useful for componentization as it’s done in the actual cplmain)
    • rgBoldStatics: an array of resource ID’s of controls that you want to apper with a bold font
    • iNumStatics: the length of the above array
    • fSip: this flag indicates if the tab wants the SIP to appear or not
    • pszHelp: a string which ‘points’ to the help documentation (for example L”file:applet.htm#thistab_help”)

The cplmain.cpp file expects an array of CPLAPPLETINFO named rgApplets and providentially the array is not defined in cplmain.cpp but in an included file (cpltable.cpp).

Then: if you want a powerful framework for your control applets you can create a DLL (remember the CPL=1 directive in the sources file to set the correct extension) which includes cplmain.cpp and another bunch of files from %_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\CPLMAIN (like cplglobl.h for example).
Once you fill your own cpltable.cpp file with the relevant structures and create the resources files ‘all’ you need is to implement the applet logic as you would do for a dialog box. 

As a footnote: remember that your .cpl file must export:

  •  
    • CPlApplet
    • The dialog procedure for each tab of the applets
    • the pszLaunchCplCallback function for the applets that use it

Obviously looking at the actual implementation of cplmain to retrieve further details (the registry entries used here and there, for example) is extremely useful if not necessary!

Posted in Windows Embedded CE, Windows Embedded Compact | Tagged , , , | Leave a comment

Filter Drivers – part2


In a previous post I introduced the stream interface filter driver. How do you implement it?
The driver you want to filter must have a registry entry which tells the Device Manager which filter to use.
Let’s say you want to filter the serial driver, you’ll have a registry which looks like (notice the GUID):

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial2]
 "Filter"= "{CC5195AA-BA49-48a0-BE17-DF6D1B0173FF}"

[HKEY_LOCAL_MACHINE\Drivers\Filters\{CC5195AA-BA49-48a0-BE17-DF6D1B0173FF}\MyFilter]
 "Dll"="MyFilter.dll"
 "InitEntry"="MyFilterInit"
 "Order"=dword:0

The filter is a DLL that must export a ‘init’ function with the same name as declared in the registry. The function prototype is

typedef PDRIVER_FILTER  (*pInitEntry) (LPCTSTR lpcFilterRegistryPath,
LPCTSTR pDeviceRegistryPath, PDRIVER_FILTER pNextFilter );

 

  • lpcFilterRegistryPath is the registry key of the filter
  • lpDeviceRegistryPath is the registry key of the filtered driver
  • pNextFilter is a pointer to a structure which contains the filtered driver entry points

The function returns a a pointer to a structure (DRIVER_FILTER) which contains the filter driver entry points: once you have setup the filter the device manager will call the filter driver entry points instead of the driver ones.

You have to fill the returned structure appropriately so that every entry point of the filtered driver has a correspondent not NULL entry in the filter, otherwise the device manager won’t load neither the filter nor the driver.
Those entry points have a prototype which is similar to the stream interface ones (XXX_Open, etc.) with an additional parameter which is a pointer to the
DRIVER_FILTER returned by the filter driver in its init function.

You can have more than one filter driver in the same stack, loaded by the device manager according to the “Order” registry value (similarly to
‘normal’ drivers); if you add, as an example:

[HKEY_LOCAL_MACHINE\Drivers\Filters\{CC5195AA-BA49-48a0-BE17-DF6D1B0173FF}\MyFilter2]
 "Dll"="MyFilter2.dll"
 "InitEntry"="MyFilterInit"
 "Order"=dword:1

the stack will look like: Device Manager -> MyFilter2.dll -> MyFilter.dll -> com16550.dll
The pNextFilter argument to the init function of MyFilter2 will represent the entry points of the lower filter (MyFilter) rather than those belonging to com16550.dll.

Posted in Windows Embedded Compact | Tagged , , , , , | Leave a comment

Touch Screen Stream Interface


Drivers which deal with user I/O like display, keyboard, mouse and touch are someway different respect to the others: they’re loaded by GWES, first of all, and they have a specific interface rather than following the common stream interface. This has at least a drawback: the developer has to familiarize with different programming practices. In WEC 7 MSFT introduces a MDD/PDD stream interface model for the touch screen drivers which can furnish aid to the programmers. Does this mean that old-fashioned touch screen drivers won’t work in WEC7? No they still work  because GWES still relies on the old touch screen driver model. The magic is provided by tchproxy.dll: as the name suggests it acts as a proxy between GWES and the stream interface touch screen driver since it exposes itself to GWES as a classical touch screen driver while internally calling the stream interface touch screen driver. This driver must -rougly speaking- handle a number of IOCTL‘s which are functionally almost equivalent to the low (PDD) interface in the old model: for example IOCTL_TOUCH_ENABLE_TOUCHPANEL <-> DdsiTouchPanelEnable, IOCTL_TOUCH_GET_SAMPLES <-> DdsiTouchPanelGetPoint and so on (you can see
the details in the documentation if you installed any of the WEC7 CTP). Looking at the system registry you may then have these scenarios:

 [HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\TOUCH]
     "DriverName"="tchproxy.dll"

 [HKEY_LOCAL_MACHINE\Drivers\Touch]
     "Prefix"="TCH"
     "Index"=dword:1
     "Order"=dword:2
     "Flags"=dword:8        ; DEVFLAGS_NAKEDENTRIES
     "IClass"=multi_sz:"{25121442-08CA-48dd-91CC-BFC9F790027C}",
                       "{7119776D-9F23-4e4e-9D9B-9AE962733770}"
     "Priority256"=dword:6D    ; touch ist priority = 109
     "DLL"="touch.dll"

 [HKEY_LOCAL_MACHINE\System\GWE\TouchProxy]
     "tchcaldll"="tchcaldll.dll"
   

This is the default case, in which GWES loads the proxy which in turns load the actual driver. The tchcall.dll includes the standard calibration routines if the driver does not
implements its own. If you’re going to write a driver from scratch this is the recommended approach.

 [HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\TOUCH]
     "DriverName"="touch.dll"
   

In this case GWES loads directly a touch screen driver written according to the old model so you can reuse what you’ve already done.

 [HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\TOUCH]
     "DriverName"="tchproxy.dll"

 [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Touch]
     "Prefix"="TCH"
     "Index"=dword:1
     "Order"=dword:2
     "Flags"=dword:8        ; DEVFLAGS_NAKEDENTRIES
     "IClass"=multi_sz:"{25121442-08CA-48dd-91CC-BFC9F790027C}",
                       "{7119776D-9F23-4e4e-9D9B-9AE962733770}"
     "Priority256"=dword:6D    ; touch ist priority = 109
     "DLL"="touch.dll"

In this case the actual touch screen driver is loaded by the device manager: the touch proxy wait for the driver to be loaded, possibly failing after a timeout. Note that in this case the registry entries must include the {25121442-08CA-48dd-91CC-BFC9F790027C} GUID since the proxy registers a notification for this TCH_DRIVER_CLASS_GUID.

Posted in Windows Embedded Compact | Tagged , , , , | Leave a comment