Saturday, May 8, 2010

The HTC G Sensor RAPI Client

Now that we have a device RAPI extension DLL to capture the HTC G Sensor data it's time to write the PC client DLL. I wrote this as a native code DLL because it made my life simpler when interfacing with RAPI. It exposes five C-style functions that can be readily P/Invoked from .NET:
  • HTCGSensor_New - Creates a new native client object
  • HTCGSensor_Delete - Destroys the native client object
  • HTCGSensor_Open - Opens the remote HTC G Sensor server
  • HTCGSensor_Close - Closes the remote HTC G Sensor server
  • HTCGSensor_Read - Reads the HTC G Sensor data
The last three should be familiar to you right now, but why do we need the first two? Believe me that I learned this one quite painfully: when exposing native objects to managed code, always provide their memory management functions. The reason is quite simple: if you are allocating the native objects from the native C++ heap, you must delete them using the C++ memory management library. That's why you see a "new" and a "delete".

So what do these functions look like? They are actually very simple:

HTCGCLIENT_API void* HTCGSensor_New()
{
    HTCGSensorClient* pClient = new HTCGSensorClient();
    return pClient;
}

Why the void* return type? These exported functions are meant to be used by the P/Invoke mechanism that should know nothing about the pointer type: it will be mapped to an IntPtr type and treated as an opaque data type. Likewise, there's no need to check the data type on input:
 
HTCGCLIENT_API int HTCGSensor_Open(void* pClient)
{
    HTCGSensorClient* client = (HTCGSensorClient*)pClient;
    if(client)
        return client->Open();
    return E_FAIL;
}

So these functions are there to act as conduits between the a C++ object of type HTCGSensorClient, where the bulk of the work is done, and the calling .NET assembly. It should come as no surprise that this class also implements the Open, Close and Read methods, besides the standard constructor and destructor. Things get a bit more interesting here because we must open a connection to the device server through RAPI (the sample code uses an updated version of the CRemoteAPI class that I published ages ago). In a nutshell, the Open function (see the sample code) initializes RAPI and remotely invokes the HTCGSensorStream function on the HTCGServer.dll file (by default it should be placed on the device's \Windows folder, but you might want to change this). Note that a reference to a client-side version of the IRAPIStream object is kept and that's how the Read function gets the G sensor data. This function is implemented in terms of a write and a read operation. The write tells the server to fetch the G sensor data and then waits on the read for the data to arrive (that's why this might prove slow for high-frequency sampling: for each sample there must be a round-trip to the server). The data is copied to an HTCGSensorData variable passed in by pointer reference. Closing the client implies writing a command to the server and closing the RAPI connection.
 
On my next post I will wrap this small project up by publishing the high-level .NET assemblies that consume the HTC G Sensor data.
 
Sample code: HTCGClient.zip

No comments:

Post a Comment