Windows 7 Sensor & Location Platformを使ってみよう – その5


さて、5回目、さっそくいってみようっ


4回目までは、本質が見えにくくならないよう、Managedコードでの使い方を紹介してきました。(Sensor & Location Platformの概念や使い方はわかっていただけましたか?)
しかし、やはり場合によっては、COMを使った、Nativeコード開発も必要です。ざっとVC++(Native)でプログラムを組む場合のコードを、まずは、紹介してしまいます。


センサーリストの取得:






// SensorMangaerのCOMインターフェイス取り出し
CComPtr<ISensorManager> pSensorManager; 
HRESULT hr = CoCreateInstance(
  __uuidof(SensorManager),NULL,CLSCTX_INPROC_SERVER,
  __uuidof(ISensorManager),(LPVOID*)&pSensorManager);

// SensorCollectionインターフェイスの作成
CComPtr<ISensorCollection> pSensorCollection;
hr = pSensorCollection.CoCreateInstance(CLSID_SensorCollection);

// センサーリスト(照度センサー)を取得
hr = this->m_pSensorManager->GetSensorsByType(SENSOR_TYPE_AMBIENT_LIGHT, &pSensorCollection);


まずは、こんな感じです。もちろん実際には、HRESULT型の戻り値はちゃんとチェックして(!SUCCEEDED(hr)って感じ)エラーの場合には処理が必要です。また、"Sensors.h"の#includeも必要になります。Managedのときと違って、長ったらしいですね。
全てのコードを見せていくのは煩雑なので、以後、Windows API Code Packで使っているCOM Interopの紹介を通じて、読者の皆さんに使い方のあたりをつけてもらいます。


Code PackのSensorsプロジェクトのObjectModelフォルダーにある、SensorManager.csを見てください。この中で、SensorManagerクラスが定義されていて、GetAllSensorsやGetSensorsByCategory等のメソッドは、内部で、nativeISensorManagerというインスタンスを使っているのがわかります。このnativeISensorManagerをC#テキストエディタ上で選択し、右クリック→”定義へ移動”を選択すると、InteropフォルダーのISensorManager.csが開き、nativeISensorManagerに表示が変わります。そこには、






using System.Runtime.InteropService;
...
[ComImport, InterfaceType( ComInterfaceType.InterfaceIsIUnknown ), Guid( "BD77DB67-45A8-42DC-8D00-6DCF15F8377A" )]
internal interface nativeISensorManager
{
....
   [PreserveSig]
    HRESULT GetSensorsByCategory(
        [In, MarshalAs( UnmanagedType.LPStruct )] REFSENSOR_CATEGORY_ID sensorCategory,
        [Out, MarshalAs( UnmanagedType.Interface )] out ISensorCollection ppSensorsFound );


という、定義がなされています。インターフェイスに付与されたComImportやPreserveSig、In, MarshalAs等が、COM Interopであることを記述しています。COMインターフェースは、それぞれGuidの識別子を持っているので、ComImportの第二引数でそれを指定して、このManagedのインターフェイスをCOMのインターフェイスにマップしています。メソッド引数も、それぞれ、



  • REFSENSOR_CATEGORY_ID → System.Runtime.InteropService.UnmanagedType.LPStruct

  • ISensorCollection → System.Runtime.InteropService.UnmanagedType.Interface

としてMarshalingするように指定されています。※Marshalingとは、Nativeコードでの型(メモリ上に素直にアライメントされる)とManagedコードの型(CLRで動作するためのメモリ構造をもつ)の違いを、Native⇔Managed間で処理が移る際に、データの内容を変換することです。場合によっては、このMarshalingで処理時間がかかってパフォーマンスに悪影響を及ぼす場合があるのでご注意、ご注意
このように、NativeのCOMインタフェースをManaged上にマップすることによって、冒頭に書いた正味5行のNativeコードが、Managedではたった二行で記述できるようになります。COM Interopってうまく使えば、便利便利。


さてと。。。他のCOMインターフェイスも基本、全て同じ要領でManagedにマップしているので、一通りInteropフォルダーに入っているソースコードを確認してみてくださいね。おっと、一個だけちょっと違う形式のものがありました。SensorNativeMethods.csです。このファイルでは、





[DllImport( "kernel32.dll" )]
internal static extern bool SystemTimeToFileTime( ref SYSTEMTIME lpSystemTime, …);

と書かれています。これは、Win32 APIを、Managedにマップする方法です。Win32で用意されているSystemTimeToFileTImeという関数をマップしています。この関数は、kernel32.dllに入っているので、DllImport属性でそれを教えているわけです。この形式を使えば、使いたいWin32 APIをManagedコードで利用できます。こちらもMarshalingが入るので、あまり多用すると実行速度に影響を及ぼします。 


さて、COMインターフェイスを使うときと、Codepack(COM Interop)を使う場合とで、最も見た目が違うのが、センサーデータの非同期処理でしょう。Managedの場合は単にDelegatorを登録するだけですが、COMインターフェイスではそうはいきません。センサー状態の変化取得を例に、元々のCOMインターフェイスを使ってNativeでコードを書くとどうなるか最後に紹介してこの回を終わりにします。


まずは、状態の変化を受信するためのコールバッククラスを定義します。






#include "SensorsApi.h"

class CSensorManagerEventSink : public ISensorManagerEvents // COMとして宣言
{
public:
 CSensorManagerEventSink()
   : m_lRefCount(0) { ; }
 STDMETHODIMP QueryInterface(REFIID riid, void **ppObject )
 { // COMの定番コード
  *ppObject = 0 ;
  if( riid == __uuidof(ISensorManagerEvents) ) {
   *ppObject = reinterpret_cast<ISensorManagerEvents*>(this);
  } else if( riid == IID_IUnknown ) {
   *ppObject = reinterpret_cast<IUnknown*>(this);
  } else {
   return E_NOINTERFACE;
  }
  (reinterpret_cast<IUnknown*>(*ppObject))->AddRef();
  return S_OK;
 }
 ULONG _stdcall AddRef() // COMの定番コード
 {
  m_lRefCount++;
  return m_lRefCount;
 }
 ULONG _stdcall Release() // COMの定番コード
 {
  ULONG lRet = --m_lRefCount;
  if (lRet==0){
   delete this;
  }
  return lRet;
 }
 // ISensorManagerEvents methods このメソッドが状態変化時コールされる
 STDMETHOD (OnSensorEnter)(ISensor *pSensor, SensorState state);

private:
 long m_lRefCount;
};


そして、このクラスのOnSensorEnterメソッドの実装を定義します。






STDMETHODIMP CSensorManagerEventSink::OnSensorEnter(ISensor *pSensor, SensorState state)
{
  // ここに処理を記述
  ....
 
 return S_OK;
}


そして、このクラスのインスタンスを、ISensorManagerに登録します。





CSensorManagerEventSink* eventSink = new CSensorManagerEventSink();
sensorManager->SetEventSink(eventSink);

これで、OKです。sensorManagerは、ISensorManager*型、インスタンスへの参照取得は、冒頭のコードを参考にしてください。
CodepackのCOM Interopを使ったWrapperも、NativeのCOMインターフェイスをManagedにマップしながら、上に示したコードのような処理を定義しています。


Managedで処理の概略をおさえ、CodepackのCOM Interopを参考にしながら、Nativeコードを書いてみる、というのも、まぁ乙なもんでしょう。
結構長くなりましたが、この回はこれでおしまい。


その6へ 目次へ

Comments (0)

Skip to main content