diff --git a/src/FakeGPS.Driver/Driver.cpp b/src/FakeGPS.Driver/Driver.cpp new file mode 100644 index 0000000..2ce474f --- /dev/null +++ b/src/FakeGPS.Driver/Driver.cpp @@ -0,0 +1,97 @@ +// 2016 OK + +/*++ + +Module: + + Driver.cpp + +Description: + + This module contains the implementation for the sensors service driver callback class. + +--*/ + +#include "Internal.h" +#include "FakeGPSLib.h" // IDL Generated File +#include "Driver.tmh" +#include "Driver.h" +#include "FakeGPS.h" + +/*++ + +CMyDriver::CMyDriver + + Object constructor function + +--*/ +CMyDriver::CMyDriver() +{ +} + +/*++ + +CMyDriver::OnDeviceAdd + + The framework call this function when device is detected. This driver creates a device callback object + +Parameters: + + pDriver - pointer to an IWDFDriver object + pDeviceInit - pointer to a device initialization object + +Return Values: + + S_OK: device initialized successfully + +--*/ +HRESULT CMyDriver::OnDeviceAdd( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit) +{ + // informational at the start of the run in case there are no other log entries to view + Trace(TRACE_LEVEL_CRITICAL, " "); + Trace(TRACE_LEVEL_CRITICAL, "------------------------------ START ------------------------------------------"); + Trace(TRACE_LEVEL_CRITICAL, "FakeGPS - Trace log running with TRACE_LEVEL == CRITICAL"); + Trace(TRACE_LEVEL_ERROR, "FakeGPS - Trace log running with TRACE_LEVEL == ERROR"); + Trace(TRACE_LEVEL_WARNING, "FakeGPS - Trace log running with TRACE_LEVEL == WARNING"); + Trace(TRACE_LEVEL_INFORMATION, "FakeGPS - Trace log running with TRACE_LEVEL == INFORMATION"); + Trace(TRACE_LEVEL_VERBOSE, "FakeGPS - Trace log running with TRACE_LEVEL == VERBOSE"); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = CFakeGPS::CreateInstance(pDriver, pDeviceInit); + + return hr; +} + +/*++ + +CMyDriver::OnInitialize + + The framework calls this function just after loading the driver. The driver + can perform any global, device independent initialization in this routine. + +--*/ +HRESULT CMyDriver::OnInitialize( + _In_ IWDFDriver* pDriver) +{ + UNREFERENCED_PARAMETER(pDriver); + return S_OK; +} + +/*++ + +CMyDriver::OnDeinitialize + + The framework calls this function just before de-initializing itself. All + WDF framework resources should be released by driver before returning + from this call. + +--*/ +void CMyDriver::OnDeinitialize( + _In_ IWDFDriver* pDriver) +{ + UNREFERENCED_PARAMETER(pDriver); + return; +} \ No newline at end of file diff --git a/src/FakeGPS.Driver/Driver.h b/src/FakeGPS.Driver/Driver.h new file mode 100644 index 0000000..bd1782e --- /dev/null +++ b/src/FakeGPS.Driver/Driver.h @@ -0,0 +1,49 @@ +// 2016 OK + +/*++ + +Module: + + Driver.h + +Description: + + This module contains the type definitions for the sensors service + driver callback class. + +--*/ + +#pragma once + +// +// This class handles driver events for the sensors service driver. +// It supports the OnDeviceAdd event, which occurs when the driver is called +// to setup per-device handlers for a new device stack. +// + +class ATL_NO_VTABLE CMyDriver : + public CComObjectRootEx, + public CComCoClass, + public IDriverEntry +{ +public: + + CMyDriver(); + + DECLARE_NO_REGISTRY() + DECLARE_CLASSFACTORY() + DECLARE_NOT_AGGREGATABLE(CMyDriver) + + BEGIN_COM_MAP(CMyDriver) + COM_INTERFACE_ENTRY(IDriverEntry) + END_COM_MAP() + +public: + + // IDriverEntry + STDMETHOD(OnInitialize)(_In_ IWDFDriver* pDriver); + STDMETHOD(OnDeviceAdd)(_In_ IWDFDriver* pDriver, _In_ IWDFDeviceInitialize* pDeviceInit); + STDMETHOD_(void, OnDeinitialize)(_In_ IWDFDriver* pDriver); +}; + +OBJECT_ENTRY_AUTO(__uuidof(FakeGPS), CMyDriver) \ No newline at end of file diff --git a/src/FakeGPS.Driver/FakeGPS.Driver.vcxproj b/src/FakeGPS.Driver/FakeGPS.Driver.vcxproj new file mode 100644 index 0000000..561d645 --- /dev/null +++ b/src/FakeGPS.Driver/FakeGPS.Driver.vcxproj @@ -0,0 +1,346 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F} + $(MSBuildProjectName) + 1 + Win8.1 Debug + Win32 + {4562112D-0C40-48D3-A465-96EE1AED55AB} + + + + WindowsV6.3 + False + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + + True + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + Desktop + + + + + False + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + Desktop + + + + + True + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + Desktop + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + ;%(AdditionalIncludeDirectories) + internal.h + Use + $(IntDir)\internal.h.pch + + + true + true + Trace.h + + + + FakeGPS + Dynamic + + + FakeGPS + Dynamic + AllRules.ruleset + true + + + FakeGPS + Dynamic + + + FakeGPS + Dynamic + + + + Sync + %(PreprocessorDefinitions);_UNICODE;UNICODE + true + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + %(PreprocessorDefinitions);_UNICODE;UNICODE + true + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + %(PreprocessorDefinitions);_UNICODE;UNICODE + true + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + %(PreprocessorDefinitions);_UNICODE;UNICODE + false + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib;$(DDK_LIB_PATH)\SensorsClassExtension.lib;$(SDK_LIB_PATH)\setupapi.lib + FakeGPS.def + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10;$(INCLUDES) + ProgramDatabase + + + true + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib;$(DDK_LIB_PATH)\SensorsClassExtension.lib;$(SDK_LIB_PATH)\setupapi.lib + FakeGPS.def + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib;$(DDK_LIB_PATH)\SensorsClassExtension.lib;$(SDK_LIB_PATH)\setupapi.lib + FakeGPS.def + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + false + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);$(UMDF_INC_PATH)\10 + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\propsys.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib;$(DDK_LIB_PATH)\SensorsClassExtension.lib;$(SDK_LIB_PATH)\setupapi.lib + FakeGPS.def + UseLinkTimeCodeGeneration + + + + + ;%(AdditionalIncludeDirectories) + internal.h + Create + $(IntDir)\internal.h.pch + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/FakeGPS.Driver/FakeGPS.cpp b/src/FakeGPS.Driver/FakeGPS.cpp new file mode 100644 index 0000000..977dcd6 --- /dev/null +++ b/src/FakeGPS.Driver/FakeGPS.cpp @@ -0,0 +1,852 @@ +// 2016 OK + +/*++ + +Module: + + FakeGPS.cpp + +Description: + + This module contains the implementation for the sensor service driver device callback object. + +--*/ + +#include "Internal.h" +#include "FakeGPS.h" +#include "FakeGPS.tmh" +#include "Queue.h" + +#include +#include +#include + +/*++ + +CFakeGPS::CFakeGPS + + Object constructor function + +--*/ +CFakeGPS::CFakeGPS() : + m_spWdfDevice(NULL), + m_pSensorManager(NULL), + m_spQueue(nullptr), + m_dwShutdownControlFlags(0) +{ +} + +/*++ + +CFakeGPS::~CFakeGPS + + Object destructor function + +--*/ +CFakeGPS::~CFakeGPS() +{ + SAFE_RELEASE(m_pSensorManager); +} + +/*++ + +CFakeGPS::CreateInstance + + This static method is used to create and initialize an instance of + CFakeGPS for use with a given hardware device. + +Parameters: + + pDeviceInit - pointer to an interface used to initialize the device + pDriver - pointer to an IWDFDriver interface + +Return Values: + S_OK: object created successfully + +--*/ +HRESULT CFakeGPS::CreateInstance( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + CComObject* pFakeGPS = NULL; + + HRESULT hr = CComObject::CreateInstance(&pFakeGPS); + + if (SUCCEEDED(hr) && (nullptr != pFakeGPS)) + { + pFakeGPS->AddRef(); + + // Prepare device parameters + pDeviceInit->SetLockingConstraint(None); + pDeviceInit->SetPowerPolicyOwnership(TRUE); // Power policy + //pDeviceInit->SetFilter(); // If you're writing a filter driver then set this flag + //pDeviceInit->AutoForwardCreateCleanupClose(WdfTrue); + + CComPtr spCallback; + hr = pFakeGPS->QueryInterface(IID_IUnknown, (void**) &spCallback); + + CComPtr spIWDFDevice; + if (SUCCEEDED(hr)) + { + // Create the IWDFDevice object + hr = pDriver->CreateDevice(pDeviceInit, spCallback, &spIWDFDevice); + } + + if (SUCCEEDED(hr)) + { + // Apply power policy settings + WUDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + + WUDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT( + &idleSettings, + SENSOR_POWER_POLICY_S0_IDLE_CAPABILITIES); + + idleSettings.IdleTimeout = SENSOR_POWER_POLICY_IDLE_TIMEOUT; + idleSettings.ExcludeD3Cold = SENSOR_POWER_POLICY_EXCLUDE_D3_COLD; + + CComPtr spIWDFDevice3; + hr = spIWDFDevice->QueryInterface(IID_PPV_ARGS(&spIWDFDevice3)); + + if (SUCCEEDED(hr)) + { + hr = spIWDFDevice3->AssignS0IdleSettingsEx(&idleSettings); + } + } + + // Release the pFakeGPS pointer when done. Note: UMDF holds a reference to it above + SAFE_RELEASE(pFakeGPS); + } + + return hr; +} + +/*++ + +CFakeGPS::OnPrepareHardware + + Called by UMDF to prepare the hardware for use. In our case + we create the SensorDDI object and initialize the Sensor Class Extension + +Parameters: + pWdfDevice - pointer to an IWDFDevice object representing the device + +Return Values: + S_OK: success + +--*/ +HRESULT CFakeGPS::OnPrepareHardware( + _In_ IWDFDevice* pWdfDevice) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = (NULL != pWdfDevice) ? S_OK : E_UNEXPECTED; + + if (SUCCEEDED(hr)) + { + hr = EnterProcessing(PROCESSING_IPNPCALLBACKHARDWARE); + + if (SUCCEEDED(hr)) + { + if (NULL != pWdfDevice) + { + // Store the IWDFDevice pointer + m_spWdfDevice = pWdfDevice; + } + + // Create & Configure the default IO Queue + if (SUCCEEDED(hr)) + { + hr = ConfigureQueue(); + } + + // Create the sensor manager object + if (SUCCEEDED(hr)) + { + hr = CComObject::CreateInstance(&m_pSensorManager); + + if (nullptr != m_pSensorManager) + { + if ((SUCCEEDED(hr)) && (NULL != m_pSensorManager)) + { + m_pSensorManager->AddRef(); + } + + // Initialize the sensor manager object + if (SUCCEEDED(hr)) + { + hr = m_pSensorManager->Initialize(m_spWdfDevice, this); + } + } + else + { + hr = E_POINTER; + } + } + + if (SUCCEEDED(hr)) + { + hr = StringCchCopy(m_pSensorManager->m_wszDeviceName, MAX_PATH, DEFAULT_DEVICE_MODEL_VALUE); + + if (SUCCEEDED(hr)) + { + ULONG ulCchInstanceId = 0; + BOOL fResult = FALSE; + WCHAR* wszInstanceId = nullptr; + WCHAR* tempStr = nullptr; + + try + { + wszInstanceId = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for instance ID string, hr = %!HRESULT!", hr); + + if (nullptr != wszInstanceId) + { + delete [] wszInstanceId; + } + } + + try + { + tempStr = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for instance ID temp string, hr = %!HRESULT!", hr); + + if (nullptr != tempStr) + { + delete [] tempStr; + } + } + + if (SUCCEEDED(pWdfDevice->RetrieveDeviceInstanceId(NULL, &ulCchInstanceId))) + { + if (SUCCEEDED(pWdfDevice->RetrieveDeviceInstanceId(wszInstanceId, &ulCchInstanceId))) + { + HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE; + + if (INVALID_HANDLE_VALUE != (hDeviceInfo = ::SetupDiCreateDeviceInfoList(NULL, NULL))) + { + SP_DEVINFO_DATA deviceInfo = { sizeof(SP_DEVINFO_DATA) }; + + if (TRUE == ::SetupDiOpenDeviceInfo(hDeviceInfo, wszInstanceId, NULL, 0, &deviceInfo)) + { + DEVPROPTYPE propType; + ULONG ulSize; + + fResult = ::SetupDiGetDeviceProperty(hDeviceInfo, &deviceInfo, &DEVPKEY_Device_DeviceDesc, &propType, (PBYTE) tempStr, MAX_PATH*sizeof(WCHAR), &ulSize, 0); + + if (FALSE == fResult) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + +#pragma warning(suppress: 26035) //possible failure to null terminate string + + if (SUCCEEDED(hr) && (wcscmp(tempStr, L"") != 0)) + { + wcscpy_s(m_pSensorManager->m_wszDeviceName, MAX_PATH, tempStr); + } + + ::SetupDiDestroyDeviceInfoList(hDeviceInfo); + } + } + } + } + +#pragma warning(suppress: 6001) //using unitialized memory + + if (nullptr != wszInstanceId) + { + delete [] wszInstanceId; + } + +#pragma warning(suppress: 6001) //using uninitialized memory + + if (nullptr != tempStr) + { + delete [] tempStr; + } + } + } + + } // processing in progress + + ExitProcessing(PROCESSING_IPNPCALLBACKHARDWARE); + } + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_CRITICAL, "Abnormal results during hardware initialization, hr = %!HRESULT!", hr); + } + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} + +/*++ + +CFakeGPS::OnReleaseHardware + + Called by UMDF to uninitialize the hardware. + +Parameters: + pWdfDevice - pointer to an IWDFDevice object for the device + +Return Values: + S_OK: + +--*/ +HRESULT CFakeGPS::OnReleaseHardware( + _In_ IWDFDevice* pWdfDevice) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + EnterShutdown(); + + // Stop the device and uninitialize the sensor manager object + if (NULL != m_pSensorManager) + { + hr = m_pSensorManager->Stop(); + + m_pSensorManager->Uninitialize(); + SAFE_RELEASE(m_pSensorManager); + } + + // Release the IWDFDevice handle, if it matches + if (pWdfDevice == m_spWdfDevice.p) + { + m_spWdfDevice.Release(); + } + + ExitShutdown(); + + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + Trace(TRACE_LEVEL_CRITICAL, "FakeGPS - Trace log ending for this instance of driver"); + Trace(TRACE_LEVEL_CRITICAL, "------------------------------- END -------------------------------------------"); + + return hr; +} + +/*++ + +CFakeGPS::OnD0Entry + + This method is called after a new device enters the system + +Parameters: + pWdfDevice - pointer to a device object + prwviousSate - power device state + +--*/ +HRESULT CFakeGPS::OnD0Entry( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE previousState) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(previousState); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_IPNPCALLBACK); + + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr) && NULL != m_pSensorManager) + { + hr = m_pSensorManager->Start(); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_CRITICAL, "Failed to initialize sensors, hr = %!HRESULT!", hr); + } + } + + } // processing in progress + + ExitProcessing(PROCESSING_IPNPCALLBACK); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} + +/*++ + +CFakeGPS::OnD0Exit + + This method is called when a device leaves the system + +Parameters: + pWdfDevice - pointer to a device object + newState - power device state + +--*/ +HRESULT CFakeGPS::OnD0Exit( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE newState) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(newState); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_IPNPCALLBACK); + + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr) && NULL != m_pSensorManager) + { + hr = m_pSensorManager->Stop(); + } + + } // processing in progress + + ExitProcessing(PROCESSING_IPNPCALLBACK); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} + +VOID CFakeGPS::OnSurpriseRemoval( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + EnterShutdown(); + + return; +} + +HRESULT CFakeGPS::OnQueryRemove( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + return S_OK; +} + +HRESULT CFakeGPS::OnQueryStop( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + return S_OK; +} + +/*++ + +CFakeGPS::OnCleanupFile + + This method is called when the file handle to the device is closed + +Parameters: + pWdfFile - pointer to a file object + +--*/ +VOID CFakeGPS::OnCleanupFile( + _In_ IWDFFile* pWdfFile) +{ + //Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_IFILECALLBACKCLEANUP); + + if (SUCCEEDED(hr)) + { + if (NULL != m_pSensorManager) + { + m_pSensorManager->CleanupFile(pWdfFile); + } + + } // processing in progress + + ExitProcessing(PROCESSING_IFILECALLBACKCLEANUP); + + return; +} + +/*++ + +CFakeGPS::ConfigureQueue + + This method is called after the device callback object has been initialized + and returned to the driver. It would setup the device's queues and their + corresponding callback objects. + +Parameters: + +Return Values: + S_OK: success + +--*/ +HRESULT CFakeGPS::ConfigureQueue() +{ + HRESULT hr = S_OK; + + CComPtr spIoQueue; + + if (NULL != m_spWdfDevice) + { + m_spWdfDevice->GetDefaultIoQueue(&spIoQueue); + } + else + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr) && (NULL == spIoQueue)) + { + hr = CMyQueue::CreateInstance(m_spWdfDevice, this, &m_spQueue); + } + + return hr; +} + +/*++ + +CFakeGPS::ProcessIoControl + + This method is a helper that takes the incoming IOCTL and forwards + it to the Windows Sensor Class Extension for processing. + +Parameters: + pQueue - [in] pointer to the UMDF queue that handled the request + pRequest - [in] pointer to the request + ControlCode - [in] the IOCTL code + InputBufferSizeInBytes - [in] size of the incoming IOCTL buffer + OutputBufferSizeInBytes - [out] size of the outgoing IOCTL buffer + pcbWritten - pointer to a DWORD containing the number of bytes returned + +Return Values: + S_OK: + +--*/ +HRESULT CFakeGPS::ProcessIoControl( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes, + DWORD* pcbWritten) +{ + UNREFERENCED_PARAMETER(pQueue); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + UNREFERENCED_PARAMETER(pcbWritten); + + HRESULT hr = S_OK; + + if (NULL != m_pSensorManager) + { + hr = m_pSensorManager->ProcessIoControl(pRequest); + } + else + { + hr = E_UNEXPECTED; + } + + return hr; +} + +/*++ + +CFakeGPS::ProcessIoControlRadioManagement + + This method processes IO for Radio Management. The radio state is + either read or set on the sensor. + +Parameters: + pRequest - [in] pointer to the request + ControlCode - [in] the IOCTL code + +Return Values: + S_OK: + +--*/ +HRESULT CFakeGPS::ProcessIoControlRadioManagement( + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode) +{ + HRESULT hr = S_OK; + + if (nullptr != m_pSensorManager) + { + hr = m_pSensorManager->ProcessIoControlRadioManagement(pRequest, ControlCode); + } + else + { + hr = E_UNEXPECTED; + } + + return hr; +} + + +inline HRESULT CFakeGPS::EnterProcessing(DWORD64 dwControlFlag) +{ + //Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + if ((InterlockedOr(&m_dwShutdownControlFlags, dwControlFlag) & SHUTDOWN_IN_PROGRESS) != 0) + { + hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); + } + + return hr; +} + +inline void CFakeGPS::ExitProcessing(DWORD64 dwControlFlag) +{ + //Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + InterlockedAnd(&m_dwShutdownControlFlags, ~dwControlFlag); +} + +inline void CFakeGPS::EnterShutdown() +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + // Begin shutdown. Spin if control handler is in progress. + while (((InterlockedOr(&m_dwShutdownControlFlags, SHUTDOWN_IN_PROGRESS) & PROCESSING_IN_PROGRESS) != 0)) + { + Yield(); + } +} + +inline void CFakeGPS::ExitShutdown() +{ + // End shutdown. Clear the flag. + InterlockedAnd(&m_dwShutdownControlFlags, ~SHUTDOWN_IN_PROGRESS); +} + +/*++ + +CFakeGPS::OnSelfManagedIoCleanup + + Called by UMDF to release memory for a device's self-managed I/O + operations, after the device is removed. + +Parameters: + pWdfDevice - pointer to an IWDFDevice object representing the device + +Return Values: + +--*/ +VOID CFakeGPS::OnSelfManagedIoCleanup( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); +} + +/*++ + +CFakeGPS::OnSelfManagedIoFlush + + Called by UMDF to flush the device for a device's self-managed I/O operations. + +Parameters: + pWdfDevice - pointer to an IWDFDevice object representing the device + +Return Values: + +--*/ +VOID CFakeGPS::OnSelfManagedIoFlush( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = EnterProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + if (SUCCEEDED(hr)) + { + if (nullptr != m_spQueue) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Purging queue"); + m_spQueue->PurgeSynchronously(); + } + } + + ExitProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); +} + +/*++ + +CFakeGPS::OnSelfManagedIoInit + + Called by UMDF to initialize a device's self-managed I/O operations. + +Parameters: + pWdfDevice - pointer to an IWDFDevice object representing the device + +Return Values: + S_OK: success + +--*/ +HRESULT CFakeGPS::OnSelfManagedIoInit( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = EnterProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + if (SUCCEEDED(hr)) + { + if (nullptr != m_pSensorManager) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Hardware is now available"); + m_pSensorManager->m_fDeviceActive = true; + } + } + + ExitProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + return hr; +} + +/*++ + +CFakeGPS::OnSelfManagedIoRestart + + Called by UMDF to restart a device's self-managed I/O operations. + +Parameters: + pWdfDevice - pointer to an IWDFDevice object representing the device + +Return Values: + S_OK: success + +--*/ +HRESULT CFakeGPS::OnSelfManagedIoRestart( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = EnterProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + if (SUCCEEDED(hr)) + { + if (nullptr != m_pSensorManager) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Hardware is now available"); + m_pSensorManager->m_fDeviceActive = true; + } + } + + ExitProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + return hr; +} + +/*++ + +CFakeGPS::OnSelfManagedIoStop + + The OnSelfManagedIoStop method is not used in the current version of the UMDF. + +Parameters: + pWdfDevice - pointer to an IWDFDevice object representing the device + +Return Values: + S_OK: success + +--*/ +HRESULT CFakeGPS::OnSelfManagedIoStop( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + HRESULT hr = S_OK; + + return hr; +} + +/*++ + +CFakeGPS::OnSelfManagedIoSuspend + + Called by UMDF to suspend a device's self-managed I/O operations. + + All outstanding I/O must be completed. The queue is stopped + to flush out in progress requests. The queue is restarted and + the driver continues to get I/O, but the m_fDeviceActive flag is + set to false so that the hardware will not be accessed. + +Parameters: + pWdfDevice - pointer to an IWDFDevice object representing the device + +Return Values: + S_OK: success + +--*/ +HRESULT CFakeGPS::OnSelfManagedIoSuspend( + _In_ IWDFDevice* pWdfDevice) +{ + UNREFERENCED_PARAMETER(pWdfDevice); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = EnterProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + if (SUCCEEDED(hr)) + { + if (nullptr != m_pSensorManager) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Hardware is now not available"); + + m_pSensorManager->m_fDeviceActive = false; + } + + if (nullptr != m_spQueue) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Flushing queue of running requests"); + + // TODO: If all hardware requests are not guaranteed to complete within 1 second then + // the queue should be stopped and all pending hardware requests canceled + //m_spQueue->Stop(nullptr); // Uncomment this line if the hardware requests need to be canceled + + // As noted above, cancel all pending hardware requests here to allow all ISensorDriver:: callbacks to complete + + m_spQueue->StopSynchronously(); + + // NOTE Any asynchronous work that accesses the hardware should be stopped + + m_spQueue->Start(); + } + } + + ExitProcessing(PROCESSING_IPNPCALLBACKSELFMANAGEDIO); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} \ No newline at end of file diff --git a/src/FakeGPS.Driver/FakeGPS.def b/src/FakeGPS.Driver/FakeGPS.def new file mode 100644 index 0000000..6600f5b --- /dev/null +++ b/src/FakeGPS.Driver/FakeGPS.def @@ -0,0 +1,10 @@ +; FakeGPS.def : Declares the module parameters. + +LIBRARY FakeGPS.dll + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + diff --git a/src/FakeGPS.Driver/FakeGPS.h b/src/FakeGPS.Driver/FakeGPS.h new file mode 100644 index 0000000..8b33667 --- /dev/null +++ b/src/FakeGPS.Driver/FakeGPS.h @@ -0,0 +1,100 @@ +// 2016 OK + +/*++ + +Module: + + FakeGPS.h + +Description: + + This module contains the type definitions for the sensor service driver device callback class. + +--*/ + +#pragma once + +#include "SensorManager.h" + +class ATL_NO_VTABLE CFakeGPS : + public CComObjectRootEx, + public IPnpCallback, + public IPnpCallbackHardware, + public IPnpCallbackSelfManagedIo, + public IFileCallbackCleanup +{ +public: + ~CFakeGPS(); + + DECLARE_NOT_AGGREGATABLE(CFakeGPS) + + BEGIN_COM_MAP(CFakeGPS) + COM_INTERFACE_ENTRY(IPnpCallback) + COM_INTERFACE_ENTRY(IPnpCallbackHardware) + COM_INTERFACE_ENTRY(IPnpCallbackSelfManagedIo) + COM_INTERFACE_ENTRY(IFileCallbackCleanup) + END_COM_MAP() + +protected: + + CFakeGPS(); + + HRESULT ConfigureQueue(); + + // COM Interface methods +public: + + // IPnpCallbackHardware + STDMETHOD_(HRESULT, OnPrepareHardware)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(HRESULT, OnReleaseHardware)(_In_ IWDFDevice* pWdfDevice); + + // IPnpCallback + STDMETHOD(OnD0Entry)(_In_ IWDFDevice* pWdfDevice, _In_ WDF_POWER_DEVICE_STATE previousState); + STDMETHOD(OnD0Exit)(_In_ IWDFDevice* pWdfDevice, _In_ WDF_POWER_DEVICE_STATE newState); + STDMETHOD_(VOID, OnSurpriseRemoval)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(HRESULT, OnQueryRemove)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(HRESULT, OnQueryStop)(_In_ IWDFDevice* pWdfDevice); + + // IFileCallbackCleanup + STDMETHOD_(VOID, OnCleanupFile)(_In_ IWDFFile *pWdfFile); + + // IPnpCallbackSelfManagedIo + STDMETHOD_(VOID, OnSelfManagedIoCleanup)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(VOID, OnSelfManagedIoFlush)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoInit)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoRestart)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoStop)(_In_ IWDFDevice* pWdfDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoSuspend)(_In_ IWDFDevice* pWdfDevice); + +public: + + // The factory method used to create an instance of this device + static HRESULT CreateInstance( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit); + + HRESULT ProcessIoControl( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes, + DWORD* pcbWritten); + + HRESULT ProcessIoControlRadioManagement( + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode); + + inline HRESULT EnterProcessing(DWORD64 dwControlFlag); + inline void ExitProcessing(DWORD64 dwControlFlag); + +private: + + inline void EnterShutdown(); + inline void ExitShutdown(); + + CComPtr m_spWdfDevice; + CComObject* m_pSensorManager; + CComPtr m_spQueue; + volatile DWORD64 m_dwShutdownControlFlags; +}; \ No newline at end of file diff --git a/src/FakeGPS.Driver/FakeGPS.inf b/src/FakeGPS.Driver/FakeGPS.inf new file mode 100644 index 0000000..24087d7 Binary files /dev/null and b/src/FakeGPS.Driver/FakeGPS.inf differ diff --git a/src/FakeGPS.Driver/FakeGPS.rc b/src/FakeGPS.Driver/FakeGPS.rc new file mode 100644 index 0000000..f658fe7 --- /dev/null +++ b/src/FakeGPS.Driver/FakeGPS.rc @@ -0,0 +1,88 @@ +// Microsoft Visual C++ generated resource script. +// +#include "Resource.h" +///////////////////////////////////////////////////////////////////////////// +// English (United Kingdom) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x9L +#else + FILEFLAGS 0x8L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Julian Kay" + VALUE "FileDescription", "FakeGPS Sensor" + VALUE "FileVersion", "1.0.0.0" + VALUE "InternalName", "FakeGPS" + VALUE "LegalCopyright", "© Julian Kay" + VALUE "OriginalFilename", "FakeGPS.dll" + VALUE "ProductName", "FakeGPS Sensor" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (United Kingdom) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/FakeGPS.Driver/FakeGPSLib.idl b/src/FakeGPS.Driver/FakeGPSLib.idl new file mode 100644 index 0000000..3c0d7c7 --- /dev/null +++ b/src/FakeGPS.Driver/FakeGPSLib.idl @@ -0,0 +1,31 @@ +/*++ + +Module: + + SensorsServiceDriver.idl + +Description: + + Sensors service driver + +--*/ + +import "wudfddi.idl"; + +[ + uuid(cbb555e7-a07a-4fac-a32b-94428ce1c592), + version(1.0), + helpstring("Fake GPS") +] +library FakeGPSLib +{ + importlib("stdole2.tlb"); + [ + uuid(ceeb6d87-3f96-4cc8-9f46-369939171dc7), + helpstring("FakeGPS Class") + ] + coclass FakeGPS + { + [default] interface IDriverEntry; + }; +}; diff --git a/src/FakeGPS.Driver/FakeGPSModule.cpp b/src/FakeGPS.Driver/FakeGPSModule.cpp new file mode 100644 index 0000000..81b6dd4 --- /dev/null +++ b/src/FakeGPS.Driver/FakeGPSModule.cpp @@ -0,0 +1,162 @@ +// 2016 OK + +/*++ + +Module: + + FakeGPSModule.cpp + +Description: + + This module contains the implementation of the sensors service driver's + entry point and its exported functions for providing COM support. + + This module is dependent on the following defines: + + MYDRIVER_TRACING_ID - A wide string passed to WPP when initializing tracing. + +--*/ + +#include "Internal.h" +#include "FakeGPSModule.tmh" + +class CFakeGPSModule : public CAtlDllModuleT {} _AtlModule; + +/*++ + +DllMain + + This is the main DLL Entry Point. + +Parameters: + + hInstance - Handle to the DLL module + dwReason - Indicates why the DLL entry point is being called + lpReserved - Additional information based on dwReason + +Return Values: + + TRUE = initialization succeeds + FALSE = initialization fails + +--*/ +extern "C" BOOL WINAPI DllMain( + HINSTANCE hInstance, + DWORD dwReason, + LPVOID lpReserved) +{ + (lpReserved); + + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + // Initialize tracing + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + DisableThreadLibraryCalls(hInstance); + break; + + case DLL_PROCESS_DETACH: + // Cleanup tracing. + WPP_CLEANUP(); + _AtlModule.Term(); + break; + + default: + break; + } + + // Call the ATL module class so it can initialize + return _AtlModule.DllMain(dwReason, lpReserved); +} + +/*++ + +DllCanUnloadNow + + Used to determine whether the DLL can be unloaded by OLE + +Parameters: + + void - (unused argument) + +Return Values: + + S_OK: DLL can be unloaded + S_FALSE: DLL cannot be unloaded now + +--*/ +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + +/*++ + +DllGetClassObject + + Returns a class factory to create an object of the requested type + +Parameters: + + rclsid - CLSID that will associate the correct data and code + riid - Reference to the IID the caller will use + ppv - pointer to an interface pointer requested in riid + +Return Values: + + S_OK: The object was retrieved successfully. + CLASS_E_CLASSNOTAVAILABLE: The DLL does not support the class + +--*/ +STDAPI DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + +/*++ + +DllRegisterServer + + Adds entries to the system registry + +Parameters: + + void - (unused argument) + +Return Values: + + S_OK: The registry entries were created successfully + SELFREG_E_TYPELIB: The server was unable to complete the registration of all the type libraries used by its classes + SELFREG_E_CLASS: The server was unable to complete the registration of all the object classes + +--*/ +__control_entrypoint(DllExport) +STDAPI DllRegisterServer(void) +{ + return S_OK; +} + +/*++ + +DllUnregisterServer + + Removes entries from the system registry + +Parameters: + void - (unused argument) + +Return Values: + S_OK: The registry entries were removed successfully + S_FALSE: Unregistration of known entries was successful, but other entries still exist for this server's classes + SELFREG_E_TYPELIB: The server was unable to remove the entries of all the type libraries used by its classes + SELFREG_E_CLASS: The server was unable to remove the entries of all the object classes + +--*/ +__control_entrypoint(DllExport) +STDAPI DllUnregisterServer(void) +{ + return S_OK; +} \ No newline at end of file diff --git a/src/FakeGPS.Driver/GeolocationSensor.cpp b/src/FakeGPS.Driver/GeolocationSensor.cpp new file mode 100644 index 0000000..c827383 --- /dev/null +++ b/src/FakeGPS.Driver/GeolocationSensor.cpp @@ -0,0 +1,1477 @@ +// 2016 OK + +/* + +Module: + + Geolocation.cpp + +Description: + + Implements the CGeolocationSensor container class + +--*/ + +#include "Internal.h" +#include "SensorDDI.h" +#include "SensorManager.h" +#include "Sensor.h" +#include "GeolocationSensor.h" +#include "GeolocationSensor.tmh" + +#if USE_GEOLOCATION_SPECIFIC_UNIQUE_ID +// {A3921B4F-BEEF-BEEF-94DB-47F59CBDE2C9} +static const GUID SENSOR_ID_FAKEGPS = +{ 0xa3921b4f, 0xbeef, 0xbeef, { 0x94, 0xdb, 0x47, 0xf5, 0x9c, 0xbd, 0xe2, 0xc9 } }; +#endif + +const PROPERTYKEY g_SupportedGeolocationProperties [] = +{ + SENSOR_PROPERTY_TYPE, // [VT_CLSID] + SENSOR_PROPERTY_STATE, // [VT_UI4] + SENSOR_PROPERTY_MIN_REPORT_INTERVAL, // [VT_UI4] + SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, // [VT_UI4] + SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID, // [VT_CLSID] + SENSOR_PROPERTY_MANUFACTURER, // [VT_LPWSTR] + SENSOR_PROPERTY_MODEL, // [VT_LPWSTR] + SENSOR_PROPERTY_SERIAL_NUMBER, // [VT_LPWSTR] + SENSOR_PROPERTY_FRIENDLY_NAME, // [VT_LPWSTR] + SENSOR_PROPERTY_DESCRIPTION, // [VT_LPWSTR] + SENSOR_PROPERTY_CONNECTION_TYPE, // [VT_UI4] + SENSOR_PROPERTY_CHANGE_SENSITIVITY, // [VT_UNKNOWN], IPortableDeviceValues + SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY, // [VT_UI4] + SENSOR_PROPERTY_RADIO_STATE, // [VT_UI4] + SENSOR_PROPERTY_RADIO_STATE_PREVIOUS, // [VT_UI4] + WPD_FUNCTIONAL_OBJECT_CATEGORY, // [VT_CLSID] +}; + +const PROPERTYKEY g_OptionalSupportedGeolocationProperties [] = +{ + SENSOR_PROPERTY_RANGE_MAXIMUM, // [VT_UNKNOWN], IPortableDeviceValues + SENSOR_PROPERTY_RANGE_MINIMUM, // [VT_UNKNOWN], IPortableDeviceValues + SENSOR_PROPERTY_ACCURACY, // [VT_UNKNOWN], IPortableDeviceValues + SENSOR_PROPERTY_RESOLUTION, // [VT_UNKNOWN], IPortableDeviceValues +}; + +const PROPERTYKEY g_SettableGeolocationProperties [] = +{ + SENSOR_PROPERTY_CHANGE_SENSITIVITY, // [VT_UNKNOWN], IPortableDeviceValues + SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, // [VT_UI4] + SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY, // [VT_UI4] +}; + +const PROPERTYKEY g_SupportedGeolocationDataFields [] = +{ + SENSOR_DATA_TYPE_TIMESTAMP, // [VT_FILETIME] + SENSOR_DATA_TYPE_LATITUDE_DEGREES, // [VT_R8] + SENSOR_DATA_TYPE_LONGITUDE_DEGREES, // [VT_R8] + SENSOR_DATA_TYPE_ERROR_RADIUS_METERS, // [VT_R8] + SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS, // [VT_R8] + SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS, // [VT_R8] + SENSOR_DATA_TYPE_SPEED_KNOTS, // [VT_R8] + SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES, // [VT_R8] +}; + +const PROPERTYKEY g_RequiredGeolocationDataFields [] = +{ + SENSOR_DATA_TYPE_LATITUDE_DEGREES, // [VT_R8] + SENSOR_DATA_TYPE_LONGITUDE_DEGREES, // [VT_R8] + SENSOR_DATA_TYPE_ERROR_RADIUS_METERS, // [VT_R8] + SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS, // [VT_R8] + SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS, // [VT_R8] + SENSOR_DATA_TYPE_SPEED_KNOTS, // [VT_R8] + SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES, // [VT_R8] +}; + +const PROPERTYKEY g_SupportedGeolocationEvents [] = +{ + SENSOR_EVENT_DATA_UPDATED, 0, + SENSOR_EVENT_STATE_CHANGED, 0, +}; + +/*++ + +CGeolocationSensor::CGeolocationSensor + + Object constructor function + +--*/ +CGeolocationSensor::CGeolocationSensor() +{ +} + +/*++ + +CGeolocationSensor::~CGeolocationSensor + + Object destructor function + +--*/ +CGeolocationSensor::~CGeolocationSensor() +{ +} + +/*++ + +CGeolocationSensor::Initialize + + Initializes the PROPERTYKEY/PROPVARIANT values for the Supported Properties & Supported Data + +--*/ +HRESULT CGeolocationSensor::Initialize( + _In_ SensorType sensType, + _In_ DWORD sensNum, + _In_ LPWSTR pwszManufacturer, + _In_ LPWSTR pwszProduct, + _In_ LPWSTR pwszSerialNumber, + _In_ LPWSTR sensorID, + _In_ IWDFDevice* pWdfDevice, + _In_ CSensorManager* pSensorManager) +{ + // Check if we are already initialized + HRESULT hr = (TRUE == IsInitialized()) ? E_UNEXPECTED : S_OK; + + if (SUCCEEDED(hr)) + { + hr = InitializeSensor( + sensType, + sensNum, + pwszManufacturer, + pwszProduct, + pwszSerialNumber, + sensorID, + pWdfDevice); + + if (SUCCEEDED(hr)) + { + // reset the initialization flag + // since there is specialized + // initialization still to do + m_fSensorInitialized = FALSE; + } + } + + strcpy_s(m_SensorName, DESCRIPTOR_MAX_LENGTH, SENSOR_GEOLOCATION_TRACE_NAME); + + m_pSensorManager = pSensorManager; + + if (SUCCEEDED(hr)) + { + hr = InitializeGeolocation(pWdfDevice); + } + + if (SUCCEEDED(hr)) + { + m_fSensorInitialized = TRUE; + } + + return hr; +} + +/*++ + +CGeolocationSensor::InitializeGeolocation + + Initializes the Geolocation PropertyKeys and DataFieldKeys + +--*/ +HRESULT CGeolocationSensor::InitializeGeolocation(_In_ IWDFDevice* pWdfDevice) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + m_pWdfDevice = pWdfDevice; + + HRESULT hr = S_OK; + + hr = AddGeolocationPropertyKeys(); + + if (SUCCEEDED(hr)) + { + hr = AddGeolocationSettablePropertyKeys(); + } + + if (SUCCEEDED(hr)) + { + hr = AddGeolocationDataFieldKeys(); + } + + if (SUCCEEDED(hr)) + { + hr = SetGeolocationDefaultValues(pWdfDevice); + } + + return hr; +} + + +/*++ + +CGeolocationSensor::AddGeolocationPropertyKeys + + Copies the PROPERTYKEYS for Geolocation Supported Properties and sets the Values to VT_EMPTY + +--*/ +HRESULT CGeolocationSensor::AddGeolocationPropertyKeys() +{ + HRESULT hr = S_OK; + + for (DWORD dwIndex = 0; dwIndex < ARRAY_SIZE(g_SupportedGeolocationProperties); dwIndex++) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Initialize all the PROPERTYKEY values to VT_EMPTY + hr = SetProperty(g_SupportedGeolocationProperties[dwIndex], &var, nullptr); + + // Also add the PROPERTYKEY to the list of supported properties + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorProperties->Add(g_SupportedGeolocationProperties[dwIndex]); + } + + PropVariantClear(&var); + } + + for (DWORD dwIndex = 0; dwIndex < ARRAY_SIZE(g_OptionalSupportedGeolocationProperties); dwIndex++) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Initialize all the PROPERTYKEY values to VT_EMPTY + hr = SetProperty(g_OptionalSupportedGeolocationProperties[dwIndex], &var, nullptr); + + // Also add the PROPERTYKEY to the list of settable properties + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorProperties->Add(g_OptionalSupportedGeolocationProperties[dwIndex]); + } + + PropVariantClear(&var); + } + + return hr; +} + +/*++ + +CGeolocationSensor::AddGeolocationSettablePropertyKeys + + Copies the PROPERTYKEYS for Geolocation Supported Properties and sets the Values to VT_EMPTY + +--*/ +HRESULT CGeolocationSensor::AddGeolocationSettablePropertyKeys() +{ + HRESULT hr = S_OK; + + for (DWORD dwIndex = 0; dwIndex < ARRAY_SIZE(g_SettableGeolocationProperties); dwIndex++) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Initialize all the PROPERTYKEY values to VT_EMPTY + hr = SetProperty(g_SettableGeolocationProperties[dwIndex], &var, nullptr); + + // Also add the PROPERTYKEY to the list of settable properties + if (SUCCEEDED(hr)) + { + hr = m_spSettableSensorProperties->Add(g_SettableGeolocationProperties[dwIndex]); + } + + PropVariantClear(&var); + } + + return hr; +} + +/*++ + +CGeolocationSensor::AddGeolocationDataFieldKeys + + Copies the PROPERTYKEYS for Geolocation Supported DataFields and sets the Values to VT_EMPTY + +--*/ +HRESULT CGeolocationSensor::AddGeolocationDataFieldKeys() +{ + HRESULT hr = S_OK; + + DWORD dwIndex; + + for (dwIndex = 0; (dwIndex < ARRAY_SIZE(g_SupportedGeolocationDataFields)) && SUCCEEDED(hr); dwIndex++) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Initialize all the PROPERTYKEY values to VT_EMPTY + hr = SetDataField(g_SupportedGeolocationDataFields[dwIndex], &var); + + // Also add the PROPERTYKEY to the list of supported data fields + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->Add(g_SupportedGeolocationDataFields[dwIndex]); + } + + PropVariantClear(&var); + } + + for (dwIndex = 0; (dwIndex < ARRAY_SIZE(g_RequiredGeolocationDataFields)) && SUCCEEDED(hr); dwIndex++) + { + // initialization of the data field was accomplished in the supported data field for loop + + hr = m_spRequiredDataFields->Add(g_RequiredGeolocationDataFields[dwIndex]); + } + + return hr; +} + +/*++ + +CGeolocationSensor::SetGeolocationDefaultValues + + Fills in default values for most Geolocation Properties and Data Fields. + +--*/ +HRESULT CGeolocationSensor::SetGeolocationDefaultValues(_In_ IWDFDevice* pWdfDevice) +{ + HRESULT hr = S_OK; + WCHAR tempStr[DESCRIPTOR_MAX_LENGTH]; + + if ((NULL == m_spSensorPropertyValues) || (NULL == m_spSensorDataFieldValues) || (NULL == m_pSensorManager)) + { + hr = E_POINTER; + } + + + m_Latitude = 0.0; + m_Longitude = 0.0; + + // Default values for SENSOR PROPERTIES + m_ulDefaultCurrentReportInterval = DEFAULT_GEOLOCATION_CURRENT_REPORT_INTERVAL; + m_ulDefaultMinimumReportInterval = GEOLOCATION_MIN_REPORT_INTERVAL; + + m_fltDefaultChangeSensitivity = DEFAULT_GEOLOCATION_CHANGE_SENSITIVITY; + + m_fltDefaultRangeMaximum = DEFAULT_GEOLOCATION_MAXIMUM; + m_fltDefaultRangeMinimum = DEFAULT_GEOLOCATION_MINIMUM; + m_fltDefaultAccuracy = DEFAULT_GEOLOCATION_ACCURACY; + m_fltDefaultResolution = DEFAULT_GEOLOCATION_RESOLUTION; + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, SENSOR_CATEGORY_LOCATION); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetGuidValue(SENSOR_PROPERTY_TYPE, SENSOR_TYPE_LOCATION_GPS); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_STATE, SENSOR_STATE_MIN); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_MIN_REPORT_INTERVAL, GEOLOCATION_MIN_REPORT_INTERVAL); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetStringValue(SENSOR_PROPERTY_MANUFACTURER, m_pwszManufacturer); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetStringValue(SENSOR_PROPERTY_MODEL, m_pwszProduct); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetStringValue(SENSOR_PROPERTY_SERIAL_NUMBER, m_pwszSerialNumber); + } + + if (SUCCEEDED(hr)) + { + if (m_pSensorManager->m_NumMappedSensors > 1) + { + StringCchCopy(tempStr, DESCRIPTOR_MAX_LENGTH, m_pSensorManager->m_wszDeviceName); + StringCchCat(tempStr, DESCRIPTOR_MAX_LENGTH, L": "); + StringCchCat(tempStr, DESCRIPTOR_MAX_LENGTH, SENSOR_GEOLOCATION_NAME); + hr = m_spSensorPropertyValues->SetStringValue(SENSOR_PROPERTY_FRIENDLY_NAME, tempStr); + } + else + { + hr = m_spSensorPropertyValues->SetStringValue(SENSOR_PROPERTY_FRIENDLY_NAME, m_pSensorManager->m_wszDeviceName); + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetStringValue(SENSOR_PROPERTY_DESCRIPTION, SENSOR_GEOLOCATION_DESCRIPTION); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_CONNECTION_TYPE, SENSOR_CONNECTION_TYPE_PC_INTEGRATED); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY, LOCATION_DESIRED_ACCURACY_DEFAULT); + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // Radio State values are persisted, pull them from property store + CComPtr spPropStore; + hr = pWdfDevice->RetrieveDevicePropertyStore(NULL, WdfPropertyStoreCreateIfMissing, &spPropStore, NULL); + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + + hr = spPropStore->GetNamedValue(PROP_STORE_KEY_RADIO_STATE, &var); + if (SUCCEEDED(hr)) + { + m_ulCurrentGeolocationRadioState = var.ulVal; + m_ulRequiredGeolocationRadioState = var.ulVal; + } + else + { + PropVariantInit(&var); + var.vt = VT_UI4; + var.ulVal = m_ulCurrentGeolocationRadioState; + hr = spPropStore->SetNamedValue(PROP_STORE_KEY_RADIO_STATE, &var); + PropVariantClear(&var); + } + + if (SUCCEEDED(hr)) + { + hr = spPropStore->GetNamedValue(PROP_STORE_KEY_PREVIOUS_RADIO_STATE, &var); + if (SUCCEEDED(hr)) + { + m_ulPreviousGeolocationRadioState = var.ulVal; + } + else + { + PropVariantInit(&var); + var.vt = VT_UI4; + var.ulVal = m_ulPreviousGeolocationRadioState; + hr = spPropStore->SetNamedValue(PROP_STORE_KEY_PREVIOUS_RADIO_STATE, &var); + PropVariantClear(&var); + } + } + } + + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_RADIO_STATE, m_ulCurrentGeolocationRadioState); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_RADIO_STATE_PREVIOUS, m_ulPreviousGeolocationRadioState); + } + + // TODO: Default + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + + // Will use as a default, if not set + LPWSTR lat = L"1.31337"; + + hr = spPropStore->GetNamedValue(PROP_STORE_KEY_LATITUDE, &var); + if (SUCCEEDED(hr)) + { + lat = var.pwszVal; + } + else + { + PropVariantInit(&var); + var.vt = VT_LPWSTR; //pwszVal + var.pwszVal = lat; + hr = spPropStore->SetNamedValue(PROP_STORE_KEY_LATITUDE, &var); + PropVariantClear(&var); + } + + m_Latitude = _tstof(lat); + } + + // TODO: Default + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + + // Will use as a default, if not set + LPWSTR lon = L"1.31337"; + + hr = spPropStore->GetNamedValue(PROP_STORE_KEY_LONGITUDE, &var); + if (SUCCEEDED(hr)) + { + lon = var.pwszVal; + } + else + { + PropVariantInit(&var); + var.vt = VT_LPWSTR; //pwszVal + var.pwszVal = lon; + hr = spPropStore->SetNamedValue(PROP_STORE_KEY_LONGITUDE, &var); + PropVariantClear(&var); + } + + m_Longitude = _tstof(lon); + } + +#else + UNREFERENCED_PARAMETER(pWdfDevice); +#endif + + // Default values for SENSOR PER-DATAFIELD PROPERTIES + + DWORD uPropertyCount = 0; + PROPERTYKEY propkey; + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorProperties->GetCount(&uPropertyCount); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, DEFAULT_GEOLOCATION_CURRENT_REPORT_INTERVAL); + } + + if (SUCCEEDED(hr)) + { + // Only set the defaults if the property is supported + for (DWORD i = 0; i < uPropertyCount; i++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorProperties->GetAt(i, &propkey); + } + + if (SUCCEEDED(hr) && (SENSOR_PROPERTY_CHANGE_SENSITIVITY == propkey)) + { + CComPtr spChangeSensitivityValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spChangeSensitivityValues)); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if ((SENSOR_DATA_TYPE_LATITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_LONGITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_ERROR_RADIUS_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS == datakey) || + (SENSOR_DATA_TYPE_SPEED_KNOTS == datakey) || + (SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES == datakey)) + { + hr = spChangeSensitivityValues->SetFloatValue(datakey, m_fltDefaultChangeSensitivity); + } + } + } + } + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_CHANGE_SENSITIVITY, spChangeSensitivityValues); + } + } + + if (SUCCEEDED(hr) && (SENSOR_PROPERTY_RANGE_MAXIMUM == propkey)) + { + CComPtr spMaximumValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spMaximumValues)); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if ((SENSOR_DATA_TYPE_LATITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_LONGITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_ERROR_RADIUS_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS == datakey) || + (SENSOR_DATA_TYPE_SPEED_KNOTS == datakey) || + (SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES == datakey)) + { + hr = spMaximumValues->SetFloatValue(datakey, m_fltDefaultRangeMaximum); + } + } + } + } + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_RANGE_MAXIMUM, spMaximumValues); + } + } + + if (SUCCEEDED(hr) && (SENSOR_PROPERTY_RANGE_MINIMUM == propkey)) + { + CComPtr spMinimumValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spMinimumValues)); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if ((SENSOR_DATA_TYPE_LATITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_LONGITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_ERROR_RADIUS_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS == datakey) || + (SENSOR_DATA_TYPE_SPEED_KNOTS == datakey) || + (SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES == datakey)) + { + hr = spMinimumValues->SetFloatValue(datakey, m_fltDefaultRangeMinimum); + } + } + } + } + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_RANGE_MINIMUM, spMinimumValues); + } + } + + if (SUCCEEDED(hr) && (SENSOR_PROPERTY_ACCURACY == propkey)) + { + CComPtr spAccuracyValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spAccuracyValues)); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if ((SENSOR_DATA_TYPE_LATITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_LONGITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_ERROR_RADIUS_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS == datakey) || + (SENSOR_DATA_TYPE_SPEED_KNOTS == datakey) || + (SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES == datakey)) + { + hr = spAccuracyValues->SetFloatValue(datakey, m_fltDefaultAccuracy); + } + } + } + } + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_ACCURACY, spAccuracyValues); + } + } + + if (SUCCEEDED(hr) && (SENSOR_PROPERTY_RESOLUTION == propkey)) + { + CComPtr spResolutionValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spResolutionValues)); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if ((SENSOR_DATA_TYPE_LATITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_LONGITUDE_DEGREES == datakey) || + (SENSOR_DATA_TYPE_ERROR_RADIUS_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS == datakey) || + (SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS == datakey) || + (SENSOR_DATA_TYPE_SPEED_KNOTS == datakey) || + (SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES == datakey)) + { + hr = spResolutionValues->SetFloatValue(datakey, m_fltDefaultResolution); + } + } + } + } + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_RESOLUTION, spResolutionValues); + } + } + } + } + + // Default values for SENSOR DATA FIELDS + + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + + // Get the current time as FILETIME format + FILETIME ft; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + GetSystemTimePreciseAsFileTime(&ft); // Use the higher resolution timer when available +#else + GetSystemTimeAsFileTime(&ft); // API not available, fallback to 16ms resolution timer +#endif + + hr = InitPropVariantFromFileTime(&ft, &var); + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_TIMESTAMP, &var); + } + + PropVariantClear(&var); + } + + PROPVARIANT value; + PropVariantInit(&value); + + value.vt = VT_EMPTY; + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_LATITUDE_DEGREES, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_LONGITUDE_DEGREES, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ERROR_RADIUS_METERS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_SPEED_KNOTS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES, &value); + } + + PropVariantClear(&value); + + return hr; +} + + +/*++ + +CGeolocationSensor::GetPropertyValuesForGeolocationObject + + This method is called to populate property values for the object specified. + + Read the specified properties for the specified object and populate pValues with the results. + +Parameters: + + wszObjectID - the object whose properties are being requested. + pKeys - the list of property keys of the properties to request from the object + pValues - an IPortableDeviceValues which will contain the property values retreived from the object + +--*/ +HRESULT CGeolocationSensor::GetPropertyValuesForGeolocationObject( + LPCWSTR wszObjectID, + IPortableDeviceKeyCollection* pKeys, + IPortableDeviceValues* pValues) +{ + HRESULT hr = S_OK; + CAtlStringW strObjectID = wszObjectID; + DWORD cKeys = 0; + BOOL fError = FALSE; + + if ((wszObjectID == NULL) || + (pKeys == NULL) || + (pValues == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + hr = pKeys->GetCount(&cKeys); + + if ((NULL == m_spSensorPropertyValues) || (NULL == m_spSensorDataFieldValues)) + { + hr = E_POINTER; + } + + if (hr == S_OK) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + hr = pKeys->GetAt(dwIndex, &Key); + + if (hr == S_OK && !IsEqualPropertyKey(Key, WPD_PROPERTY_NULL)) + { + // Preset the property value to 'error not supported'. The actual value + // will replace this value, if read from the device. + pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + + // Set general properties for sensor + if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_ID, m_SensorID); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + hr = pValues->SetStringValue(WPD_OBJECT_NAME, SENSOR_GEOLOCATION_NAME); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, m_SensorID); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, WPD_DEVICE_OBJECT_ID); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE); + } + else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY)) + { + hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, SENSOR_CATEGORY_LOCATION); + } + else + { + // Get Geolocation sensor properties + PROPVARIANT value; + PropVariantInit(&value); + + HRESULT hrTemp = m_pSensorManager->m_pSensorList.GetAt(m_pSensorManager->m_pSensorList.FindIndex(m_SensorNum))->GetProperty(Key, &value); + if (SUCCEEDED(hrTemp)) + { + pValues->SetValue(Key, &value); + } + else + { + // Failed to find the requested property, convey the hr back to the caller + pValues->SetErrorValue(Key, hrTemp); + fError = TRUE; + } + + PropVariantClear(&value); + } + } + } + } + + return (FALSE == fError) ? hr : S_FALSE; +} + +/*++ + +CGeolocationSensor::UpdateGeolocationPropertyValues + + This method updates the writable properties of the sensor. + +--*/ +HRESULT CGeolocationSensor::UpdateGeolocationPropertyValues() +{ + HRESULT hr = E_UNEXPECTED; + + hr = HandleReportIntervalUpdate(); + + if (SUCCEEDED(hr)) + { + HandleLocationDesiredAccuracyUpdate(); + } + + if (SUCCEEDED(hr)) + { + HandleGeolocationRadioStateUpdate(); + } + + if (SUCCEEDED(hr)) + { + HandleChangeSensitivityUpdate(); + } + + if (SUCCEEDED(hr)) + { + HandleSetReportingAndPowerStates(); + } + + return hr; +} + +/*++ + +CGeolocationSensor::UpdateGeolocationDataValues + + This method sends a poll data request to the device. + +--*/ +HRESULT CGeolocationSensor::UpdateGeolocationDataFieldValues() +{ + HRESULT hr = S_OK; + + // Only use the device if powered on + if (m_pSensorManager->m_fDeviceActive) + { + bool fContinue = true; +#if (NTDDI_VERSION >= NTDDI_WIN8) + fContinue = DRS_RADIO_ON == m_ulRequiredGeolocationRadioState; +#endif + + if (fContinue) + { + PROPVARIANT value; + PropVariantInit(&value); + + // move out of loop? + CComPtr spPropStore; + hr = m_pWdfDevice->RetrieveDevicePropertyStore(NULL, WdfPropertyStoreCreateIfMissing, &spPropStore, NULL); + + // TODO: Default + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + hr = spPropStore->GetNamedValue(PROP_STORE_KEY_LATITUDE, &var); + if (SUCCEEDED(hr)) + { + LPWSTR lat; + lat = var.pwszVal; + m_Latitude = _tstof(lat); + } + } + + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + hr = spPropStore->GetNamedValue(PROP_STORE_KEY_LONGITUDE, &var); + if (SUCCEEDED(hr)) + { + LPWSTR lon; + lon = var.pwszVal; + m_Longitude = _tstof(lon); + } + } + + DOUBLE dblNewLatitude = m_Latitude; + DOUBLE dblNewLongitude = m_Longitude; + + // NOTE: these guys are currently static values + const DOUBLE dblNewErrorRadius = 4.321; + const DOUBLE dblNewAltitude = 0; + const DOUBLE dblNewAltitudeError = 9.876; + const DOUBLE dblNewSpeed = 0; + const DOUBLE dblNewHeading = 0; + + bool fStateChanged = FALSE; + bool fHasValidData = FALSE; + + if (SUCCEEDED(hr)) + { + // Get the current time as FILETIME format + FILETIME ft; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + GetSystemTimePreciseAsFileTime(&ft); // Use the higher resolution timer when available +#else + GetSystemTimeAsFileTime(&ft); // API not available, fallback to lower resolution timer +#endif + + hr = InitPropVariantFromFileTime(&ft, &value); + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_TIMESTAMP, &value); + + PropVariantClear(&value); + } + } + + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + + FLOAT fltMax = GetRangeMaximumValue( + m_fltDefaultRangeMaximum, + TRUE, + 90.0F, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum); + + FLOAT fltMin = GetRangeMinimumValue( + m_fltDefaultRangeMinimum, + TRUE, + -90.0F, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum); + + if ((dblNewLatitude > fltMax) || (dblNewLatitude < fltMin)) + { + value.vt = VT_NULL; + } + else + { + value.vt = VT_R8; + value.dblVal = dblNewLatitude; + fHasValidData = TRUE; + } + + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_LATITUDE_DEGREES, &value); + + PropVariantClear(&value); + } + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + + FLOAT fltMax = GetRangeMaximumValue( + m_fltDefaultRangeMaximum, + TRUE, + 180.0F, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum); + + FLOAT fltMin = GetRangeMinimumValue( + m_fltDefaultRangeMinimum, + TRUE, + -180.0F, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum); + + if ((dblNewLongitude > fltMax) || (dblNewLongitude < fltMin)) + { + value.vt = VT_NULL; + } + else + { + value.vt = VT_R8; + value.dblVal = dblNewLongitude; + fHasValidData = TRUE; + } + + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_LONGITUDE_DEGREES, &value); + + PropVariantClear(&value); + } + + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + + FLOAT fltMax = GetRangeMaximumValue( + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum); + + FLOAT fltMin = GetRangeMinimumValue( + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum); + + if ((dblNewErrorRadius > fltMax) || (dblNewErrorRadius < fltMin)) + { + value.vt = VT_NULL; + } + else + { + value.vt = VT_R8; + value.dblVal = dblNewErrorRadius; + fHasValidData = TRUE; + } + + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ERROR_RADIUS_METERS, &value); + + PropVariantClear(&value); + } + + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + + FLOAT fltMax = GetRangeMaximumValue( + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum); + + FLOAT fltMin = GetRangeMinimumValue( + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum); + + if ((dblNewAltitude > fltMax) || (dblNewAltitude < fltMin)) + { + value.vt = VT_NULL; + } + else + { + value.vt = VT_R8; + value.dblVal = dblNewAltitude; + fHasValidData = TRUE; + } + + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS, &value); + + PropVariantClear(&value); + } + + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + + FLOAT fltMax = GetRangeMaximumValue( + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum); + + FLOAT fltMin = GetRangeMinimumValue( + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum); + + if ((dblNewAltitudeError > fltMax) || (dblNewAltitudeError < fltMin)) + { + value.vt = VT_NULL; + } + else + { + value.vt = VT_R8; + value.dblVal = dblNewAltitudeError; + fHasValidData = TRUE; + } + + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS, &value); + + PropVariantClear(&value); + } + + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + + FLOAT fltMax = GetRangeMaximumValue( + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum); + + FLOAT fltMin = GetRangeMinimumValue( + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum); + + if ((dblNewSpeed > fltMax) || (dblNewSpeed < fltMin)) + { + value.vt = VT_NULL; + } + else + { + value.vt = VT_R8; + value.dblVal = dblNewSpeed; + fHasValidData = TRUE; + } + + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_SPEED_KNOTS, &value); + + PropVariantClear(&value); + } + + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + + FLOAT fltMax = GetRangeMaximumValue( + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum, + FALSE, + m_fltDefaultRangeMaximum); + + FLOAT fltMin = GetRangeMinimumValue( + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum, + FALSE, + m_fltDefaultRangeMinimum); + + if ((dblNewHeading > fltMax) || (dblNewHeading < fltMin)) + { + value.vt = VT_NULL; + } + else + { + value.vt = VT_R8; + value.dblVal = dblNewHeading; + fHasValidData = TRUE; + } + + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES, &value); + + PropVariantClear(&value); + } + + if (SUCCEEDED(hr)) + { + m_fInitialDataReceived = TRUE; + } + + if (TRUE == fHasValidData) + { + // NOTE: the sensor state is set to ready so that the values above are visible to clients + + SetState(SENSOR_STATE_READY, &fStateChanged); + } + else + { + // NOTE: in the event the Geolocation is a device that will actively continue to find location, + // like a GPS that has lost the satelite fix, the sensor state must be set to + // SENSOR_STATE_INITIALIZING and remain in that state until the device once again has a valid + // location, in which case it can be set back to SENSOR_STATE_READY + + SetState(SENSOR_STATE_INITIALIZING, &fStateChanged); + + // Any sensor that has given up finding location for now will set this state to SENSOR_STATE_NO_DATA + // and remain in that state until the sensor once again has valid data + + //SetState(SENSOR_STATE_NO_DATA, &fStateChanged); + } + + // Call RaiseDataEvent to send a data notification to all clients. + // Do not call RaiseDataEvent from this function as this function + // is called as a result of a client data request IO. + // Call RaiseDataEvent when the location device updates data. + //RaiseDataEvent(); + } + } + else + { + Trace(TRACE_LEVEL_WARNING, "%!FUNC! Not updating data as device is not active"); + } + + return hr; +} + +#if USE_GEOLOCATION_SPECIFIC_UNIQUE_ID +// Sets the persistent unique ID property +// this overrides the CSensor base class version +HRESULT CGeolocationSensor::SetUniqueID(_In_ IWDFDevice* pWdfDevice) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pWdfDevice); + + hr = m_spSensorPropertyValues->SetGuidValue(SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID, SENSOR_ID_FAKEGPS); + + return hr; +} +#endif + +HRESULT CGeolocationSensor::SetState(_In_ SensorState newState, _Out_ bool* pfStateChanged) +{ + Trace(TRACE_LEVEL_VERBOSE, "%!FUNC! Entry"); + + HRESULT hr = m_pSensorManager->SetState(this, newState, pfStateChanged); + + if (SUCCEEDED(hr)) + { + if (true == *pfStateChanged) + { + if (SENSOR_STATE_READY != newState) + { + PROPVARIANT value; + PropVariantInit(&value); + + value.vt = VT_EMPTY; + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_LATITUDE_DEGREES, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_LONGITUDE_DEGREES, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ERROR_RADIUS_METERS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_METERS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_ALTITUDE_ELLIPSOID_ERROR_METERS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_SPEED_KNOTS, &value); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_TRUE_HEADING_DEGREES, &value); + } + + PropVariantClear(&value); + } + } + } + + return hr; +} diff --git a/src/FakeGPS.Driver/GeolocationSensor.h b/src/FakeGPS.Driver/GeolocationSensor.h new file mode 100644 index 0000000..d4d9ddc --- /dev/null +++ b/src/FakeGPS.Driver/GeolocationSensor.h @@ -0,0 +1,87 @@ +/*++ + +Module: + + GeolocationSensor.h + +Description: + + Defines the CGeolocationSensor container class + +--*/ + +#pragma once + +class CSensorManager; //forward declaration + +class CGeolocationSensor : public CSensor +{ +public: + CGeolocationSensor(); + ~CGeolocationSensor(); + + HRESULT Initialize( + _In_ SensorType sensType, + _In_ DWORD sensNum, + _In_ LPWSTR pwszManufacturer, + _In_ LPWSTR pwszProduct, + _In_ LPWSTR pwszSerialNumber, + _In_ LPWSTR sensorID, + _In_ IWDFDevice* pWdfDevice, + _In_ CSensorManager* pSensorManager); + + HRESULT GetPropertyValuesForGeolocationObject( + LPCWSTR wszObjectID, + IPortableDeviceKeyCollection* pKeys, + IPortableDeviceValues* pValues); + + HRESULT UpdateGeolocationPropertyValues(); + HRESULT UpdateGeolocationDataFieldValues(); + +//TODO define this == 1 if the sensor must have a known unique ID for all instances on any system +#define USE_GEOLOCATION_SPECIFIC_UNIQUE_ID 1 + +#if USE_GEOLOCATION_SPECIFIC_UNIQUE_ID + virtual HRESULT SetUniqueID(_In_ IWDFDevice* pWdfDevice); +#endif + + virtual HRESULT SetState(_In_ SensorState newState, _Out_ bool* pfStateChanged); + +private: + + HRESULT InitializeGeolocation(_In_ IWDFDevice* pWdfDevice); + HRESULT AddGeolocationPropertyKeys(); + HRESULT AddGeolocationSettablePropertyKeys(); + HRESULT AddGeolocationDataFieldKeys(); + HRESULT SetGeolocationDefaultValues(_In_ IWDFDevice* pWdfDevice); + + DOUBLE m_Latitude; + DOUBLE m_Longitude; + + // TODO: m_spWdfDevice2? + IWDFDevice* m_pWdfDevice; + +}; + + +const unsigned short SENSOR_GEOLOCATION_NAME[] = L"FakeGPS"; +const unsigned short SENSOR_GEOLOCATION_DESCRIPTION[] = L"FakeGPS Sensor"; +const char SENSOR_GEOLOCATION_TRACE_NAME[] = "FakeGPS"; + +// Default Values +const FLOAT DEFAULT_GEOLOCATION_CHANGE_SENSITIVITY = 5.0F; +const FLOAT DEFAULT_GEOLOCATION_MAXIMUM = FLT_MAX; +const FLOAT DEFAULT_GEOLOCATION_MINIMUM = -FLT_MAX; +const FLOAT DEFAULT_GEOLOCATION_ACCURACY = FLT_MAX; +const FLOAT DEFAULT_GEOLOCATION_RESOLUTION = FLT_MAX; + +const FLOAT MIN_GEOLOCATION_SENSITIVITY = -FLT_MAX; +const FLOAT MIN_GEOLOCATION_MAXIMUM = -FLT_MAX; +const FLOAT MAX_GEOLOCATION_MINIMUM = FLT_MAX; +const FLOAT MIN_GEOLOCATION_ACCURACY = -FLT_MAX; +const FLOAT MIN_GEOLOCATION_RESOLUTION = -FLT_MAX; + +const ULONG GEOLOCATION_MIN_REPORT_INTERVAL = 250; +const ULONG DEFAULT_GEOLOCATION_CURRENT_REPORT_INTERVAL = 250; + +const USHORT USAGE_GEOLOCATION_SENSOR = (0x53); \ No newline at end of file diff --git a/src/FakeGPS.Driver/Internal.cpp b/src/FakeGPS.Driver/Internal.cpp new file mode 100644 index 0000000..7da154a --- /dev/null +++ b/src/FakeGPS.Driver/Internal.cpp @@ -0,0 +1,3 @@ +// 2016 OK + +#include "Internal.h" \ No newline at end of file diff --git a/src/FakeGPS.Driver/Queue.cpp b/src/FakeGPS.Driver/Queue.cpp new file mode 100644 index 0000000..d0e56ea --- /dev/null +++ b/src/FakeGPS.Driver/Queue.cpp @@ -0,0 +1,201 @@ +// 2016 OK + +/*++ + +Module: + + Queue.cpp + +Description: + + This file contains the queue entry points and callbacks. + +--*/ + +#include "Internal.h" +#include "FakeGPS.h" +#include "Queue.h" +#include "Queue.tmh" + +// Initialize member variables +CMyQueue::CMyQueue() : + m_pParentDevice(NULL) +{ +} + +/*++ + +CMyQueue::~CMyQueue + + Object destructor function + +--*/ +CMyQueue::~CMyQueue() +{ + // Release the reference that was incremented in CreateInstance() + SAFE_RELEASE(m_pParentDevice); +} + +/*++ + +CMyQueue::CreateInstance + + This function supports the COM factory creation system + +Parameters: + parentDevice - A pointer to the CWSSDevice object + ppUkwn - pointer to a pointer to the queue to be returned + ppQueue - The queue that is created + +Return Values: + S_OK: The queue was created successfully + +--*/ +HRESULT CMyQueue::CreateInstance( + _In_ IWDFDevice* pWdfDevice, + CFakeGPS* pFakeGPS, + IWDFIoQueue** ppQueue) +{ + HRESULT hr = (NULL != pFakeGPS) ? S_OK : E_UNEXPECTED; + + if (NULL == pFakeGPS) + { + return E_INVALIDARG; + } + else if (nullptr == ppQueue) + { + return E_INVALIDARG; + } + + *ppQueue = nullptr; + + CComObject* pMyQueue = NULL; + if (SUCCEEDED(hr)) + { + hr = CComObject::CreateInstance(&pMyQueue); + } + + if (SUCCEEDED(hr) && (nullptr != pMyQueue)) + { + // AddRef the object + pMyQueue->AddRef(); + + // Store the parent device object + pMyQueue->m_pParentDevice = pFakeGPS; + + // Increment the reference for the lifetime of the CMyQueue object. + pMyQueue->m_pParentDevice->AddRef(); + + CComPtr spIUnknown; + hr = pMyQueue->QueryInterface(IID_IUnknown, (void**) &spIUnknown); + + if (SUCCEEDED(hr)) + { + // Create the framework queue + hr = pWdfDevice->CreateIoQueue(spIUnknown, + TRUE, // DefaultQueue + WdfIoQueueDispatchParallel, // Parallel queue handling + FALSE, // Non PowerManaged so I/O is self managed and + // requests can be received while in Dx + TRUE, // AllowZeroLengthRequests + ppQueue + ); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "%!FUNC!: Could not create default I/O queue, %!hresult!", hr); + } + } + + // Release the pMyQueue pointer when done. Note: UMDF holds a reference to it above + SAFE_RELEASE(pMyQueue); + } + + return hr; +} + +/*++ + +CMyQueue::OnDeviceIoControl + + This method is called when an IOCTL is sent to the device + +Parameters: + pQueue - pointer to an IO queue + pRequest - pointer to an IO request + ControlCode - The IOCTL to process + InputBufferSizeInBytes - the size of the input buffer + OutputBufferSizeInBytes - the size of the output buffer + +--*/ +STDMETHODIMP_(void) CMyQueue::OnDeviceIoControl( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes) +{ + UNREFERENCED_PARAMETER(pQueue); + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + + //Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + DWORD dwWritten = 0; + + if (IOCTL_GPS_RADIO_MANAGEMENT_GET_RADIO_STATE == ControlCode || + IOCTL_GPS_RADIO_MANAGEMENT_GET_PREVIOUS_RADIO_STATE == ControlCode || + IOCTL_GPS_RADIO_MANAGEMENT_SET_RADIO_STATE == ControlCode || + IOCTL_GPS_RADIO_MANAGEMENT_SET_PREVIOUS_RADIO_STATE == ControlCode) + { +#if (NTDDI_VERSION >= NTDDI_WIN8) + + if (FAILED(EnterProcessing(PROCESSING_IQUEUECALLBACKDEVICEIOCONTROL))) + { + // Unsupported request + pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0); + } + else + { + HRESULT hr = m_pParentDevice->ProcessIoControlRadioManagement(pRequest, ControlCode); + pRequest->CompleteWithInformation(hr, sizeof(DEVICE_RADIO_STATE)); + } // processing in progress + + ExitProcessing(PROCESSING_IQUEUECALLBACKDEVICEIOCONTROL); +#endif + } + else if (IS_WPD_IOCTL(ControlCode)) + { + if (FAILED(EnterProcessing(PROCESSING_IQUEUECALLBACKDEVICEIOCONTROL))) + { + // Unsupported request + pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0); + } + else + { + m_pParentDevice->ProcessIoControl(pQueue, + pRequest, + ControlCode, + InputBufferSizeInBytes, + OutputBufferSizeInBytes, + &dwWritten); + } // processing in progress + + ExitProcessing(PROCESSING_IQUEUECALLBACKDEVICEIOCONTROL); + } + else + { + // Unsupported request + pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0); + } +} + +inline HRESULT CMyQueue::EnterProcessing(DWORD64 dwControlFlag) +{ + return m_pParentDevice->EnterProcessing(dwControlFlag); +} + +inline void CMyQueue::ExitProcessing(DWORD64 dwControlFlag) +{ + m_pParentDevice->ExitProcessing(dwControlFlag); +} \ No newline at end of file diff --git a/src/FakeGPS.Driver/Queue.h b/src/FakeGPS.Driver/Queue.h new file mode 100644 index 0000000..ea0e618 --- /dev/null +++ b/src/FakeGPS.Driver/Queue.h @@ -0,0 +1,57 @@ +// 2016 OK + +/*++ + +Module: + + Queue.h + +Description: + + This module contains the type definitions for the sensors service driver queue callback class. + +--*/ + +#pragma once + +class ATL_NO_VTABLE CMyQueue : + public CComObjectRootEx, + public IQueueCallbackDeviceIoControl +{ + +public: + + virtual ~CMyQueue(); + + DECLARE_NOT_AGGREGATABLE(CMyQueue) + + BEGIN_COM_MAP(CMyQueue) + COM_INTERFACE_ENTRY(IQueueCallbackDeviceIoControl) + END_COM_MAP() + + static HRESULT CreateInstance( + _In_ IWDFDevice* pWdfDevice, + CFakeGPS* pFakeGPS, + IWDFIoQueue** ppQueue); + +protected: + + CMyQueue(); + + // COM Interface methods +public: + + // IQueueCallbackDeviceIoControl + STDMETHOD_(void, OnDeviceIoControl)( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes); + +private: + inline HRESULT EnterProcessing(DWORD64 dwControlFlag); + inline void ExitProcessing(DWORD64 dwControlFlag); + + CFakeGPS* m_pParentDevice; // Parent device object +}; \ No newline at end of file diff --git a/src/FakeGPS.Driver/Resource.h b/src/FakeGPS.Driver/Resource.h new file mode 100644 index 0000000..ec03cea --- /dev/null +++ b/src/FakeGPS.Driver/Resource.h @@ -0,0 +1,1619 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SensorsServiceDriver.rc +// +#define SW_HIDE 0 +#define HIDE_WINDOW 0 +#define WM_NULL 0x0000 +#define WA_INACTIVE 0 +#define HTNOWHERE 0 +#define SMTO_NORMAL 0x0000 +#define ICON_SMALL 0 +#define SIZE_RESTORED 0 +#define BN_CLICKED 0 +#define BST_UNCHECKED 0x0000 +#define HDS_HORZ 0x0000 +#define TBSTYLE_BUTTON 0x0000 +#define TBS_HORZ 0x0000 +#define TBS_BOTTOM 0x0000 +#define TBS_RIGHT 0x0000 +#define LVS_ICON 0x0000 +#define LVS_ALIGNTOP 0x0000 +#define TCS_TABS 0x0000 +#define TCS_SINGLELINE 0x0000 +#define TCS_RIGHTJUSTIFY 0x0000 +#define DTS_SHORTDATEFORMAT 0x0000 +#define PGS_VERT 0x00000000 +#define LANG_NEUTRAL 0x00 +#define SUBLANG_NEUTRAL 0x00 +#define SORT_DEFAULT 0x0 +#define SORT_JAPANESE_XJIS 0x0 +#define SORT_CHINESE_BIG5 0x0 +#define SORT_CHINESE_PRCP 0x0 +#define SORT_KOREAN_KSC 0x0 +#define SORT_HUNGARIAN_DEFAULT 0x0 +#define SORT_GEORGIAN_TRADITIONAL 0x0 +#define _USE_DECLSPECS_FOR_SAL 0 +#define _USE_ATTRIBUTES_FOR_SAL 0 +#define __drv_typeConst 0 +#define VER_PRODUCTMINORVERSION 0 +#define VER_DEBUG 0 +#define VER_PRERELEASE 0 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#define MINIMUM_RESERVED_MANIFEST_RESOURCE_ID 1 +#define SW_SHOWNORMAL 1 +#define SW_NORMAL 1 +#define SHOW_OPENWINDOW 1 +#define SW_PARENTCLOSING 1 +#define VK_LBUTTON 0x01 +#define WM_CREATE 0x0001 +#define WA_ACTIVE 1 +#define PWR_OK 1 +#define PWR_SUSPENDREQUEST 1 +#define NFR_ANSI 1 +#define UIS_SET 1 +#define UISF_HIDEFOCUS 0x1 +#define XBUTTON1 0x0001 +#define WMSZ_LEFT 1 +#define HTCLIENT 1 +#define SMTO_BLOCK 0x0001 +#define MA_ACTIVATE 1 +#define ICON_BIG 1 +#define SIZE_MINIMIZED 1 +#define MK_LBUTTON 0x0001 +#define TME_HOVER 0x00000001 +#define CS_VREDRAW 0x0001 +#define CF_TEXT 1 +#define SCF_ISSECURE 0x00000001 +#define IDOK 1 +#define BN_PAINT 1 +#define BST_CHECKED 0x0001 +#define TBSTYLE_SEP 0x0001 +#define TTS_ALWAYSTIP 0x01 +#define TBS_AUTOTICKS 0x0001 +#define UDS_WRAP 0x0001 +#define PBS_SMOOTH 0x01 +#define LWS_TRANSPARENT 0x0001 +#define LVS_REPORT 0x0001 +#define TVS_HASBUTTONS 0x0001 +#define TVS_EX_NOSINGLECOLLAPSE 0x0001 +#define TCS_SCROLLOPPOSITE 0x0001 +#define ACS_CENTER 0x0001 +#define MCS_DAYSTATE 0x0001 +#define DTS_UPDOWN 0x0001 +#define PGS_HORZ 0x00000001 +#define NFS_EDIT 0x0001 +#define BCSIF_GLYPH 0x0001 +#define BCSS_NOSPLIT 0x0001 +#define LANG_ARABIC 0x01 +#define SUBLANG_DEFAULT 0x01 +#define SUBLANG_AFRIKAANS_SOUTH_AFRICA 0x01 +#define SUBLANG_ALBANIAN_ALBANIA 0x01 +#define SUBLANG_ALSATIAN_FRANCE 0x01 +#define SUBLANG_AMHARIC_ETHIOPIA 0x01 +#define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 +#define SUBLANG_ARMENIAN_ARMENIA 0x01 +#define SUBLANG_ASSAMESE_INDIA 0x01 +#define SUBLANG_AZERI_LATIN 0x01 +#define SUBLANG_AZERBAIJANI_AZERBAIJAN_LATIN 0x01 +#define SUBLANG_BANGLA_INDIA 0x01 +#define SUBLANG_BASHKIR_RUSSIA 0x01 +#define SUBLANG_BASQUE_BASQUE 0x01 +#define SUBLANG_BELARUSIAN_BELARUS 0x01 +#define SUBLANG_BENGALI_INDIA 0x01 +#define SUBLANG_BRETON_FRANCE 0x01 +#define SUBLANG_BULGARIAN_BULGARIA 0x01 +#define SUBLANG_CATALAN_CATALAN 0x01 +#define SUBLANG_CENTRAL_KURDISH_IRAQ 0x01 +#define SUBLANG_CHEROKEE_CHEROKEE 0x01 +#define SUBLANG_CHINESE_TRADITIONAL 0x01 +#define SUBLANG_CORSICAN_FRANCE 0x01 +#define SUBLANG_CZECH_CZECH_REPUBLIC 0x01 +#define SUBLANG_CROATIAN_CROATIA 0x01 +#define SUBLANG_DANISH_DENMARK 0x01 +#define SUBLANG_DARI_AFGHANISTAN 0x01 +#define SUBLANG_DIVEHI_MALDIVES 0x01 +#define SUBLANG_DUTCH 0x01 +#define SUBLANG_ENGLISH_US 0x01 +#define SUBLANG_ESTONIAN_ESTONIA 0x01 +#define SUBLANG_FAEROESE_FAROE_ISLANDS 0x01 +#define SUBLANG_FILIPINO_PHILIPPINES 0x01 +#define SUBLANG_FINNISH_FINLAND 0x01 +#define SUBLANG_FRENCH 0x01 +#define SUBLANG_FRISIAN_NETHERLANDS 0x01 +#define SUBLANG_GALICIAN_GALICIAN 0x01 +#define SUBLANG_GEORGIAN_GEORGIA 0x01 +#define SUBLANG_GERMAN 0x01 +#define SUBLANG_GREEK_GREECE 0x01 +#define SUBLANG_GREENLANDIC_GREENLAND 0x01 +#define SUBLANG_GUJARATI_INDIA 0x01 +#define SUBLANG_HAUSA_NIGERIA_LATIN 0x01 +#define SUBLANG_HAWAIIAN_US 0x01 +#define SUBLANG_HEBREW_ISRAEL 0x01 +#define SUBLANG_HINDI_INDIA 0x01 +#define SUBLANG_HUNGARIAN_HUNGARY 0x01 +#define SUBLANG_ICELANDIC_ICELAND 0x01 +#define SUBLANG_IGBO_NIGERIA 0x01 +#define SUBLANG_INDONESIAN_INDONESIA 0x01 +#define SUBLANG_INUKTITUT_CANADA 0x01 +#define SUBLANG_ITALIAN 0x01 +#define SUBLANG_JAPANESE_JAPAN 0x01 +#define SUBLANG_KANNADA_INDIA 0x01 +#define SUBLANG_KAZAK_KAZAKHSTAN 0x01 +#define SUBLANG_KHMER_CAMBODIA 0x01 +#define SUBLANG_KICHE_GUATEMALA 0x01 +#define SUBLANG_KINYARWANDA_RWANDA 0x01 +#define SUBLANG_KONKANI_INDIA 0x01 +#define SUBLANG_KOREAN 0x01 +#define SUBLANG_KYRGYZ_KYRGYZSTAN 0x01 +#define SUBLANG_LAO_LAO 0x01 +#define SUBLANG_LATVIAN_LATVIA 0x01 +#define SUBLANG_LITHUANIAN 0x01 +#define SUBLANG_LUXEMBOURGISH_LUXEMBOURG 0x01 +#define SUBLANG_MACEDONIAN_MACEDONIA 0x01 +#define SUBLANG_MALAY_MALAYSIA 0x01 +#define SUBLANG_MALAYALAM_INDIA 0x01 +#define SUBLANG_MALTESE_MALTA 0x01 +#define SUBLANG_MAORI_NEW_ZEALAND 0x01 +#define SUBLANG_MAPUDUNGUN_CHILE 0x01 +#define SUBLANG_MARATHI_INDIA 0x01 +#define SUBLANG_MOHAWK_MOHAWK 0x01 +#define SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA 0x01 +#define SUBLANG_NEPALI_NEPAL 0x01 +#define SUBLANG_NORWEGIAN_BOKMAL 0x01 +#define SUBLANG_OCCITAN_FRANCE 0x01 +#define SUBLANG_ODIA_INDIA 0x01 +#define SUBLANG_ORIYA_INDIA 0x01 +#define SUBLANG_PASHTO_AFGHANISTAN 0x01 +#define SUBLANG_PERSIAN_IRAN 0x01 +#define SUBLANG_POLISH_POLAND 0x01 +#define SUBLANG_PORTUGUESE_BRAZILIAN 0x01 +#define SUBLANG_PUNJABI_INDIA 0x01 +#define SUBLANG_QUECHUA_BOLIVIA 0x01 +#define SUBLANG_ROMANIAN_ROMANIA 0x01 +#define SUBLANG_ROMANSH_SWITZERLAND 0x01 +#define SUBLANG_RUSSIAN_RUSSIA 0x01 +#define SUBLANG_SAKHA_RUSSIA 0x01 +#define SUBLANG_SAMI_NORTHERN_NORWAY 0x01 +#define SUBLANG_SANSKRIT_INDIA 0x01 +#define SUBLANG_SCOTTISH_GAELIC 0x01 +#define SUBLANG_SERBIAN_CROATIA 0x01 +#define SUBLANG_SINDHI_INDIA 0x01 +#define SUBLANG_SINHALESE_SRI_LANKA 0x01 +#define SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA 0x01 +#define SUBLANG_SLOVAK_SLOVAKIA 0x01 +#define SUBLANG_SLOVENIAN_SLOVENIA 0x01 +#define SUBLANG_SPANISH 0x01 +#define SUBLANG_SWAHILI_KENYA 0x01 +#define SUBLANG_SWEDISH 0x01 +#define SUBLANG_SYRIAC_SYRIA 0x01 +#define SUBLANG_TAJIK_TAJIKISTAN 0x01 +#define SUBLANG_TAMIL_INDIA 0x01 +#define SUBLANG_TATAR_RUSSIA 0x01 +#define SUBLANG_TELUGU_INDIA 0x01 +#define SUBLANG_THAI_THAILAND 0x01 +#define SUBLANG_TIBETAN_PRC 0x01 +#define SUBLANG_TIGRINYA_ETHIOPIA 0x01 +#define SUBLANG_TSWANA_SOUTH_AFRICA 0x01 +#define SUBLANG_TURKISH_TURKEY 0x01 +#define SUBLANG_TURKMEN_TURKMENISTAN 0x01 +#define SUBLANG_UIGHUR_PRC 0x01 +#define SUBLANG_UKRAINIAN_UKRAINE 0x01 +#define SUBLANG_UPPER_SORBIAN_GERMANY 0x01 +#define SUBLANG_URDU_PAKISTAN 0x01 +#define SUBLANG_UZBEK_LATIN 0x01 +#define SUBLANG_VIETNAMESE_VIETNAM 0x01 +#define SUBLANG_WELSH_UNITED_KINGDOM 0x01 +#define SUBLANG_WOLOF_SENEGAL 0x01 +#define SUBLANG_XHOSA_SOUTH_AFRICA 0x01 +#define SUBLANG_YAKUT_RUSSIA 0x01 +#define SUBLANG_YI_PRC 0x01 +#define SUBLANG_YORUBA_NIGERIA 0x01 +#define SUBLANG_ZULU_SOUTH_AFRICA 0x01 +#define SORT_INVARIANT_MATH 0x1 +#define SORT_JAPANESE_UNICODE 0x1 +#define SORT_CHINESE_UNICODE 0x1 +#define SORT_KOREAN_UNICODE 0x1 +#define SORT_GERMAN_PHONE_BOOK 0x1 +#define SORT_HUNGARIAN_TECHNICAL 0x1 +#define SORT_GEORGIAN_MODERN 0x1 +#define __drv_typeCond 1 +#define VS_VERSION_INFO 1 +#define VFFF_ISSHAREDFILE 0x0001 +#define VFF_CURNEDEST 0x0001 +#define VIFF_FORCEINSTALL 0x0001 +#define WINAPI_FAMILY_PC_APP 2 +#define ISOLATIONAWARE_MANIFEST_RESOURCE_ID 2 +#define SW_SHOWMINIMIZED 2 +#define SHOW_ICONWINDOW 2 +#define SW_OTHERZOOM 2 +#define VK_RBUTTON 0x02 +#define WM_DESTROY 0x0002 +#define WA_CLICKACTIVE 2 +#define PWR_SUSPENDRESUME 2 +#define NFR_UNICODE 2 +#define UIS_CLEAR 2 +#define UISF_HIDEACCEL 0x2 +#define XBUTTON2 0x0002 +#define WMSZ_RIGHT 2 +#define HTCAPTION 2 +#define SMTO_ABORTIFHUNG 0x0002 +#define MA_ACTIVATEANDEAT 2 +#define ICON_SMALL2 2 +#define SIZE_MAXIMIZED 2 +#define MK_RBUTTON 0x0002 +#define TME_LEAVE 0x00000002 +#define CS_HREDRAW 0x0002 +#define CF_BITMAP 2 +#define IDCANCEL 2 +#define BN_HILITE 2 +#define BST_INDETERMINATE 0x0002 +#define HDS_BUTTONS 0x0002 +#define TBSTYLE_CHECK 0x0002 +#define TTS_NOPREFIX 0x02 +#define TBS_VERT 0x0002 +#define UDS_SETBUDDYINT 0x0002 +#define LWS_IGNORERETURN 0x0002 +#define LVS_SMALLICON 0x0002 +#define TVS_HASLINES 0x0002 +#define TVS_EX_MULTISELECT 0x0002 +#define TCS_BOTTOM 0x0002 +#define TCS_RIGHT 0x0002 +#define ACS_TRANSPARENT 0x0002 +#define MCS_MULTISELECT 0x0002 +#define DTS_SHOWNONE 0x0002 +#define PGS_AUTOSCROLL 0x00000002 +#define NFS_STATIC 0x0002 +#define BCSIF_IMAGE 0x0002 +#define BCSS_STRETCH 0x0002 +#define LANG_BULGARIAN 0x02 +#define SUBLANG_SYS_DEFAULT 0x02 +#define SUBLANG_ARABIC_IRAQ 0x02 +#define SUBLANG_AZERI_CYRILLIC 0x02 +#define SUBLANG_AZERBAIJANI_AZERBAIJAN_CYRILLIC 0x02 +#define SUBLANG_BANGLA_BANGLADESH 0x02 +#define SUBLANG_BENGALI_BANGLADESH 0x02 +#define SUBLANG_CHINESE_SIMPLIFIED 0x02 +#define SUBLANG_DUTCH_BELGIAN 0x02 +#define SUBLANG_ENGLISH_UK 0x02 +#define SUBLANG_FRENCH_BELGIAN 0x02 +#define SUBLANG_FULAH_SENEGAL 0x02 +#define SUBLANG_GERMAN_SWISS 0x02 +#define SUBLANG_INUKTITUT_CANADA_LATIN 0x02 +#define SUBLANG_IRISH_IRELAND 0x02 +#define SUBLANG_ITALIAN_SWISS 0x02 +#define SUBLANG_KASHMIRI_SASIA 0x02 +#define SUBLANG_KASHMIRI_INDIA 0x02 +#define SUBLANG_LOWER_SORBIAN_GERMANY 0x02 +#define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02 +#define SUBLANG_MONGOLIAN_PRC 0x02 +#define SUBLANG_NEPALI_INDIA 0x02 +#define SUBLANG_NORWEGIAN_NYNORSK 0x02 +#define SUBLANG_PORTUGUESE 0x02 +#define SUBLANG_PULAR_SENEGAL 0x02 +#define SUBLANG_PUNJABI_PAKISTAN 0x02 +#define SUBLANG_QUECHUA_ECUADOR 0x02 +#define SUBLANG_SAMI_NORTHERN_SWEDEN 0x02 +#define SUBLANG_SERBIAN_LATIN 0x02 +#define SUBLANG_SINDHI_PAKISTAN 0x02 +#define SUBLANG_SINDHI_AFGHANISTAN 0x02 +#define SUBLANG_SPANISH_MEXICAN 0x02 +#define SUBLANG_SWEDISH_FINLAND 0x02 +#define SUBLANG_TAMAZIGHT_ALGERIA_LATIN 0x02 +#define SUBLANG_TAMIL_SRI_LANKA 0x02 +#define SUBLANG_TIGRIGNA_ERITREA 0x02 +#define SUBLANG_TIGRINYA_ERITREA 0x02 +#define SUBLANG_TSWANA_BOTSWANA 0x02 +#define SUBLANG_URDU_INDIA 0x02 +#define SUBLANG_UZBEK_CYRILLIC 0x02 +#define SUBLANG_VALENCIAN_VALENCIA 0x02 +#define SORT_CHINESE_PRC 0x2 +#define __drv_typeBitset 2 +#define VFF_FILEINUSE 0x0002 +#define VIFF_DONTDELETEOLD 0x0002 +#define WINAPI_FAMILY_PHONE_APP 3 +#define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID 3 +#define SW_SHOWMAXIMIZED 3 +#define SW_MAXIMIZE 3 +#define SHOW_FULLSCREEN 3 +#define SW_PARENTOPENING 3 +#define VK_CANCEL 0x03 +#define WM_MOVE 0x0003 +#define PWR_CRITICALRESUME 3 +#define NF_QUERY 3 +#define UIS_INITIALIZE 3 +#define WMSZ_TOP 3 +#define HTSYSMENU 3 +#define MA_NOACTIVATE 3 +#define SIZE_MAXSHOW 3 +#define CF_METAFILEPICT 3 +#define IDABORT 3 +#define BN_UNHILITE 3 +#define LVS_LIST 0x0003 +#define LVS_TYPEMASK 0x0003 +#define LANG_CATALAN 0x03 +#define LANG_VALENCIAN 0x03 +#define SUBLANG_CUSTOM_DEFAULT 0x03 +#define SUBLANG_ARABIC_EGYPT 0x03 +#define SUBLANG_CHINESE_HONGKONG 0x03 +#define SUBLANG_ENGLISH_AUS 0x03 +#define SUBLANG_FRENCH_CANADIAN 0x03 +#define SUBLANG_GERMAN_AUSTRIAN 0x03 +#define SUBLANG_QUECHUA_PERU 0x03 +#define SUBLANG_SAMI_NORTHERN_FINLAND 0x03 +#define SUBLANG_SERBIAN_CYRILLIC 0x03 +#define SUBLANG_SPANISH_MODERN 0x03 +#define SORT_CHINESE_BOPOMOFO 0x3 +#define __drv_typeExpr 3 +#define VER_PRODUCTMINORVERSIONWIN81 3 +#define WINAPI_FAMILY_SYSTEM 4 +#define SW_SHOWNOACTIVATE 4 +#define SHOW_OPENNOACTIVATE 4 +#define SW_OTHERUNZOOM 4 +#define VK_MBUTTON 0x04 +#define NF_REQUERY 4 +#define UISF_ACTIVE 0x4 +#define WMSZ_TOPLEFT 4 +#define HTGROWBOX 4 +#define MA_NOACTIVATEANDEAT 4 +#define SIZE_MAXHIDE 4 +#define MK_SHIFT 0x0004 +#define CF_SYLK 4 +#define IDRETRY 4 +#define BN_DISABLE 4 +#define BST_PUSHED 0x0004 +#define HDS_HOTTRACK 0x0004 +#define TBSTYLE_GROUP 0x0004 +#define TBS_TOP 0x0004 +#define TBS_LEFT 0x0004 +#define UDS_ALIGNRIGHT 0x0004 +#define PBS_VERTICAL 0x04 +#define LWS_NOPREFIX 0x0004 +#define LVS_SINGLESEL 0x0004 +#define TVS_LINESATROOT 0x0004 +#define TVS_EX_DOUBLEBUFFER 0x0004 +#define TCS_MULTISELECT 0x0004 +#define ACS_AUTOPLAY 0x0004 +#define MCS_WEEKNUMBERS 0x0004 +#define DTS_LONGDATEFORMAT 0x0004 +#define PGS_DRAGNDROP 0x00000004 +#define NFS_LISTCOMBO 0x0004 +#define BCSIF_STYLE 0x0004 +#define BCSS_ALIGNLEFT 0x0004 +#define LANG_CHINESE 0x04 +#define LANG_CHINESE_SIMPLIFIED 0x04 +#define SUBLANG_CUSTOM_UNSPECIFIED 0x04 +#define SUBLANG_ARABIC_LIBYA 0x04 +#define SUBLANG_CHINESE_SINGAPORE 0x04 +#define SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN 0x04 +#define SUBLANG_ENGLISH_CAN 0x04 +#define SUBLANG_FRENCH_SWISS 0x04 +#define SUBLANG_GERMAN_LUXEMBOURG 0x04 +#define SUBLANG_SAMI_LULE_NORWAY 0x04 +#define SUBLANG_SPANISH_GUATEMALA 0x04 +#define SUBLANG_TAMAZIGHT_MOROCCO_TIFINAGH 0x04 +#define SORT_JAPANESE_RADICALSTROKE 0x4 +#define SORT_CHINESE_RADICALSTROKE 0x4 +#define VFF_BUFFTOOSMALL 0x0004 +#define WINAPI_FAMILY_SERVER 5 +#define SW_SHOW 5 +#define VK_XBUTTON1 0x05 +#define WM_SIZE 0x0005 +#define WMSZ_TOPRIGHT 5 +#define HTMENU 5 +#define CF_DIF 5 +#define IDIGNORE 5 +#define BN_DOUBLECLICKED 5 +#define LANG_CZECH 0x05 +#define SUBLANG_UI_CUSTOM_DEFAULT 0x05 +#define SUBLANG_ARABIC_ALGERIA 0x05 +#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN 0x05 +#define SUBLANG_CHINESE_MACAU 0x05 +#define SUBLANG_ENGLISH_NZ 0x05 +#define SUBLANG_FRENCH_LUXEMBOURG 0x05 +#define SUBLANG_GERMAN_LIECHTENSTEIN 0x05 +#define SUBLANG_SAMI_LULE_SWEDEN 0x05 +#define SUBLANG_SPANISH_COSTA_RICA 0x05 +#define SW_MINIMIZE 6 +#define VK_XBUTTON2 0x06 +#define WM_ACTIVATE 0x0006 +#define WMSZ_BOTTOM 6 +#define HTHSCROLL 6 +#define CF_TIFF 6 +#define IDYES 6 +#define BN_SETFOCUS 6 +#define LANG_DANISH 0x06 +#define SUBLANG_ARABIC_MOROCCO 0x06 +#define SUBLANG_ENGLISH_EIRE 0x06 +#define SUBLANG_FRENCH_MONACO 0x06 +#define SUBLANG_SAMI_SOUTHERN_NORWAY 0x06 +#define SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN 0x06 +#define SUBLANG_SPANISH_PANAMA 0x06 +#define VER_PRODUCTMAJORVERSIONWIN81 6 +#define SW_SHOWMINNOACTIVE 7 +#define WM_SETFOCUS 0x0007 +#define WMSZ_BOTTOMLEFT 7 +#define HTVSCROLL 7 +#define CF_OEMTEXT 7 +#define IDNO 7 +#define BN_KILLFOCUS 7 +#define LANG_GERMAN 0x07 +#define SUBLANG_ARABIC_TUNISIA 0x07 +#define SUBLANG_ENGLISH_SOUTH_AFRICA 0x07 +#define SUBLANG_SAMI_SOUTHERN_SWEDEN 0x07 +#define SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC 0x07 +#define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07 +#define SW_SHOWNA 8 +#define VK_BACK 0x08 +#define WM_KILLFOCUS 0x0008 +#define WMSZ_BOTTOMRIGHT 8 +#define HTMINBUTTON 8 +#define SMTO_NOTIMEOUTIFNOTHUNG 0x0008 +#define MK_CONTROL 0x0008 +#define CS_DBLCLKS 0x0008 +#define CF_DIB 8 +#define IDCLOSE 8 +#define BST_FOCUS 0x0008 +#define HDS_HIDDEN 0x0008 +#define TBSTYLE_DROPDOWN 0x0008 +#define TBS_BOTH 0x0008 +#define UDS_ALIGNLEFT 0x0008 +#define PBS_MARQUEE 0x08 +#define LWS_USEVISUALSTYLE 0x0008 +#define LVS_SHOWSELALWAYS 0x0008 +#define TVS_EDITLABELS 0x0008 +#define TVS_EX_NOINDENTSTATE 0x0008 +#define TCS_FLATBUTTONS 0x0008 +#define ACS_TIMER 0x0008 +#define MCS_NOTODAYCIRCLE 0x0008 +#define NFS_BUTTON 0x0008 +#define BCSIF_SIZE 0x0008 +#define BCSS_IMAGE 0x0008 +#define LANG_GREEK 0x08 +#define SUBLANG_ARABIC_OMAN 0x08 +#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC 0x08 +#define SUBLANG_ENGLISH_JAMAICA 0x08 +#define SUBLANG_SAMI_SKOLT_FINLAND 0x08 +#define SUBLANG_SPANISH_VENEZUELA 0x08 +#define SW_RESTORE 9 +#define VK_TAB 0x09 +#define HTMAXBUTTON 9 +#define CF_PALETTE 9 +#define IDHELP 9 +#define DTS_TIMEFORMAT 0x0009 +#define LANG_ENGLISH 0x09 +#define SUBLANG_ARABIC_YEMEN 0x09 +#define SUBLANG_ENGLISH_CARIBBEAN 0x09 +#define SUBLANG_SAMI_INARI_FINLAND 0x09 +#define SUBLANG_SERBIAN_SERBIA_LATIN 0x09 +#define SUBLANG_SPANISH_COLOMBIA 0x09 +#define SW_SHOWDEFAULT 10 +#define WM_ENABLE 0x000A +#define HTLEFT 10 +#define CF_PENDATA 10 +#define IDTRYAGAIN 10 +#define HELP_CONTEXTMENU 0x000a +#define LANG_SPANISH 0x0a +#define SUBLANG_ARABIC_SYRIA 0x0a +#define SUBLANG_ENGLISH_BELIZE 0x0a +#define SUBLANG_SERBIAN_SERBIA_CYRILLIC 0x0a +#define SUBLANG_SPANISH_PERU 0x0a +#define VER_PRODUCTMAJORVERSION 10 +#define SW_FORCEMINIMIZE 11 +#define SW_MAX 11 +#define WM_SETREDRAW 0x000B +#define HTRIGHT 11 +#define CF_RIFF 11 +#define IDCONTINUE 11 +#define HELP_FINDER 0x000b +#define LANG_FINNISH 0x0b +#define SUBLANG_ARABIC_JORDAN 0x0b +#define SUBLANG_ENGLISH_TRINIDAD 0x0b +#define SUBLANG_SERBIAN_MONTENEGRO_LATIN 0x0b +#define SUBLANG_SPANISH_ARGENTINA 0x0b +#define VK_CLEAR 0x0C +#define WM_SETTEXT 0x000C +#define HTTOP 12 +#define CF_WAVE 12 +#define HELP_WM_HELP 0x000c +#define DTS_SHORTDATECENTURYFORMAT 0x000C +#define LANG_FRENCH 0x0c +#define SUBLANG_ARABIC_LEBANON 0x0c +#define SUBLANG_ENGLISH_ZIMBABWE 0x0c +#define SUBLANG_SERBIAN_MONTENEGRO_CYRILLIC 0x0c +#define SUBLANG_SPANISH_ECUADOR 0x0c +#define VK_RETURN 0x0D +#define WM_GETTEXT 0x000D +#define HTTOPLEFT 13 +#define CF_UNICODETEXT 13 +#define HELP_SETPOPUP_POS 0x000d +#define LANG_HEBREW 0x0d +#define SUBLANG_ARABIC_KUWAIT 0x0d +#define SUBLANG_ENGLISH_PHILIPPINES 0x0d +#define SUBLANG_SPANISH_CHILE 0x0d +#define WM_GETTEXTLENGTH 0x000E +#define HTTOPRIGHT 14 +#define CF_ENHMETAFILE 14 +#define LANG_HUNGARIAN 0x0e +#define SUBLANG_ARABIC_UAE 0x0e +#define SUBLANG_SPANISH_URUGUAY 0x0e +#define WM_PAINT 0x000F +#define HTBOTTOM 15 +#define CF_HDROP 15 +#define LANG_ICELANDIC 0x0f +#define SUBLANG_ARABIC_BAHRAIN 0x0f +#define SUBLANG_SPANISH_PARAGUAY 0x0f +#define MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID 16 +#define VK_SHIFT 0x10 +#define WM_CLOSE 0x0010 +#define HTBOTTOMLEFT 16 +#define WVR_ALIGNTOP 0x0010 +#define MK_MBUTTON 0x0010 +#define TME_NONCLIENT 0x00000010 +#define CF_LOCALE 16 +#define HELP_TCARD_DATA 0x0010 +#define TBSTYLE_AUTOSIZE 0x0010 +#define TTS_NOANIMATE 0x10 +#define TBS_NOTICKS 0x0010 +#define UDS_AUTOBUDDY 0x0010 +#define PBS_SMOOTHREVERSE 0x10 +#define LWS_USECUSTOMTEXT 0x0010 +#define LVS_SORTASCENDING 0x0010 +#define TVS_DISABLEDRAGDROP 0x0010 +#define TVS_EX_RICHTOOLTIP 0x0010 +#define TCS_FORCEICONLEFT 0x0010 +#define MCS_NOTODAY 0x0010 +#define DTS_APPCANPARSE 0x0010 +#define NFS_ALL 0x0010 +#define LANG_ITALIAN 0x10 +#define SUBLANG_ARABIC_QATAR 0x10 +#define SUBLANG_ENGLISH_INDIA 0x10 +#define SUBLANG_SPANISH_BOLIVIA 0x10 +#define VK_CONTROL 0x11 +#define WM_QUERYENDSESSION 0x0011 +#define HTBOTTOMRIGHT 17 +#define CF_DIBV5 17 +#define HELP_TCARD_OTHER_CALLER 0x0011 +#define LANG_JAPANESE 0x11 +#define SUBLANG_ENGLISH_MALAYSIA 0x11 +#define SUBLANG_SPANISH_EL_SALVADOR 0x11 +#define VK_MENU 0x12 +#define WM_QUIT 0x0012 +#define HTBORDER 18 +#define CF_MAX 18 +#define LANG_KOREAN 0x12 +#define SUBLANG_ENGLISH_SINGAPORE 0x12 +#define SUBLANG_SPANISH_HONDURAS 0x12 +#define VK_PAUSE 0x13 +#define WM_QUERYOPEN 0x0013 +#define HTOBJECT 19 +#define LANG_DUTCH 0x13 +#define SUBLANG_SPANISH_NICARAGUA 0x13 +#define VK_CAPITAL 0x14 +#define WM_ERASEBKGND 0x0014 +#define HTCLOSE 20 +#define LANG_NORWEGIAN 0x14 +#define SUBLANG_SPANISH_PUERTO_RICO 0x14 +#define _SAL_VERSION 20 +#define VK_KANA 0x15 +#define VK_HANGEUL 0x15 +#define VK_HANGUL 0x15 +#define WM_SYSCOLORCHANGE 0x0015 +#define HTHELP 21 +#define LANG_POLISH 0x15 +#define SUBLANG_SPANISH_US 0x15 +#define WM_ENDSESSION 0x0016 +#define LANG_PORTUGUESE 0x16 +#define VK_JUNJA 0x17 +#define LANG_ROMANSH 0x17 +#define RT_MANIFEST 24 +#define VK_FINAL 0x18 +#define WM_SHOWWINDOW 0x0018 +#define LANG_ROMANIAN 0x18 +#define VK_HANJA 0x19 +#define VK_KANJI 0x19 +#define LANG_RUSSIAN 0x19 +#define WM_WININICHANGE 0x001A +#define LANG_BOSNIAN 0x1a +#define LANG_CROATIAN 0x1a +#define LANG_SERBIAN 0x1a +#define VK_ESCAPE 0x1B +#define WM_DEVMODECHANGE 0x001B +#define LANG_SLOVAK 0x1b +#define VK_CONVERT 0x1C +#define WM_ACTIVATEAPP 0x001C +#define LANG_ALBANIAN 0x1c +#define VK_NONCONVERT 0x1D +#define WM_FONTCHANGE 0x001D +#define LANG_SWEDISH 0x1d +#define VK_ACCEPT 0x1E +#define WM_TIMECHANGE 0x001E +#define LANG_THAI 0x1e +#define VK_MODECHANGE 0x1F +#define WM_CANCELMODE 0x001F +#define LANG_TURKISH 0x1f +#define VK_SPACE 0x20 +#define WM_SETCURSOR 0x0020 +#define SMTO_ERRORONEXIT 0x0020 +#define WVR_ALIGNLEFT 0x0020 +#define MK_XBUTTON1 0x0020 +#define CS_OWNDC 0x0020 +#define TBSTYLE_NOPREFIX 0x0020 +#define TTS_NOFADE 0x20 +#define TBS_ENABLESELRANGE 0x0020 +#define UDS_ARROWKEYS 0x0020 +#define LWS_RIGHT 0x0020 +#define LVS_SORTDESCENDING 0x0020 +#define TVS_SHOWSELALWAYS 0x0020 +#define TVS_EX_AUTOHSCROLL 0x0020 +#define TCS_FORCELABELLEFT 0x0020 +#define DTS_RIGHTALIGN 0x0020 +#define NFS_USEFONTASSOC 0x0020 +#define LANG_URDU 0x20 +#define VK_PRIOR 0x21 +#define WM_MOUSEACTIVATE 0x0021 +#define LANG_INDONESIAN 0x21 +#define VK_NEXT 0x22 +#define WM_CHILDACTIVATE 0x0022 +#define LANG_UKRAINIAN 0x22 +#define VK_END 0x23 +#define WM_QUEUESYNC 0x0023 +#define LANG_BELARUSIAN 0x23 +#define VK_HOME 0x24 +#define WM_GETMINMAXINFO 0x0024 +#define LANG_SLOVENIAN 0x24 +#define VK_LEFT 0x25 +#define LANG_ESTONIAN 0x25 +#define VK_UP 0x26 +#define WM_PAINTICON 0x0026 +#define LANG_LATVIAN 0x26 +#define VK_RIGHT 0x27 +#define WM_ICONERASEBKGND 0x0027 +#define LANG_LITHUANIAN 0x27 +#define VK_DOWN 0x28 +#define WM_NEXTDLGCTL 0x0028 +#define LANG_TAJIK 0x28 +#define VK_SELECT 0x29 +#define LANG_FARSI 0x29 +#define LANG_PERSIAN 0x29 +#define VK_PRINT 0x2A +#define WM_SPOOLERSTATUS 0x002A +#define LANG_VIETNAMESE 0x2a +#define VK_EXECUTE 0x2B +#define WM_DRAWITEM 0x002B +#define LANG_ARMENIAN 0x2b +#define VK_SNAPSHOT 0x2C +#define WM_MEASUREITEM 0x002C +#define LANG_AZERI 0x2c +#define LANG_AZERBAIJANI 0x2c +#define VK_INSERT 0x2D +#define WM_DELETEITEM 0x002D +#define LANG_BASQUE 0x2d +#define VK_DELETE 0x2E +#define WM_VKEYTOITEM 0x002E +#define LANG_LOWER_SORBIAN 0x2e +#define LANG_UPPER_SORBIAN 0x2e +#define VK_HELP 0x2F +#define WM_CHARTOITEM 0x002F +#define LANG_MACEDONIAN 0x2f +#define WM_SETFONT 0x0030 +#define WM_GETFONT 0x0031 +#define WM_SETHOTKEY 0x0032 +#define LANG_TSWANA 0x32 +#define WM_GETHOTKEY 0x0033 +#define LANG_XHOSA 0x34 +#define LANG_ZULU 0x35 +#define LANG_AFRIKAANS 0x36 +#define WM_QUERYDRAGICON 0x0037 +#define LANG_GEORGIAN 0x37 +#define LANG_FAEROESE 0x38 +#define WM_COMPAREITEM 0x0039 +#define LANG_HINDI 0x39 +#define LANG_MALTESE 0x3a +#define LANG_SAMI 0x3b +#define LANG_IRISH 0x3c +#define WM_GETOBJECT 0x003D +#define LANG_MALAY 0x3e +#define LANG_KAZAK 0x3f +#define WVR_ALIGNBOTTOM 0x0040 +#define MK_XBUTTON2 0x0040 +#define CS_CLASSDC 0x0040 +#define HDS_DRAGDROP 0x0040 +#define BTNS_SHOWTEXT 0x0040 +#define TTS_BALLOON 0x40 +#define TBS_FIXEDLENGTH 0x0040 +#define UDS_HORZ 0x0040 +#define LVS_SHAREIMAGELISTS 0x0040 +#define TVS_RTLREADING 0x0040 +#define TVS_EX_FADEINOUTEXPANDOS 0x0040 +#define TCS_HOTTRACK 0x0040 +#define MCS_NOTRAILINGDATES 0x0040 +#define LANG_KYRGYZ 0x40 +#define WM_COMPACTING 0x0041 +#define LANG_SWAHILI 0x41 +#define LANG_TURKMEN 0x42 +#define LANG_UZBEK 0x43 +#define WM_COMMNOTIFY 0x0044 +#define LANG_TATAR 0x44 +#define LANG_BANGLA 0x45 +#define LANG_BENGALI 0x45 +#define WM_WINDOWPOSCHANGING 0x0046 +#define LANG_PUNJABI 0x46 +#define WM_WINDOWPOSCHANGED 0x0047 +#define LANG_GUJARATI 0x47 +#define WM_POWER 0x0048 +#define LANG_ODIA 0x48 +#define LANG_ORIYA 0x48 +#define LANG_TAMIL 0x49 +#define WM_COPYDATA 0x004A +#define LANG_TELUGU 0x4a +#define WM_CANCELJOURNAL 0x004B +#define LANG_KANNADA 0x4b +#define LANG_MALAYALAM 0x4c +#define LANG_ASSAMESE 0x4d +#define WM_NOTIFY 0x004E +#define LANG_MARATHI 0x4e +#define LANG_SANSKRIT 0x4f +#define WM_INPUTLANGCHANGEREQUEST 0x0050 +#define LANG_MONGOLIAN 0x50 +#define WM_INPUTLANGCHANGE 0x0051 +#define LANG_TIBETAN 0x51 +#define WM_TCARD 0x0052 +#define LANG_WELSH 0x52 +#define WM_HELP 0x0053 +#define LANG_KHMER 0x53 +#define WM_USERCHANGED 0x0054 +#define LANG_LAO 0x54 +#define WM_NOTIFYFORMAT 0x0055 +#define LANG_GALICIAN 0x56 +#define LANG_KONKANI 0x57 +#define LANG_MANIPURI 0x58 +#define LANG_SINDHI 0x59 +#define LANG_SYRIAC 0x5a +#define VK_LWIN 0x5B +#define LANG_SINHALESE 0x5b +#define VK_RWIN 0x5C +#define LANG_CHEROKEE 0x5c +#define VK_APPS 0x5D +#define LANG_INUKTITUT 0x5d +#define LANG_AMHARIC 0x5e +#define VK_SLEEP 0x5F +#define LANG_TAMAZIGHT 0x5f +#define VK_NUMPAD0 0x60 +#define LANG_KASHMIRI 0x60 +#define VK_NUMPAD1 0x61 +#define LANG_NEPALI 0x61 +#define VK_NUMPAD2 0x62 +#define LANG_FRISIAN 0x62 +#define VK_NUMPAD3 0x63 +#define LANG_PASHTO 0x63 +#define WINAPI_FAMILY_DESKTOP_APP 100 +#define VK_NUMPAD4 0x64 +#define LANG_FILIPINO 0x64 +#define VS_USER_DEFINED 100 +#define VK_NUMPAD5 0x65 +#define LANG_DIVEHI 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define LANG_FULAH 0x67 +#define LANG_PULAR 0x67 +#define VK_NUMPAD8 0x68 +#define LANG_HAUSA 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define LANG_YORUBA 0x6a +#define VK_ADD 0x6B +#define LANG_QUECHUA 0x6b +#define VK_SEPARATOR 0x6C +#define LANG_SOTHO 0x6c +#define VK_SUBTRACT 0x6D +#define LANG_BASHKIR 0x6d +#define VK_DECIMAL 0x6E +#define LANG_LUXEMBOURGISH 0x6e +#define VK_DIVIDE 0x6F +#define LANG_GREENLANDIC 0x6f +#define VK_F1 0x70 +#define LANG_IGBO 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define LANG_TIGRIGNA 0x73 +#define LANG_TIGRINYA 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define LANG_HAWAIIAN 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define WHEEL_DELTA 120 +#define LANG_YI 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define LANG_MAPUDUNGUN 0x7a +#define VK_F12 0x7B +#define WM_CONTEXTMENU 0x007B +#define VK_F13 0x7C +#define WM_STYLECHANGING 0x007C +#define LANG_MOHAWK 0x7c +#define VK_F14 0x7D +#define WM_STYLECHANGED 0x007D +#define VK_F15 0x7E +#define WM_DISPLAYCHANGE 0x007E +#define LANG_BRETON 0x7e +#define VK_F16 0x7F +#define WM_GETICON 0x007F +#define LANG_INVARIANT 0x7f +#define VK_F17 0x80 +#define WM_SETICON 0x0080 +#define WVR_ALIGNRIGHT 0x0080 +#define CS_PARENTDC 0x0080 +#define CF_OWNERDISPLAY 0x0080 +#define HDS_FULLDRAG 0x0080 +#define BTNS_WHOLEDROPDOWN 0x0080 +#define TTS_CLOSE 0x80 +#define TBS_NOTHUMB 0x0080 +#define UDS_NOTHOUSANDS 0x0080 +#define LVS_NOLABELWRAP 0x0080 +#define TVS_NOTOOLTIPS 0x0080 +#define TVS_EX_PARTIALCHECKBOXES 0x0080 +#define TCS_VERTICAL 0x0080 +#define MCS_SHORTDAYSOFWEEK 0x0080 +#define LANG_UIGHUR 0x80 +#define VK_F18 0x81 +#define WM_NCCREATE 0x0081 +#define CF_DSPTEXT 0x0081 +#define LANG_MAORI 0x81 +#define VK_F19 0x82 +#define WM_NCDESTROY 0x0082 +#define CF_DSPBITMAP 0x0082 +#define LANG_OCCITAN 0x82 +#define VK_F20 0x83 +#define WM_NCCALCSIZE 0x0083 +#define CF_DSPMETAFILEPICT 0x0083 +#define LANG_CORSICAN 0x83 +#define VK_F21 0x84 +#define WM_NCHITTEST 0x0084 +#define LANG_ALSATIAN 0x84 +#define VK_F22 0x85 +#define WM_NCPAINT 0x0085 +#define LANG_SAKHA 0x85 +#define LANG_YAKUT 0x85 +#define VK_F23 0x86 +#define WM_NCACTIVATE 0x0086 +#define LANG_KICHE 0x86 +#define VK_F24 0x87 +#define WM_GETDLGCODE 0x0087 +#define LANG_KINYARWANDA 0x87 +#define VK_NAVIGATION_VIEW 0x88 +#define WM_SYNCPAINT 0x0088 +#define LANG_WOLOF 0x88 +#define VK_NAVIGATION_MENU 0x89 +#define VK_NAVIGATION_UP 0x8A +#define VK_NAVIGATION_DOWN 0x8B +#define VK_NAVIGATION_LEFT 0x8C +#define LANG_DARI 0x8c +#define VK_NAVIGATION_RIGHT 0x8D +#define VK_NAVIGATION_ACCEPT 0x8E +#define CF_DSPENHMETAFILE 0x008E +#define VK_NAVIGATION_CANCEL 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define LANG_SCOTTISH_GAELIC 0x91 +#define VK_OEM_NEC_EQUAL 0x92 +#define VK_OEM_FJ_JISHO 0x92 +#define LANG_CENTRAL_KURDISH 0x92 +#define VK_OEM_FJ_MASSHOU 0x93 +#define VK_OEM_FJ_TOUROKU 0x94 +#define VK_OEM_FJ_LOYA 0x95 +#define VK_OEM_FJ_ROYA 0x96 +#define VK_LSHIFT 0xA0 +#define WM_NCMOUSEMOVE 0x00A0 +#define VK_RSHIFT 0xA1 +#define WM_NCLBUTTONDOWN 0x00A1 +#define VK_LCONTROL 0xA2 +#define WM_NCLBUTTONUP 0x00A2 +#define VK_RCONTROL 0xA3 +#define WM_NCLBUTTONDBLCLK 0x00A3 +#define VK_LMENU 0xA4 +#define WM_NCRBUTTONDOWN 0x00A4 +#define VK_RMENU 0xA5 +#define WM_NCRBUTTONUP 0x00A5 +#define VK_BROWSER_BACK 0xA6 +#define WM_NCRBUTTONDBLCLK 0x00A6 +#define VK_BROWSER_FORWARD 0xA7 +#define WM_NCMBUTTONDOWN 0x00A7 +#define VK_BROWSER_REFRESH 0xA8 +#define WM_NCMBUTTONUP 0x00A8 +#define VK_BROWSER_STOP 0xA9 +#define WM_NCMBUTTONDBLCLK 0x00A9 +#define VK_BROWSER_SEARCH 0xAA +#define VK_BROWSER_FAVORITES 0xAB +#define WM_NCXBUTTONDOWN 0x00AB +#define VK_BROWSER_HOME 0xAC +#define WM_NCXBUTTONUP 0x00AC +#define VK_VOLUME_MUTE 0xAD +#define WM_NCXBUTTONDBLCLK 0x00AD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0xB0 +#define EM_GETSEL 0x00B0 +#define VK_MEDIA_PREV_TRACK 0xB1 +#define EM_SETSEL 0x00B1 +#define VK_MEDIA_STOP 0xB2 +#define EM_GETRECT 0x00B2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define EM_SETRECT 0x00B3 +#define VK_LAUNCH_MAIL 0xB4 +#define EM_SETRECTNP 0x00B4 +#define VK_LAUNCH_MEDIA_SELECT 0xB5 +#define EM_SCROLL 0x00B5 +#define VK_LAUNCH_APP1 0xB6 +#define EM_LINESCROLL 0x00B6 +#define VK_LAUNCH_APP2 0xB7 +#define EM_SCROLLCARET 0x00B7 +#define EM_GETMODIFY 0x00B8 +#define EM_SETMODIFY 0x00B9 +#define VK_OEM_1 0xBA +#define EM_GETLINECOUNT 0x00BA +#define VK_OEM_PLUS 0xBB +#define EM_LINEINDEX 0x00BB +#define VK_OEM_COMMA 0xBC +#define EM_SETHANDLE 0x00BC +#define VK_OEM_MINUS 0xBD +#define EM_GETHANDLE 0x00BD +#define VK_OEM_PERIOD 0xBE +#define EM_GETTHUMB 0x00BE +#define VK_OEM_2 0xBF +#define VK_OEM_3 0xC0 +#define EM_LINELENGTH 0x00C1 +#define EM_REPLACESEL 0x00C2 +#define VK_GAMEPAD_A 0xC3 +#define VK_GAMEPAD_B 0xC4 +#define EM_GETLINE 0x00C4 +#define VK_GAMEPAD_X 0xC5 +#define EM_LIMITTEXT 0x00C5 +#define VK_GAMEPAD_Y 0xC6 +#define EM_CANUNDO 0x00C6 +#define VK_GAMEPAD_RIGHT_SHOULDER 0xC7 +#define EM_UNDO 0x00C7 +#define VK_GAMEPAD_LEFT_SHOULDER 0xC8 +#define EM_FMTLINES 0x00C8 +#define VK_GAMEPAD_LEFT_TRIGGER 0xC9 +#define EM_LINEFROMCHAR 0x00C9 +#define VK_GAMEPAD_RIGHT_TRIGGER 0xCA +#define VK_GAMEPAD_DPAD_UP 0xCB +#define EM_SETTABSTOPS 0x00CB +#define VK_GAMEPAD_DPAD_DOWN 0xCC +#define EM_SETPASSWORDCHAR 0x00CC +#define VK_GAMEPAD_DPAD_LEFT 0xCD +#define EM_EMPTYUNDOBUFFER 0x00CD +#define VK_GAMEPAD_DPAD_RIGHT 0xCE +#define EM_GETFIRSTVISIBLELINE 0x00CE +#define VK_GAMEPAD_MENU 0xCF +#define EM_SETREADONLY 0x00CF +#define VK_GAMEPAD_VIEW 0xD0 +#define EM_SETWORDBREAKPROC 0x00D0 +#define VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON 0xD1 +#define EM_GETWORDBREAKPROC 0x00D1 +#define VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON 0xD2 +#define EM_GETPASSWORDCHAR 0x00D2 +#define VK_GAMEPAD_LEFT_THUMBSTICK_UP 0xD3 +#define EM_SETMARGINS 0x00D3 +#define VK_GAMEPAD_LEFT_THUMBSTICK_DOWN 0xD4 +#define EM_GETMARGINS 0x00D4 +#define VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT 0xD5 +#define EM_GETLIMITTEXT 0x00D5 +#define VK_GAMEPAD_LEFT_THUMBSTICK_LEFT 0xD6 +#define EM_POSFROMCHAR 0x00D6 +#define VK_GAMEPAD_RIGHT_THUMBSTICK_UP 0xD7 +#define EM_CHARFROMPOS 0x00D7 +#define VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN 0xD8 +#define EM_SETIMESTATUS 0x00D8 +#define VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT 0xD9 +#define EM_GETIMESTATUS 0x00D9 +#define VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT 0xDA +#define VK_OEM_4 0xDB +#define VK_OEM_5 0xDC +#define VK_OEM_6 0xDD +#define VK_OEM_7 0xDE +#define VK_OEM_8 0xDF +#define VK_OEM_AX 0xE1 +#define VK_OEM_102 0xE2 +#define VK_ICO_HELP 0xE3 +#define VK_ICO_00 0xE4 +#define VK_PROCESSKEY 0xE5 +#define VK_ICO_CLEAR 0xE6 +#define VK_PACKET 0xE7 +#define VK_OEM_RESET 0xE9 +#define VK_OEM_JUMP 0xEA +#define VK_OEM_PA1 0xEB +#define VK_OEM_PA2 0xEC +#define VK_OEM_PA3 0xED +#define VK_OEM_WSCTRL 0xEE +#define VK_OEM_CUSEL 0xEF +#define VK_OEM_ATTN 0xF0 +#define BM_GETCHECK 0x00F0 +#define VK_OEM_FINISH 0xF1 +#define BM_SETCHECK 0x00F1 +#define VK_OEM_COPY 0xF2 +#define BM_GETSTATE 0x00F2 +#define VK_OEM_AUTO 0xF3 +#define BM_SETSTATE 0x00F3 +#define VK_OEM_ENLW 0xF4 +#define BM_SETSTYLE 0x00F4 +#define VK_OEM_BACKTAB 0xF5 +#define BM_CLICK 0x00F5 +#define VK_ATTN 0xF6 +#define BM_GETIMAGE 0x00F6 +#define VK_CRSEL 0xF7 +#define BM_SETIMAGE 0x00F7 +#define VK_EXSEL 0xF8 +#define BM_SETDONTCLICK 0x00F8 +#define VK_EREOF 0xF9 +#define VK_PLAY 0xFA +#define VK_ZOOM 0xFB +#define VK_NONAME 0xFC +#define VK_PA1 0xFD +#define VK_OEM_CLEAR 0xFE +#define WM_INPUT_DEVICE_CHANGE 0x00FE +#define SUBVERSION_MASK 0x000000FF +#define WM_INPUT 0x00FF +#define WM_KEYFIRST 0x0100 +#define WM_KEYDOWN 0x0100 +#define WVR_HREDRAW 0x0100 +#define HDS_FILTERBAR 0x0100 +#define TBSTYLE_TOOLTIPS 0x0100 +#define RBS_TOOLTIPS 0x00000100 +#define TTS_USEVISUALSTYLE 0x100 +#define SBARS_SIZEGRIP 0x0100 +#define TBS_TOOLTIPS 0x0100 +#define UDS_HOTTRACK 0x0100 +#define LVS_AUTOARRANGE 0x0100 +#define TVS_CHECKBOXES 0x0100 +#define TVS_EX_EXCLUSIONCHECKBOXES 0x0100 +#define TCS_BUTTONS 0x0100 +#define MCS_NOSELCHANGEONNAV 0x0100 +#define WM_KEYUP 0x0101 +#define WM_CHAR 0x0102 +#define WM_DEADCHAR 0x0103 +#define WM_SYSKEYDOWN 0x0104 +#define WM_SYSKEYUP 0x0105 +#define WM_SYSCHAR 0x0106 +#define WM_SYSDEADCHAR 0x0107 +#define WM_UNICHAR 0x0109 +#define WM_KEYLAST 0x0109 +#define WM_IME_STARTCOMPOSITION 0x010D +#define WM_IME_ENDCOMPOSITION 0x010E +#define WM_IME_COMPOSITION 0x010F +#define WM_IME_KEYLAST 0x010F +#define WM_INITDIALOG 0x0110 +#define WM_COMMAND 0x0111 +#define WM_SYSCOMMAND 0x0112 +#define WM_TIMER 0x0113 +#define WM_HSCROLL 0x0114 +#define WM_VSCROLL 0x0115 +#define WM_INITMENU 0x0116 +#define WM_INITMENUPOPUP 0x0117 +#define WM_GESTURE 0x0119 +#define WM_GESTURENOTIFY 0x011A +#define WM_MENUSELECT 0x011F +#define WM_MENUCHAR 0x0120 +#define WM_ENTERIDLE 0x0121 +#define WM_MENURBUTTONUP 0x0122 +#define WM_MENUDRAG 0x0123 +#define WM_MENUGETOBJECT 0x0124 +#define WM_UNINITMENUPOPUP 0x0125 +#define WM_MENUCOMMAND 0x0126 +#define WM_CHANGEUISTATE 0x0127 +#define WM_UPDATEUISTATE 0x0128 +#define WM_QUERYUISTATE 0x0129 +#define WM_CTLCOLORMSGBOX 0x0132 +#define WM_CTLCOLOREDIT 0x0133 +#define WM_CTLCOLORLISTBOX 0x0134 +#define WM_CTLCOLORBTN 0x0135 +#define WM_CTLCOLORDLG 0x0136 +#define WM_CTLCOLORSCROLLBAR 0x0137 +#define WM_CTLCOLORSTATIC 0x0138 +#define MN_GETHMENU 0x01E1 +#define _WIN32_IE_IE20 0x0200 +#define WM_MOUSEFIRST 0x0200 +#define WM_MOUSEMOVE 0x0200 +#define WVR_VREDRAW 0x0200 +#define CS_NOCLOSE 0x0200 +#define CF_PRIVATEFIRST 0x0200 +#define HDS_FLAT 0x0200 +#define TBSTYLE_WRAPABLE 0x0200 +#define RBS_VARHEIGHT 0x00000200 +#define TBS_REVERSED 0x0200 +#define LVS_EDITLABELS 0x0200 +#define TVS_TRACKSELECT 0x0200 +#define TVS_EX_DIMMEDCHECKBOXES 0x0200 +#define TCS_MULTILINE 0x0200 +#define WM_LBUTTONDOWN 0x0201 +#define WM_LBUTTONUP 0x0202 +#define WM_LBUTTONDBLCLK 0x0203 +#define WM_RBUTTONDOWN 0x0204 +#define WM_RBUTTONUP 0x0205 +#define WM_RBUTTONDBLCLK 0x0206 +#define WM_MBUTTONDOWN 0x0207 +#define WM_MBUTTONUP 0x0208 +#define WM_MBUTTONDBLCLK 0x0209 +#define WM_MOUSEWHEEL 0x020A +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_MOUSEHWHEEL 0x020E +#define WM_MOUSELAST 0x020E +#define WM_PARENTNOTIFY 0x0210 +#define WM_ENTERMENULOOP 0x0211 +#define WM_EXITMENULOOP 0x0212 +#define WM_NEXTMENU 0x0213 +#define WM_SIZING 0x0214 +#define WM_CAPTURECHANGED 0x0215 +#define WM_MOVING 0x0216 +#define WM_POWERBROADCAST 0x0218 +#define WM_DEVICECHANGE 0x0219 +#define WM_MDICREATE 0x0220 +#define WM_MDIDESTROY 0x0221 +#define WM_MDIACTIVATE 0x0222 +#define WM_MDIRESTORE 0x0223 +#define WM_MDINEXT 0x0224 +#define WM_MDIMAXIMIZE 0x0225 +#define WM_MDITILE 0x0226 +#define WM_MDICASCADE 0x0227 +#define WM_MDIICONARRANGE 0x0228 +#define WM_MDIGETACTIVE 0x0229 +#define WM_MDISETMENU 0x0230 +#define WM_ENTERSIZEMOVE 0x0231 +#define WM_EXITSIZEMOVE 0x0232 +#define WM_DROPFILES 0x0233 +#define WM_MDIREFRESHMENU 0x0234 +#define WM_POINTERDEVICECHANGE 0x238 +#define WM_POINTERDEVICEINRANGE 0x239 +#define WM_POINTERDEVICEOUTOFRANGE 0x23A +#define WM_TOUCH 0x0240 +#define WM_NCPOINTERUPDATE 0x0241 +#define WM_NCPOINTERDOWN 0x0242 +#define WM_NCPOINTERUP 0x0243 +#define WM_POINTERUPDATE 0x0245 +#define WM_POINTERDOWN 0x0246 +#define WM_POINTERUP 0x0247 +#define WM_POINTERENTER 0x0249 +#define WM_POINTERLEAVE 0x024A +#define WM_POINTERACTIVATE 0x024B +#define WM_POINTERCAPTURECHANGED 0x024C +#define WM_TOUCHHITTESTING 0x024D +#define WM_POINTERWHEEL 0x024E +#define WM_POINTERHWHEEL 0x024F +#define DM_POINTERHITTEST 0x0250 +#define WM_POINTERROUTEDTO 0x0251 +#define WM_POINTERROUTEDAWAY 0x0252 +#define WM_POINTERROUTEDRELEASED 0x0253 +#define WM_IME_SETCONTEXT 0x0281 +#define WM_IME_NOTIFY 0x0282 +#define WM_IME_CONTROL 0x0283 +#define WM_IME_COMPOSITIONFULL 0x0284 +#define WM_IME_SELECT 0x0285 +#define WM_IME_CHAR 0x0286 +#define WM_IME_REQUEST 0x0288 +#define WM_IME_KEYDOWN 0x0290 +#define WM_IME_KEYUP 0x0291 +#define WM_NCMOUSEHOVER 0x02A0 +#define WM_MOUSEHOVER 0x02A1 +#define WM_NCMOUSELEAVE 0x02A2 +#define WM_MOUSELEAVE 0x02A3 +#define WM_WTSSESSION_CHANGE 0x02B1 +#define WM_TABLET_FIRST 0x02c0 +#define WM_TABLET_LAST 0x02df +#define WM_DPICHANGED 0x02E0 +#define CF_PRIVATELAST 0x02FF +#define _WIN32_IE_IE30 0x0300 +#define WM_CUT 0x0300 +#define CF_GDIOBJFIRST 0x0300 +#define WM_COPY 0x0301 +#define _WIN32_IE_IE302 0x0302 +#define WM_PASTE 0x0302 +#define WM_CLEAR 0x0303 +#define WM_UNDO 0x0304 +#define WM_RENDERFORMAT 0x0305 +#define WM_RENDERALLFORMATS 0x0306 +#define WM_DESTROYCLIPBOARD 0x0307 +#define WM_DRAWCLIPBOARD 0x0308 +#define WM_PAINTCLIPBOARD 0x0309 +#define WM_VSCROLLCLIPBOARD 0x030A +#define WM_SIZECLIPBOARD 0x030B +#define WM_ASKCBFORMATNAME 0x030C +#define WM_CHANGECBCHAIN 0x030D +#define WM_HSCROLLCLIPBOARD 0x030E +#define WM_QUERYNEWPALETTE 0x030F +#define WM_PALETTEISCHANGING 0x0310 +#define WM_PALETTECHANGED 0x0311 +#define WM_HOTKEY 0x0312 +#define WM_PRINT 0x0317 +#define WM_PRINTCLIENT 0x0318 +#define WM_APPCOMMAND 0x0319 +#define WM_THEMECHANGED 0x031A +#define WM_CLIPBOARDUPDATE 0x031D +#define WM_DWMCOMPOSITIONCHANGED 0x031E +#define WM_DWMNCRENDERINGCHANGED 0x031F +#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 +#define WM_DWMWINDOWMAXIMIZEDCHANGE 0x0321 +#define WM_DWMSENDICONICTHUMBNAIL 0x0323 +#define WM_DWMSENDICONICLIVEPREVIEWBITMAP 0x0326 +#define WM_GETTITLEBARINFOEX 0x033F +#define WM_HANDHELDFIRST 0x0358 +#define WM_HANDHELDLAST 0x035F +#define WM_AFXFIRST 0x0360 +#define WM_AFXLAST 0x037F +#define WM_PENWINFIRST 0x0380 +#define WM_PENWINLAST 0x038F +#define WM_DDE_FIRST 0x03E0 +#define CF_GDIOBJLAST 0x03FF +#define _WIN32_WINNT_NT4 0x0400 +#define _WIN32_IE_IE40 0x0400 +#define WM_USER 0x0400 +#define WVR_VALIDRECTS 0x0400 +#define HDS_CHECKBOXES 0x0400 +#define TBSTYLE_ALTDRAG 0x0400 +#define RBS_BANDBORDERS 0x00000400 +#define TBS_DOWNISLEFT 0x0400 +#define LVS_OWNERDRAWFIXED 0x0400 +#define TVS_SINGLEEXPAND 0x0400 +#define TVS_EX_DRAWIMAGEASYNC 0x0400 +#define TCS_FIXEDWIDTH 0x0400 +#define ctlFirst 0x0400 +#define psh1 0x0400 +#define _WIN32_IE_IE401 0x0401 +#define psh2 0x0401 +#define psh3 0x0402 +#define psh4 0x0403 +#define psh5 0x0404 +#define psh6 0x0405 +#define psh7 0x0406 +#define psh8 0x0407 +#define psh9 0x0408 +#define psh10 0x0409 +#define psh11 0x040a +#define psh12 0x040b +#define psh13 0x040c +#define psh14 0x040d +#define psh15 0x040e +#define psh16 0x040f +#define _WIN32_WINDOWS 0x0410 +#define chx1 0x0410 +#define chx2 0x0411 +#define chx3 0x0412 +#define chx4 0x0413 +#define chx5 0x0414 +#define chx6 0x0415 +#define chx7 0x0416 +#define chx8 0x0417 +#define chx9 0x0418 +#define chx10 0x0419 +#define chx11 0x041a +#define chx12 0x041b +#define chx13 0x041c +#define chx14 0x041d +#define chx15 0x041e +#define chx16 0x041f +#define rad1 0x0420 +#define rad2 0x0421 +#define rad3 0x0422 +#define rad4 0x0423 +#define rad5 0x0424 +#define rad6 0x0425 +#define rad7 0x0426 +#define rad8 0x0427 +#define rad9 0x0428 +#define rad10 0x0429 +#define rad11 0x042a +#define rad12 0x042b +#define rad13 0x042c +#define rad14 0x042d +#define rad15 0x042e +#define rad16 0x042f +#define grp1 0x0430 +#define grp2 0x0431 +#define grp3 0x0432 +#define grp4 0x0433 +#define frm1 0x0434 +#define frm2 0x0435 +#define frm3 0x0436 +#define frm4 0x0437 +#define rct1 0x0438 +#define rct2 0x0439 +#define rct3 0x043a +#define rct4 0x043b +#define ico1 0x043c +#define ico2 0x043d +#define ico3 0x043e +#define ico4 0x043f +#define stc1 0x0440 +#define stc2 0x0441 +#define stc3 0x0442 +#define stc4 0x0443 +#define stc5 0x0444 +#define stc6 0x0445 +#define stc7 0x0446 +#define stc8 0x0447 +#define stc9 0x0448 +#define stc10 0x0449 +#define stc11 0x044a +#define stc12 0x044b +#define stc13 0x044c +#define stc14 0x044d +#define stc15 0x044e +#define stc16 0x044f +#define stc17 0x0450 +#define stc18 0x0451 +#define stc19 0x0452 +#define stc20 0x0453 +#define stc21 0x0454 +#define stc22 0x0455 +#define stc23 0x0456 +#define stc24 0x0457 +#define stc25 0x0458 +#define stc26 0x0459 +#define stc27 0x045a +#define stc28 0x045b +#define stc29 0x045c +#define stc30 0x045d +#define stc31 0x045e +#define stc32 0x045f +#define lst1 0x0460 +#define lst2 0x0461 +#define lst3 0x0462 +#define lst4 0x0463 +#define lst5 0x0464 +#define lst6 0x0465 +#define lst7 0x0466 +#define lst8 0x0467 +#define lst9 0x0468 +#define lst10 0x0469 +#define lst11 0x046a +#define lst12 0x046b +#define lst13 0x046c +#define lst14 0x046d +#define lst15 0x046e +#define lst16 0x046f +#define cmb1 0x0470 +#define cmb2 0x0471 +#define cmb3 0x0472 +#define cmb4 0x0473 +#define cmb5 0x0474 +#define cmb6 0x0475 +#define cmb7 0x0476 +#define cmb8 0x0477 +#define cmb9 0x0478 +#define cmb10 0x0479 +#define cmb11 0x047a +#define cmb12 0x047b +#define cmb13 0x047c +#define cmb14 0x047d +#define cmb15 0x047e +#define cmb16 0x047f +#define edt1 0x0480 +#define edt2 0x0481 +#define edt3 0x0482 +#define edt4 0x0483 +#define edt5 0x0484 +#define edt6 0x0485 +#define edt7 0x0486 +#define edt8 0x0487 +#define edt9 0x0488 +#define edt10 0x0489 +#define edt11 0x048a +#define edt12 0x048b +#define edt13 0x048c +#define edt14 0x048d +#define edt15 0x048e +#define edt16 0x048f +#define scr1 0x0490 +#define scr2 0x0491 +#define scr3 0x0492 +#define scr4 0x0493 +#define scr5 0x0494 +#define scr6 0x0495 +#define scr7 0x0496 +#define scr8 0x0497 +#define ctl1 0x04A0 +#define ctlLast 0x04ff +#define _WIN32_WINNT_WIN2K 0x0500 +#define _WIN32_IE_IE50 0x0500 +#define _WIN32_WINNT_WINXP 0x0501 +#define _WIN32_IE_IE501 0x0501 +#define _WIN32_WINNT_WS03 0x0502 +#define _WIN32_IE_IE55 0x0550 +#define _WIN32_WINNT_WIN6 0x0600 +#define _WIN32_WINNT_VISTA 0x0600 +#define _WIN32_WINNT_WS08 0x0600 +#define _WIN32_WINNT_LONGHORN 0x0600 +#define _WIN32_IE_IE60 0x0600 +#define FILEOPENORD 1536 +#define _WIN32_WINNT_WIN7 0x0601 +#define _WIN32_IE_IE60SP1 0x0601 +#define MULTIFILEOPENORD 1537 +#define _WIN32_WINNT_WIN8 0x0602 +#define _WIN32_IE_WS03 0x0602 +#define PRINTDLGORD 1538 +#define _WIN32_WINNT_WINBLUE 0x0603 +#define _WIN32_IE_IE60SP2 0x0603 +#define PRNSETUPDLGORD 1539 +#define FINDDLGORD 1540 +#define REPLACEDLGORD 1541 +#define FONTDLGORD 1542 +#define FORMATDLGORD31 1543 +#define FORMATDLGORD30 1544 +#define RUNDLGORD 1545 +#define PAGESETUPDLGORD 1546 +#define NEWFILEOPENORD 1547 +#define PRINTDLGEXORD 1549 +#define PAGESETUPDLGORDMOTIF 1550 +#define COLORMGMTDLGORD 1551 +#define NEWFILEOPENV2ORD 1552 +#define NEWFILEOPENV3ORD 1553 +#define NEWFORMATDLGWITHLINK 1591 +#define IDC_MANAGE_LINK 1592 +#define _WIN32_IE_IE70 0x0700 +#define _WIN32_IE_IE80 0x0800 +#define CS_SAVEBITS 0x0800 +#define HDS_NOSIZING 0x0800 +#define TBSTYLE_FLAT 0x0800 +#define RBS_FIXEDORDER 0x00000800 +#define SBARS_TOOLTIPS 0x0800 +#define SBT_TOOLTIPS 0x0800 +#define TBS_NOTIFYBEFOREMOVE 0x0800 +#define LVS_ALIGNLEFT 0x0800 +#define TVS_INFOTIP 0x0800 +#define TCS_RAGGEDRIGHT 0x0800 +#define _WIN32_IE_IE90 0x0900 +#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 +#define _WIN32_WINNT_WIN10 0x0A00 +#define _WIN32_IE_IE100 0x0A00 +#define _WIN32_IE_IE110 0x0A00 +#define _WIN32_WINNT 0x0A00 +#define _WIN32_IE 0x0A00 +#define VER_PRODUCTVERSION_W 0x0A00 +#define LVS_ALIGNMASK 0x0c00 +#define CS_BYTEALIGNCLIENT 0x1000 +#define HDS_OVERFLOW 0x1000 +#define TBSTYLE_LIST 0x1000 +#define RBS_REGISTERDROP 0x00001000 +#define TBS_TRANSPARENTBKGND 0x1000 +#define LVS_OWNERDATA 0x1000 +#define TVS_FULLROWSELECT 0x1000 +#define TCS_FOCUSONBUTTONDOWN 0x1000 +#define CS_BYTEALIGNWINDOW 0x2000 +#define TBSTYLE_CUSTOMERASE 0x2000 +#define RBS_AUTOSIZE 0x00002000 +#define LVS_NOSCROLL 0x2000 +#define TVS_NOSCROLL 0x2000 +#define TCS_OWNERDRAWFIXED 0x2000 +#define VER_PRODUCTBUILD 10011 +#define VER_STATICPRODUCTBUILD 10011 +#define CS_GLOBALCLASS 0x4000 +#define TBSTYLE_REGISTERDROP 0x4000 +#define RBS_VERTICALGRIPPER 0x00004000 +#define LVS_NOCOLUMNHEADER 0x4000 +#define TVS_NONEVENHEIGHT 0x4000 +#define TCS_TOOLTIPS 0x4000 +#define VER_PRODUCTBUILD_QFE 16384 +#define VER_STATICPRODUCTBUILD_QFE 16384 +#define IDH_NO_HELP 28440 +#define IDH_MISSING_CONTEXT 28441 +#define IDH_GENERIC_HELP_BUTTON 28442 +#define IDH_OK 28443 +#define IDH_CANCEL 28444 +#define IDH_HELP 28445 +#define LANG_BOSNIAN_NEUTRAL 0x781a +#define LANG_CHINESE_TRADITIONAL 0x7c04 +#define LANG_SERBIAN_NEUTRAL 0x7c1a +#define IDTIMEOUT 32000 +#define OCR_NORMAL 32512 +#define OIC_SAMPLE 32512 +#define IDI_APPLICATION 32512 +#define OCR_IBEAM 32513 +#define OIC_HAND 32513 +#define IDI_HAND 32513 +#define OCR_WAIT 32514 +#define OIC_QUES 32514 +#define IDI_QUESTION 32514 +#define OCR_CROSS 32515 +#define OIC_BANG 32515 +#define IDI_EXCLAMATION 32515 +#define OCR_UP 32516 +#define OIC_NOTE 32516 +#define IDI_ASTERISK 32516 +#define OIC_WINLOGO 32517 +#define IDI_WINLOGO 32517 +#define OIC_SHIELD 32518 +#define IDI_SHIELD 32518 +#define OCR_SIZE 32640 +#define OCR_ICON 32641 +#define OCR_SIZENWSE 32642 +#define OCR_SIZENESW 32643 +#define OCR_SIZEWE 32644 +#define OCR_SIZENS 32645 +#define OCR_SIZEALL 32646 +#define OCR_ICOCUR 32647 +#define OCR_NO 32648 +#define OCR_HAND 32649 +#define OCR_APPSTARTING 32650 +#define OBM_LFARROWI 32734 +#define OBM_RGARROWI 32735 +#define OBM_DNARROWI 32736 +#define OBM_UPARROWI 32737 +#define OBM_COMBO 32738 +#define OBM_MNARROW 32739 +#define OBM_LFARROWD 32740 +#define OBM_RGARROWD 32741 +#define OBM_DNARROWD 32742 +#define OBM_UPARROWD 32743 +#define OBM_RESTORED 32744 +#define OBM_ZOOMD 32745 +#define OBM_REDUCED 32746 +#define OBM_RESTORE 32747 +#define OBM_ZOOM 32748 +#define OBM_REDUCE 32749 +#define OBM_LFARROW 32750 +#define OBM_RGARROW 32751 +#define OBM_DNARROW 32752 +#define OBM_UPARROW 32753 +#define OBM_CLOSE 32754 +#define OBM_OLD_RESTORE 32755 +#define OBM_OLD_ZOOM 32756 +#define OBM_OLD_REDUCE 32757 +#define OBM_BTNCORNERS 32758 +#define OBM_CHECKBOXES 32759 +#define OBM_CHECK 32760 +#define OBM_BTSIZE 32761 +#define OBM_OLD_LFARROW 32762 +#define OBM_OLD_RGARROW 32763 +#define OBM_OLD_DNARROW 32764 +#define OBM_OLD_UPARROW 32765 +#define OBM_SIZE 32766 +#define OBM_OLD_CLOSE 32767 +#define WM_APP 0x8000 +#define HELP_TCARD 0x8000 +#define TBSTYLE_TRANSPARENT 0x8000 +#define RBS_DBLCLKTOGGLE 0x00008000 +#define LVS_NOSORTHEADER 0x8000 +#define TVS_NOHSCROLL 0x8000 +#define TCS_FOCUSNEVER 0x8000 +#define SC_SIZE 0xF000 +#define SC_SEPARATOR 0xF00F +#define SC_MOVE 0xF010 +#define SC_MINIMIZE 0xF020 +#define SC_MAXIMIZE 0xF030 +#define SC_NEXTWINDOW 0xF040 +#define SC_PREVWINDOW 0xF050 +#define SC_CLOSE 0xF060 +#define SC_VSCROLL 0xF070 +#define SC_HSCROLL 0xF080 +#define SC_MOUSEMENU 0xF090 +#define SC_KEYMENU 0xF100 +#define SC_ARRANGE 0xF110 +#define SC_RESTORE 0xF120 +#define SC_TASKLIST 0xF130 +#define SC_SCREENSAVE 0xF140 +#define SC_HOTKEY 0xF150 +#define SC_DEFAULT 0xF160 +#define SC_MONITORPOWER 0xF170 +#define SC_CONTEXTHELP 0xF180 +#define LVS_TYPESTYLEMASK 0xfc00 +#define SPVERSION_MASK 0x0000FF00 +#define HTERROR -2 +#define PWR_FAIL -1 +#define UNICODE_NOCHAR 0xFFFF +#define HTTRANSPARENT -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/FakeGPS.Driver/Sensor.cpp b/src/FakeGPS.Driver/Sensor.cpp new file mode 100644 index 0000000..05bcf72 --- /dev/null +++ b/src/FakeGPS.Driver/Sensor.cpp @@ -0,0 +1,1993 @@ +// 2016 OK + +/*++ + +Module: + + Sensor.cpp + +Description: + + Implements the CSensor container class + +--*/ + +#include "Internal.h" +#include "SensorDDI.h" +#include "SensorManager.h" +#include "Sensor.h" +#include "Sensor.tmh" + +// TODO: Set the follow values so they are appropriate for the device. +#define LONG_REPORT_INTERVAL_MS 5 * 60000 // Number of milliseconds where a report interval + // is long enough that the device should be + // powered down. +#define DEVICE_STARTUP_MS 60000 // Average number of milliseconds it takes the + // device to find a location fix from a cold + // start with no assistance data. + +/*++ + +CSensor::CSensor + +Object constructor function + +--*/ +CSensor::CSensor() +{ + m_spSensorPropertyValues = nullptr; + m_spSensorDataFieldValues = nullptr; + m_fSensorInitialized = FALSE; + m_fValidDataEvent = FALSE; + + m_fSensorUpdated = FALSE; // flag checks to see if data report was received + m_fInitialDataReceived = FALSE; // flag checks to see if initial poll request was fulfilled + m_fReportingState = FALSE; // tracks whether sensor reporting is turned on or off + m_ulPowerState = 0; // tracks in what state sensor power should be + + m_spSupportedSensorDataFields = nullptr; + m_spSensorPropertyValues = nullptr; + + m_spSupportedSensorProperties = nullptr; + m_spSettableSensorProperties = nullptr; + m_spRequiredDataFields = nullptr; + + m_spSensorDataFieldValues = nullptr; + + m_fSensorInitialized = FALSE; // flag checks if we have been initialized + + m_fValidDataEvent = FALSE; // flag that indicates an event needs to be fired + +#if (NTDDI_VERSION >= NTDDI_WIN8) + m_ulCurrentGeolocationRadioState = DRS_RADIO_ON; + m_ulRequiredGeolocationRadioState = DRS_RADIO_ON; + m_ulPreviousGeolocationRadioState = DRS_RADIO_ON; +#endif + + m_spWdfDevice2 = nullptr; + + m_pThreadpool = nullptr; + SecureZeroMemory(&m_ThreadpoolEnvironment, sizeof(TP_CALLBACK_ENVIRON)); + m_pLongReportIntervalTimer = nullptr; + m_fUsingLongReportIntervalTimer = false; +} + +/*++ + +CSensor::~CSensor + + Object destructor function + +--*/ +CSensor::~CSensor() +{ + Uninitialize(); +} + +/*++ + +CSensor::Initialize + + Initializes the PROPERTYKEY/PROPVARIANT values for the Supported Properties & Supported Data + +--*/ +HRESULT CSensor::InitializeSensor( + _In_ SensorType sensType, + _In_ DWORD sensNum, + _In_ LPWSTR pwszManufacturer, + _In_ LPWSTR pwszProduct, + _In_ LPWSTR pwszSerialNumber, + _In_ LPWSTR pwszSensorID, + _In_ IWDFDevice* pWdfDevice) +{ + //CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + // Check if we are already initialized + HRESULT hr = (TRUE == IsInitialized()) ? E_UNEXPECTED : S_OK; + + if (SUCCEEDED(hr)) + { + hr = pWdfDevice->QueryInterface(IID_PPV_ARGS(&m_spWdfDevice2)); + } + + if (SUCCEEDED(hr)) + { + if (NULL == m_spSupportedSensorProperties) + { + // Create a new PortableDeviceKeyCollection to store the supported property KEYS + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &m_spSupportedSensorProperties); + } + + if (NULL == m_spSensorPropertyValues) + { + // Create a new PortableDeviceValues to store the property VALUES + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &m_spSensorPropertyValues); + } + + if (NULL == m_spSupportedSensorDataFields) + { + // Create a new PortableDeviceValues to store the supported datafield KEYS + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &m_spSupportedSensorDataFields); + } + + if (NULL == m_spRequiredDataFields) + { + // Create a new PortableDeviceValues to store the supported datafield KEYS + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &m_spRequiredDataFields); + } + + if (NULL == m_spSensorDataFieldValues) + { + // Create a new PortableDeviceValues to store the datafield VALUES + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &m_spSensorDataFieldValues); + } + + if (NULL == m_spSettableSensorProperties) + { + // Create a new PortableDeviceValues to store the settable property keys + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &m_spSettableSensorProperties); + } + } + + if (SUCCEEDED(hr)) + { + m_SensorType = sensType; + m_SensorNum = sensNum; + hr = StringCchCopy(m_pwszManufacturer, DESCRIPTOR_MAX_LENGTH, pwszManufacturer); + + if (SUCCEEDED(hr)) + { + hr = StringCchCopy(m_pwszProduct, DESCRIPTOR_MAX_LENGTH, pwszProduct); + } + + if (SUCCEEDED(hr)) + { + hr = StringCchCopy(m_pwszSerialNumber, DESCRIPTOR_MAX_LENGTH, pwszSerialNumber); + } + + if (SUCCEEDED(hr)) + { + hr = StringCchCopy(m_SensorID, DESCRIPTOR_MAX_LENGTH, pwszSensorID); + } + } + + // Initialize the Report Interval and Change Sensitivities + if (SUCCEEDED(hr)) + { + m_ulLowestClientReportInterval = ULONG_MAX; + + for (int idx = 0; idx < MAX_NUM_DATA_FIELDS; idx++) + { + m_fltLowestClientChangeSensitivities[idx] = FLT_MAX; + } + } + + if (SUCCEEDED(hr)) + { + m_fSensorInitialized = TRUE; + } + + // Create threadpool for long report interval timer + if (SUCCEEDED(hr)) + { + m_pThreadpool = CreateThreadpool(nullptr); + if (nullptr != m_pThreadpool) + { + SetThreadpoolThreadMaximum(m_pThreadpool, 1); + if (TRUE == SetThreadpoolThreadMinimum(m_pThreadpool, 1)) + { + InitializeThreadpoolEnvironment(&m_ThreadpoolEnvironment); + SetThreadpoolCallbackPool(&m_ThreadpoolEnvironment, m_pThreadpool); + } + else + { + CloseThreadpool(m_pThreadpool); + m_pThreadpool = nullptr; + hr = HRESULT_FROM_WIN32(GetLastError()); + Trace(TRACE_LEVEL_ERROR, "%!FUNC! Failed SetThreadpoolThreadMinimum, hr = %!HRESULT!", hr); + } + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + Trace(TRACE_LEVEL_ERROR, "%!FUNC! Failed to create thread pool, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr)) + { + m_pLongReportIntervalTimer = CreateThreadpoolTimer( + LongReportIntervalTimerCallback, // Timer callback + this, // Optional data to pass to callback + &m_ThreadpoolEnvironment // Callback environment + ); + if (nullptr == m_pLongReportIntervalTimer) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + Trace(TRACE_LEVEL_ERROR, "%!FUNC! Failed to create thread pool timer, hr = %!HRESULT!", hr); + } + } + } + + + return hr; +} + +/*++ + +CSensor::Uninitialize + + Initializes the PROPERTYKEY/PROPVARIANT values for the Supported Properties & Supported Data + +--*/ +HRESULT CSensor::Uninitialize() +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + // Check if we are already initialized + HRESULT hr = (FALSE == IsInitialized()) ? E_UNEXPECTED : S_OK; + + // Clean up threadpool + if (SUCCEEDED(hr)) + { + if (nullptr != m_pLongReportIntervalTimer) + { + // Stop timer, cancel any pending events, and close timer + SetThreadpoolTimer(m_pLongReportIntervalTimer, nullptr, 0, 0); + WaitForThreadpoolTimerCallbacks(m_pLongReportIntervalTimer, true); + CloseThreadpoolTimer(m_pLongReportIntervalTimer); + } + + if (nullptr != m_pThreadpool) + { + CloseThreadpool(m_pThreadpool); + } + + DestroyThreadpoolEnvironment(&m_ThreadpoolEnvironment); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorProperties->Clear(); + + if (SUCCEEDED(hr)) + { + hr = m_spSettableSensorProperties->Clear(); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->Clear(); + } + + if (SUCCEEDED(hr)) + { + hr = m_spRequiredDataFields->Clear(); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->Clear(); + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->Clear(); + } + + m_spSensorPropertyValues = NULL; + m_spSensorDataFieldValues = NULL; + m_spSupportedSensorProperties = NULL; + m_spSettableSensorProperties = NULL; + m_spSupportedSensorDataFields = NULL; + m_spRequiredDataFields = NULL; + + m_pClientMap.RemoveAll(); + m_pSubscriberMap.RemoveAll(); + + m_fSensorInitialized = FALSE; + } + + return hr; +} + +/*++ + +CSensor::GetSettableProperties + + Gets the list of SettableProperties for the device + + Returns a collection of PROPERTYKEYS + +--*/ +HRESULT CSensor::GetSettableProperties(IPortableDeviceKeyCollection **ppKeys) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == ppKeys) + { + hr = E_INVALIDARG; + } + + // Make sure we have been Initialized + if ((FALSE == IsInitialized()) || (NULL == m_spSettableSensorProperties)) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + hr = m_spSettableSensorProperties.CopyTo(ppKeys); + } + + return hr; +} + + +/*++ + +CSensor::GetSupportedProperties + + Gets the list of SupportedProperties for the device + + Returns a collection of PROPERTYKEYS + +--*/ +HRESULT CSensor::GetSupportedProperties(IPortableDeviceKeyCollection **ppKeys) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == ppKeys) + { + hr = E_INVALIDARG; + } + + // Make sure we have been Initialized + if ((FALSE == IsInitialized()) || (NULL == m_spSupportedSensorProperties)) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorProperties.CopyTo(ppKeys); + } + + return hr; +} + +/*++ + +CSensor::GetSupportedDataFields + + Gets the list of SupportedDataFields for the device + + Returns a collection of PROPERTYKEYS + +--*/ +HRESULT CSensor::GetSupportedDataFields(IPortableDeviceKeyCollection **ppKeys) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == ppKeys) + { + hr = E_INVALIDARG; + } + + // Make sure we have been Initialized + if ((FALSE == IsInitialized()) || (NULL == m_spSupportedSensorDataFields)) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields.CopyTo(ppKeys); + } + + return hr; +} + +/*++ + +CSensor::GetRequiredDataFields + + Gets the list of RequiredDataFields for the GPS sensor + + Returns a collection of PROPERTYKEYS + +--*/ +HRESULT CSensor::GetRequiredDataFields(IPortableDeviceKeyCollection **ppKeys) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == ppKeys) + { + hr = E_INVALIDARG; + } + + // Make sure we have been Initialized + if ((FALSE == IsInitialized()) || (NULL == m_spRequiredDataFields)) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + hr = m_spRequiredDataFields.CopyTo(ppKeys); + } + + return hr; +} + +/*++ + +CSensor::GetProperty + + Gets the Property Value for a given Property key. + + Called to retrive a property of the device + +--*/ +HRESULT CSensor::GetProperty(REFPROPERTYKEY key, PROPVARIANT *pValue) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == m_spSensorPropertyValues) + { + hr = E_UNEXPECTED; + } + else + { + // Retrieve the value + hr = m_spSensorPropertyValues->GetValue(key, pValue); + } + + return hr; +} + +/*++ + +CSensor::GetDataField + + Gets the Data Field Value for a given Property key. + + Called to retrieve a data field of the device + +--*/ +HRESULT CSensor::GetDataField(REFPROPERTYKEY key, PROPVARIANT *pValue) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == m_spSensorDataFieldValues) + { + hr = E_UNEXPECTED; + } + else + { + // Retrieve the value + hr = m_spSensorDataFieldValues->GetValue(key, pValue); + } + + return hr; +} + +/*++ + +CSensor::GetAllDataFieldValues + + Gets the all the Data values + + Called to retrieve all data field values of the device + +--*/ +HRESULT CSensor::GetAllDataFieldValues(IPortableDeviceValues* pValues) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == m_spSensorDataFieldValues) + { + hr = E_UNEXPECTED; + } + else + { + DWORD dwCount = 0; + hr = m_spSensorDataFieldValues->GetCount(&dwCount); + + if (SUCCEEDED(hr)) + { + for (DWORD i = 0; i < dwCount; i++) + { + PROPERTYKEY key; + PROPVARIANT var; + PropVariantInit(&var); + hr = m_spSensorDataFieldValues->GetAt(i, &key, &var); + + if (SUCCEEDED(hr)) + { + hr = pValues->SetValue(key, &var); + } + + PropVariantClear(&var); + } + } + + } + + return hr; +} + + +/*++ + +CSensor::SetProperty + + Sets the Property Value for a given Property key. + + Called during Initialize and when a property is read from the device + +--*/ +HRESULT CSensor::SetProperty(REFPROPERTYKEY key, const PROPVARIANT *pValue, IPortableDeviceValues* spDfSensVals) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (nullptr != pValue) + { + if (pValue->vt == VT_EMPTY) + { + // special case for initializing the property value + hr = m_spSensorPropertyValues->SetValue(key, pValue); + } + + else if (TRUE == IsEqualPropertyKey(key, SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL)) + { + // Validate Range for Report Interval + if (VT_UI4 == V_VT(pValue)) + { + ULONG ulReportInterval = V_UI4(pValue); + if (ulReportInterval < m_ulDefaultMinimumReportInterval) + { + hr = E_INVALIDARG; + Trace(TRACE_LEVEL_ERROR, "Desired report interval < current minimum for %s: input, hr = %!HRESULT!", m_SensorName, hr); + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetValue(key, pValue); + } + } + + else if (TRUE == IsEqualPropertyKey(key, SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY)) + { + // Validate Range for Location Desired Accuracy + if (VT_UI4 == V_VT(pValue)) + { + ULONG ulLocationDesiredAccuracy = V_UI4(pValue); + if (ulLocationDesiredAccuracy > LOCATION_DESIRED_ACCURACY_HIGH) + { + hr = E_INVALIDARG; + Trace(TRACE_LEVEL_ERROR, "Desired location desired accuracy > maximum for %s: input, hr = %!HRESULT!", m_SensorName, hr); + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetValue(key, pValue); + } + } + + else + { + hr = m_spSensorPropertyValues->SetValue(key, pValue); + } + } + else if (TRUE == IsEqualPropertyKey(key, SENSOR_PROPERTY_CHANGE_SENSITIVITY) && (nullptr != spDfSensVals)) + { + DWORD cDfVals = 0; + + if (SUCCEEDED(hr)) + { + hr = spDfSensVals->GetCount(&cDfVals); + + if (SUCCEEDED(hr)) + { + PROPERTYKEY pkDfKey; + PROPVARIANT var; + FLOAT fltSensitivity = 0; + + for (DWORD dwIdx = 0; dwIdx < cDfVals; dwIdx++) + { + if (SUCCEEDED(hr)) + { + PropVariantInit(&var); + hr = spDfSensVals->GetAt(dwIdx, &pkDfKey, &var); + if (SUCCEEDED(hr)) + { + fltSensitivity = var.fltVal; + } + PropVariantClear(&var); + } + + if (SUCCEEDED(hr)) + { + // Validate sensitivity for the specific data field + + if (fltSensitivity < 0.0F) + { + hr = E_INVALIDARG; + Trace(TRACE_LEVEL_ERROR, "Desired change sensitivity < 0.0 for %s: input, hr = %!HRESULT!", m_SensorName, hr); + } + else + { + hr = spDfSensVals->SetFloatValue(pkDfKey, fltSensitivity); + } + } + } + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_CHANGE_SENSITIVITY, spDfSensVals); + } + } + else + { + hr = E_UNEXPECTED; + } + + return hr; +} + +/*++ + +CSensor::SetDataField + + Sets the Data Field Value for a given DataField key. + + Called during Initialize and when data is read from the device + +--*/ +HRESULT CSensor::SetDataField(REFPROPERTYKEY key, const PROPVARIANT *pValue) +{ + CComCritSecLock scopeLock(m_CriticalSection); // Make this call thread safe + + HRESULT hr = S_OK; + + if (NULL == pValue) + { + hr = E_INVALIDARG; + } + + if (NULL == m_spSensorDataFieldValues) + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(key, pValue); + } + + return hr; +} + +/*++ + +CSensor::Subscribe + + Sets the status of event subscribers + +--*/ +HRESULT CSensor::Subscribe(_In_ IWDFFile* appID) +{ + CComCritSecLock scopeLock(m_CriticalSection); + + HRESULT hr = S_OK; + + if (nullptr != m_pSensorManager) + { + if (nullptr != m_pSensorManager->m_pSensorDDI) + { + BOOL fClientIsSubscribed = FALSE; + + hr = m_pSensorManager->m_pSensorDDI->CheckForSubscriber(this, appID, &fClientIsSubscribed); + + if (SUCCEEDED(hr) && (FALSE == fClientIsSubscribed)) + { + SUBSCRIBER_ENTRY entry; + + entry.fSubscribed = TRUE; + m_pSubscriberMap[appID] = entry; + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Subscriber %p is already present in %s, hr = %!HRESULT!", appID, m_SensorName, hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "m_pSensorDDI is NULL in %s, hr = %!HRESULT!", m_SensorName, hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "m_pSensorManager is NULL in %s, hr = %!HRESULT!", m_SensorName, hr); + } + + return hr; +} + + +/*++ + +CSensor::Unsubscribe + + Sets the status of event subscribers + +--*/ +HRESULT CSensor::Unsubscribe(_In_ IWDFFile* appID) +{ + CComCritSecLock scopeLock(m_CriticalSection); + + HRESULT hr = S_OK; + + if (nullptr != m_pSensorManager) + { + if (nullptr != m_pSensorManager->m_pSensorDDI) + { + BOOL fSubscriberIsPresent = FALSE; + + hr = m_pSensorManager->m_pSensorDDI->CheckForSubscriber(this, appID, &fSubscriberIsPresent); + + if (SUCCEEDED(hr) && (TRUE == fSubscriberIsPresent)) + { + if (SUCCEEDED(hr)) + { + size_t cEventSubscribers = m_pSubscriberMap.GetCount(); + if (cEventSubscribers > 0) + { + hr = m_pSensorManager->m_pSensorDDI->RemoveSubscriber(this, appID); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Attempt to reduce subscriber count below 0 in %s, AppID %p", m_SensorName, appID); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to remove subscriber %p from %s, hr = %!HRESULT!", appID, m_SensorName, hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Subscriber %p is not present in %s, hr = %!HRESULT!", appID, m_SensorName, hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "m_pSensorDDI is NULL in %s, hr = %!HRESULT!", m_SensorName, hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "m_pSensorManager is NULL in %s, hr = %!HRESULT!", m_SensorName, hr); + } + + return hr; +} + +/*++ + +CSensor::GetSubscriberCount + + Retrieves the status of event subscribers + +--*/ +DWORD CSensor::GetSubscriberCount(void) +{ + CComCritSecLock scopeLock(m_CriticalSection); + + return (DWORD) m_pSubscriberMap.GetCount(); +} + +/*++ + +CSensor::RemoveProperty + + Removes the PROPERTYKEY passed in from the list of Supported Properties + +--*/ +HRESULT CSensor::RemoveProperty(REFPROPERTYKEY key) +{ + CComCritSecLock scopeLock(m_CriticalSection); + + HRESULT hr = S_OK; + DWORD cKeys = 0; + PROPERTYKEY tempKey; + + if (NULL != m_spSupportedSensorProperties) + { + hr = m_spSupportedSensorProperties->GetCount(&cKeys); + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; ++dwIndex) + { + hr = m_spSupportedSensorProperties->GetAt(dwIndex, &tempKey); + + if (SUCCEEDED(hr)) + { + if (IsEqualPropertyKey(tempKey, key)) + { + hr = m_spSupportedSensorProperties->RemoveAt(dwIndex); + + if (SUCCEEDED(hr)) + hr = m_spSensorPropertyValues->RemoveValue(key); + + break; + } + } + } + } + } + + return hr; +} + +/*++ + +CSensor::RemoveDataField + + Removes the PROPERTYKEY passed in from the list of Supported DataFields + +--*/ +HRESULT CSensor::RemoveDataField(REFPROPERTYKEY key) +{ + CComCritSecLock scopeLock(m_CriticalSection); + + HRESULT hr = S_OK; + DWORD cKeys = 0; + PROPERTYKEY tempKey; + + if (NULL != m_spSupportedSensorDataFields) + { + hr = m_spSupportedSensorDataFields->GetCount(&cKeys); + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; ++dwIndex) + { + hr = m_spSupportedSensorDataFields->GetAt(dwIndex, &tempKey); + + if (SUCCEEDED(hr)) + { + if (IsEqualPropertyKey(tempKey, key)) + { + hr = m_spSupportedSensorDataFields->RemoveAt(dwIndex); + + if (SUCCEEDED(hr)) + hr = m_spSensorDataFieldValues->RemoveValue(key); + + break; + } + } + } + } + } + + return hr; +} + + +// Sets the timestamp when new data is updated +HRESULT CSensor::SetTimeStamp() +{ + CComCritSecLock scopeLock(m_CriticalSection); + + HRESULT hr = S_OK; + + PROPVARIANT var; + + // Get the current time as FILETIME format + FILETIME ft; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + GetSystemTimePreciseAsFileTime(&ft); // Use the higher resolution timer when available +#else + GetSystemTimeAsFileTime(&ft); // API not available, fallback to lower resolution timer +#endif + + hr = InitPropVariantFromFileTime(&ft, &var); + + if (SUCCEEDED(hr)) + { + hr = m_spSensorDataFieldValues->SetValue(SENSOR_DATA_TYPE_TIMESTAMP, &var); + } + + PropVariantClear(&var); + + return hr; +} + +// Called when new data is updated from device +VOID CSensor::RaiseDataEvent() +{ + CComCritSecLock scopeLock(m_CriticalSection); + + size_t cSubscribers = m_pSubscriberMap.GetCount(); + + // no wdk content + + // Check if there are any subscribers + if (0 < cSubscribers) + { + // Set the data event flag + m_fValidDataEvent = TRUE; + + if (NULL != m_hSensorEvent) + { + SetEvent(m_hSensorEvent); + } + } +} + +// Checks if there is a valid data event +// If so returns TRUE and resets the internal flag +BOOL CSensor::HasValidDataEvent() +{ + CComCritSecLock scopeLock(m_CriticalSection); + + BOOL fDataEvent = FALSE; + + // Check if there is a valid data event to post + if (TRUE == m_fValidDataEvent) + { + fDataEvent = m_fValidDataEvent; + m_fValidDataEvent = FALSE; + } + + return fDataEvent; +} + +// Sets the persistent unique ID property +// called if no overloaded version +HRESULT CSensor::SetUniqueID(_In_ IWDFDevice* pWdfDevice) +{ + HRESULT hr = S_OK; + + CComPtr spPropStore; + if (SUCCEEDED(hr)) + { + hr = pWdfDevice->RetrieveDevicePropertyStore(NULL, WdfPropertyStoreCreateIfMissing, &spPropStore, NULL); + } + + if (SUCCEEDED(hr)) + { + GUID idGuid; + + CComBSTR bstr(GetSensorObjectID()); + LPCWSTR lpcszKeyName = bstr; + + PROPVARIANT vID; + PropVariantInit(&vID); + hr = spPropStore->GetNamedValue(lpcszKeyName, &vID); + if (SUCCEEDED(hr)) + { + hr = ::CLSIDFromString(vID.bstrVal, &idGuid); + } + else + { + hr = ::CoCreateGuid(&idGuid); + if (SUCCEEDED(hr)) + { + LPOLESTR lpszGUID = NULL; + hr = ::StringFromCLSID(idGuid, &lpszGUID); + if (SUCCEEDED(hr)) + { + vID.vt = VT_LPWSTR; + vID.pwszVal = lpszGUID; + hr = spPropStore->SetNamedValue(lpcszKeyName, &vID); + } + } + } + + PropVariantClear(&vID); + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetGuidValue(SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID, idGuid); + } + } + + return hr; +} + +HRESULT CSensor::HandleReportIntervalUpdate() +{ + HRESULT hr = S_OK; + + ULONG ulRequestedReportInterval = 0; + ULONG ulDeviceReportInterval = 0; + ULONG ulRequiredReportIntervalAtApi = 0; + + BOOL fReportIntervalSupported = TRUE; + + PROPVARIANT var; + + PropVariantInit(&var); + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->GetValue(SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, &var); + } + + if (SUCCEEDED(hr)) + { + if (VT_EMPTY == var.vt) + { + if (fReportIntervalSupported) + { + if (ulRequestedReportInterval == 0) + { + ulRequestedReportInterval = m_ulDefaultCurrentReportInterval; + } + } + else + { + ulRequestedReportInterval = m_ulDefaultCurrentReportInterval; + } + } + else + { + hr = m_spSensorPropertyValues->GetUnsignedIntegerValue(SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, &ulRequestedReportInterval); + } + } + + if (SUCCEEDED(hr)) + { + // '0' to the sensor API means the default value + if (ulRequestedReportInterval == 0) + { + ulRequestedReportInterval = m_ulDefaultCurrentReportInterval; + } + else + { + ulRequestedReportInterval = m_ulLowestClientReportInterval; + } + + //pulReportInterval is the value we request from the device + if (ulRequestedReportInterval < 0xFFFFFFFF) + { + if (m_pSensorManager->m_fDeviceActive) + { + // Write the report interval value to the device and then read back what the device has set the report interval to before + // setting this value for the DeviceProperties and at the API + Trace(TRACE_LEVEL_INFORMATION, "Requesting Report Interval = %i on %s", ulRequestedReportInterval, m_SensorName); + + ulDeviceReportInterval = ulRequestedReportInterval; + Trace(TRACE_LEVEL_INFORMATION, "Device Report Interval = %i on %s", ulDeviceReportInterval, m_SensorName); + + // check to see this is valid and set it at the api + ulRequiredReportIntervalAtApi = ulDeviceReportInterval; + + if (SUCCEEDED(hr)) + { + // Set this possible changed value in DeviceProperties and at the API + + if (SUCCEEDED(hr)) + { + Trace(TRACE_LEVEL_INFORMATION, "Setting API Report Interval = %i on %s", ulRequiredReportIntervalAtApi, m_SensorName); + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, ulRequiredReportIntervalAtApi); + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to SetUsageValue = Sensor ReportInterval in %s, hr = %!HRESULT!", m_SensorName, hr); + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to set then get %s Feature Report, hr = %!HRESULT!", m_SensorName, hr); + } + } + else + { + Trace(TRACE_LEVEL_WARNING, "%!FUNC! Not updating report interval as device is not active"); + } + } + } + + PropVariantClear(&var); + + return hr; +} + +HRESULT CSensor::HandleLocationDesiredAccuracyUpdate() +{ + HRESULT hr = S_OK; + + ULONG ulRequestedLocationDesiredAccuracy = 0; + ULONG ulDeviceLocationDesiredAccuracy = 0; + ULONG ulRequiredLocationDesiredAccuracyAtApi = 0; + + BOOL fLocationDesiredAccuracySupported = TRUE; + + PROPVARIANT var; + + PropVariantInit(&var); + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->GetValue(SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY, &var); + } + + if (SUCCEEDED(hr)) + { + if (VT_EMPTY == var.vt) + { + if (fLocationDesiredAccuracySupported) + { + if (ulRequestedLocationDesiredAccuracy == 0) + { + ulRequestedLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + } + else + { + ulRequestedLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + } + else + { + hr = m_spSensorPropertyValues->GetUnsignedIntegerValue(SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY, &ulRequestedLocationDesiredAccuracy); + } + } + + if (SUCCEEDED(hr)) + { + // '0' to the sensor API means the default value + if (ulRequestedLocationDesiredAccuracy > LOCATION_DESIRED_ACCURACY_HIGH) + { + ulRequestedLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + else + { + ulRequestedLocationDesiredAccuracy = m_ulLowestClientLocationDesiredAccuracy; + } + + //pulLocationDesiredAccuracy is the value we request from the device + if (ulRequestedLocationDesiredAccuracy <= LOCATION_DESIRED_ACCURACY_HIGH) + { + // Only use the device if powered on + if (m_pSensorManager->m_fDeviceActive) + { + // Write the location desired accuracy value to the device and then read back what the device has set the report interval to before + // setting this value for the DeviceProperties and at the API + Trace(TRACE_LEVEL_INFORMATION, "Requesting Location Desired Accuracy = %i on %s", ulRequestedLocationDesiredAccuracy, m_SensorName); + + ulDeviceLocationDesiredAccuracy = ulRequestedLocationDesiredAccuracy; + Trace(TRACE_LEVEL_INFORMATION, "Device Location Desired Accuracy = %i on %s", ulDeviceLocationDesiredAccuracy, m_SensorName); + + // check to see this is valid and set it at the api + ulRequiredLocationDesiredAccuracyAtApi = ulDeviceLocationDesiredAccuracy; + + if (SUCCEEDED(hr)) + { + // Set this possible changed value in DeviceProperties and at the API + + if (SUCCEEDED(hr)) + { + Trace(TRACE_LEVEL_INFORMATION, "Setting API Location Desired Accuracy = %i on %s", ulRequiredLocationDesiredAccuracyAtApi, m_SensorName); + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY, ulRequiredLocationDesiredAccuracyAtApi); + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to SetUsageValue = Sensor LocationDesiredAccuracy in %s, hr = %!HRESULT!", m_SensorName, hr); + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to set then get %s Feature Report, hr = %!HRESULT!", m_SensorName, hr); + } + } + else + { + Trace(TRACE_LEVEL_WARNING, "%!FUNC! Not updating desired accuracy as device is not active"); + } + } + } + + PropVariantClear(&var); + + return hr; +} + +HRESULT CSensor::HandleGeolocationRadioStateUpdate() +{ + HRESULT hr = S_OK; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + bool fStateChanged = false; + /******************************************************************************* + NOTE: + + It is at this point that the GPS implementer must handle the power setting of + the GPS device. The state of m_ulRequiredGeolocationRadioState can be either: + - DRS_SW_RADIO_OFF, in which case the power to the GPS radio should be removed + - DRS_RADIO_ON, in which case the power to the GPS radio should be restored + + *******************************************************************************/ + if (m_ulCurrentGeolocationRadioState != m_ulRequiredGeolocationRadioState) + { + m_ulCurrentGeolocationRadioState = m_ulRequiredGeolocationRadioState; + + size_t cClients = m_pClientMap.GetCount(); + + if (DRS_RADIO_ON == m_ulRequiredGeolocationRadioState) + { + Trace(TRACE_LEVEL_INFORMATION, "Turning GPS device radio state on %s", m_SensorName); + m_pSensorManager->SetState(this, SENSOR_STATE_INITIALIZING, &fStateChanged); + + if (cClients > 0) + { + Trace(TRACE_LEVEL_INFORMATION, "Calling StopIdle so the device will remain in D0 as long as there are connected clients and the radio is on"); + m_spWdfDevice2->StopIdle(FALSE); + } + } + else if (DRS_SW_RADIO_OFF == m_ulRequiredGeolocationRadioState) + { + Trace(TRACE_LEVEL_INFORMATION, "Turning GPS device radio state off %s", m_SensorName); + m_pSensorManager->SetState(this, SENSOR_STATE_NOT_AVAILABLE, &fStateChanged); + + if (cClients > 0) + { + Trace(TRACE_LEVEL_INFORMATION, "Calling ResumeIdle so the device will remain in Dx as long as there are no connected clients or the radio is off"); + m_spWdfDevice2->ResumeIdle(); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_INFORMATION, "Unsupported radio state requested for %s", m_SensorName); + } + + if (SUCCEEDED(hr)) + { + Trace(TRACE_LEVEL_INFORMATION, "Setting Radio State = %i on %s", m_ulRequiredGeolocationRadioState, m_SensorName); + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_RADIO_STATE, m_ulRequiredGeolocationRadioState); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to set Radio State %s, hr = %!HRESULT!", m_SensorName, hr); + } + else + { + // Update the store + CComPtr spPropStore; + hr = m_spWdfDevice2->RetrieveDevicePropertyStore(NULL, WdfPropertyStoreCreateIfMissing, &spPropStore, NULL); + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + PropVariantInit(&var); + var.vt = VT_UI4; + var.ulVal = m_ulRequiredGeolocationRadioState; + hr = spPropStore->SetNamedValue(PROP_STORE_KEY_RADIO_STATE, &var); + PropVariantClear(&var); + } + else + { + Trace(TRACE_LEVEL_ERROR, "RetrieveDevicePropertyStore failed for radio state, hr = %!HRESULT!", hr); + } + } + } + } + + if (SUCCEEDED(hr)) + { + Trace(TRACE_LEVEL_INFORMATION, "Setting Previous Radio State = %i on %s", m_ulPreviousGeolocationRadioState, m_SensorName); + hr = m_spSensorPropertyValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_RADIO_STATE_PREVIOUS, m_ulPreviousGeolocationRadioState); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to set Previous Radio State %s, hr = %!HRESULT!", m_SensorName, hr); + } + else + { + // Update the store + CComPtr spPropStore; + hr = m_spWdfDevice2->RetrieveDevicePropertyStore(NULL, WdfPropertyStoreCreateIfMissing, &spPropStore, NULL); + if (SUCCEEDED(hr)) + { + PROPVARIANT var; + PropVariantInit(&var); + var.vt = VT_UI4; + var.ulVal = m_ulPreviousGeolocationRadioState; + hr = spPropStore->SetNamedValue(PROP_STORE_KEY_PREVIOUS_RADIO_STATE, &var); + PropVariantClear(&var); + } + else + { + Trace(TRACE_LEVEL_ERROR, "RetrieveDevicePropertyStore failed for previous radio state, hr = %!HRESULT!", hr); + } + } + } +#endif + + return hr; +} + + +/*++ + +CCompass::GetPropertyValuesForSensorObject + + This method is called to populate property values for the object specified. + Read the specified properties for the specified object and populate pValues with the results. + +Parameter: + + wszObjectID - the object whose properties are being requested. + pKeys - the list of property keys of the properties to request from the object + pValues - an IPortableDeviceValues which will contain the property values retreived from the object + +--*/ +HRESULT CSensor::GetPropertyValuesForSensorObject( + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _In_ IPortableDeviceValues* pValues, + _In_ LPCWSTR wszSensorName, + _In_ GUID guidSensorCategory, + _Out_ BOOL* pfError) +{ + HRESULT hr = S_OK; + CAtlStringW strObjectID = wszObjectID; + DWORD cKeys = 0; + + if ((wszObjectID == NULL) || + (pKeys == NULL) || + (pValues == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + *pfError = FALSE; + + hr = pKeys->GetCount(&cKeys); + + if ((NULL == m_spSensorPropertyValues) || (NULL == m_spSensorDataFieldValues)) + { + hr = E_POINTER; + } + + if (hr == S_OK) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + hr = pKeys->GetAt(dwIndex, &Key); + + if (hr == S_OK && !IsEqualPropertyKey(Key, WPD_PROPERTY_NULL)) + { + // Preset the property value to 'error not supported'. The actual value + // will replace this value, if read from the device. + pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + + // Set general properties for sensor + if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_ID, m_SensorID); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + hr = pValues->SetStringValue(WPD_OBJECT_NAME, wszSensorName); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, m_SensorID); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, WPD_DEVICE_OBJECT_ID); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE); + } + else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY)) + { + hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, guidSensorCategory); + } + else + { + // Get sensor properties + PROPVARIANT value; + PropVariantInit(&value); + + HRESULT hrTemp = GetProperty(Key, &value); + + if (SUCCEEDED(hrTemp)) + { + pValues->SetValue(Key, &value); + } + else + { + // Failed to find the requested property, convey the hr back to the caller + pValues->SetErrorValue(Key, hrTemp); + *pfError = TRUE; + } + + PropVariantClear(&value); + + } + } + } + } + + return hr; +} + +HRESULT CSensor::InitPerDataFieldProperties(_In_ PROPERTYKEY pkDataField) +{ + HRESULT hr = S_OK; + + // Change sensitivity + + if (SUCCEEDED(hr)) + { + CComPtr spSensitivityValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = m_spSensorPropertyValues->GetIPortableDeviceValuesValue(SENSOR_PROPERTY_CHANGE_SENSITIVITY, &spSensitivityValues); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if (pkDataField == datakey) + { + hr = spSensitivityValues->SetFloatValue(datakey, m_fltDefaultChangeSensitivity); + } + } + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_CHANGE_SENSITIVITY, spSensitivityValues); + } + } + else + { + hr = E_POINTER; + } + + // Range Maximum + if (SUCCEEDED(hr)) + { + CComPtr spMaximumValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = m_spSensorPropertyValues->GetIPortableDeviceValuesValue(SENSOR_PROPERTY_RANGE_MAXIMUM, &spMaximumValues); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if (pkDataField == datakey) + { + hr = spMaximumValues->SetFloatValue(datakey, m_fltDefaultRangeMaximum); + } + } + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_RANGE_MAXIMUM, spMaximumValues); + } + } + + if (SUCCEEDED(hr)) + { + CComPtr spMinimumValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = m_spSensorPropertyValues->GetIPortableDeviceValuesValue(SENSOR_PROPERTY_RANGE_MINIMUM, &spMinimumValues); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if (pkDataField == datakey) + { + hr = spMinimumValues->SetFloatValue(datakey, m_fltDefaultRangeMinimum); + } + } + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_RANGE_MINIMUM, spMinimumValues); + } + } + + if (SUCCEEDED(hr)) + { + CComPtr spAccuracyValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = m_spSensorPropertyValues->GetIPortableDeviceValuesValue(SENSOR_PROPERTY_ACCURACY, &spAccuracyValues); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if (pkDataField == datakey) + { + hr = spAccuracyValues->SetFloatValue(datakey, m_fltDefaultAccuracy); + } + } + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_ACCURACY, spAccuracyValues); + } + } + + if (SUCCEEDED(hr)) + { + CComPtr spResolutionValues; + PROPERTYKEY datakey; + DWORD uDatafieldCount = 0; + + hr = m_spSensorPropertyValues->GetIPortableDeviceValuesValue(SENSOR_PROPERTY_RESOLUTION, &spResolutionValues); + + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetCount(&uDatafieldCount); + } + + if (SUCCEEDED(hr)) + { + // Only set the default if the data field is supported + for (DWORD j = 0; j < uDatafieldCount; j++) + { + if (SUCCEEDED(hr)) + { + hr = m_spSupportedSensorDataFields->GetAt(j, &datakey); + } + + if (SUCCEEDED(hr)) + { + if (pkDataField == datakey) + { + hr = spResolutionValues->SetFloatValue(datakey, m_fltDefaultResolution); + } + } + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_RESOLUTION, spResolutionValues); + } + } + + return hr; +} + +HRESULT CSensor::HandleChangeSensitivityUpdate() +{ + HRESULT hr = S_OK; + + FLOAT fltDesiredDatafieldSensitivity = 0.0F; + FLOAT fltDeviceDatafieldSensitivity = 0.0F; + FLOAT fltRequiredDatafieldSensitivityAtApi = 0.0F; + + BOOL fDatafieldSupported = TRUE; + + CComPtr spSensitivityValues; + + if (TRUE == fDatafieldSupported) + { + hr = m_spSensorPropertyValues->GetIPortableDeviceValuesValue(SENSOR_PROPERTY_CHANGE_SENSITIVITY, &spSensitivityValues); + + if (SUCCEEDED(hr)) + { + DWORD cDatafields = 0; + PROPERTYKEY pkDatafield; + + hr = m_spSupportedSensorDataFields->GetCount(&cDatafields); + + if (SUCCEEDED(hr)) + { + for (DWORD idx = 1; idx < cDatafields; idx++) //start with 1 to bypass timestamp + { + if (SUCCEEDED(hr)) + { + fltDesiredDatafieldSensitivity = m_fltLowestClientChangeSensitivities[idx]; + m_spSupportedSensorDataFields->GetAt(idx, &pkDatafield); + + if (m_pSensorManager->m_fDeviceActive) + { + // Write the change sensitivity value to the device and then read back what the device has set the report interval to before + Trace(TRACE_LEVEL_INFORMATION, "Requesting %s Change Sensitivity for Key = %!GUID!-%i was set = %f", m_SensorName, &pkDatafield.fmtid, pkDatafield.pid, fltDesiredDatafieldSensitivity); + fltDeviceDatafieldSensitivity = fltDesiredDatafieldSensitivity; + + // device responds with value it can be set to + fltRequiredDatafieldSensitivityAtApi = fltDeviceDatafieldSensitivity; + Trace(TRACE_LEVEL_INFORMATION, "%s set Change Sensitivity for Key = %!GUID!-%i was set = %f", m_SensorName, &pkDatafield.fmtid, pkDatafield.pid, fltDesiredDatafieldSensitivity); + + if ((SUCCEEDED(hr) && (TRUE == fDatafieldSupported))) + { + Trace(TRACE_LEVEL_INFORMATION, "%s Change Sensitivity for Key = %!GUID!-%i was set = %f", m_SensorName, &pkDatafield.fmtid, pkDatafield.pid, fltRequiredDatafieldSensitivityAtApi); + hr = spSensitivityValues->SetFloatValue(pkDatafield, fltRequiredDatafieldSensitivityAtApi); + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to SetUsageValue = Sensor Sensitivity in %s, hr = %!HRESULT!", m_SensorName, hr); + } + } + else + { + Trace(TRACE_LEVEL_WARNING, "%!FUNC! Not updating change sensitivity as device is not active"); + } + + } + } + } + + if (SUCCEEDED(hr)) + { + hr = m_spSensorPropertyValues->SetIPortableDeviceValuesValue(SENSOR_PROPERTY_CHANGE_SENSITIVITY, spSensitivityValues); + } + } + } + + return hr; +} + +HRESULT CSensor::HandleSetReportingAndPowerStates() +{ + HRESULT hr = S_OK; + + BOOL fReportingStateSupported = TRUE; + BOOL fPowerStateSupported = TRUE; + + if (m_pSensorManager->m_fDeviceActive) + { + if (SUCCEEDED(hr) && (TRUE == fReportingStateSupported)) + { + if (TRUE == m_fReportingState) + { + Trace(TRACE_LEVEL_INFORMATION, "Requesting %s Reporting State = ALL_EVENTS", m_SensorName); + //turn on event reporting at the device + Trace(TRACE_LEVEL_INFORMATION, "%s Reporting State = ALL_EVENTS", m_SensorName); + } + else + { + Trace(TRACE_LEVEL_INFORMATION, "Requesting %s Reporting State = NO_EVENTS", m_SensorName); + //turn off event reporting at the device + Trace(TRACE_LEVEL_INFORMATION, "%s Reporting State = NO_EVENTS", m_SensorName); + } + } + + if (SUCCEEDED(hr) && (TRUE == fPowerStateSupported)) + { + if (SENSOR_POWER_STATE_FULL_POWER == m_ulPowerState) + { + Trace(TRACE_LEVEL_INFORMATION, "Requesting %s Power State = FULL_POWER", m_SensorName); + // set power state to full power at the device + Trace(TRACE_LEVEL_INFORMATION, "%s Power State = FULL_POWER", m_SensorName); + } + else if (SENSOR_POWER_STATE_LOW_POWER == m_ulPowerState) + { + Trace(TRACE_LEVEL_INFORMATION, "Requesting %s Power State = LOW_POWER", m_SensorName); + // set power state to low power at the device + Trace(TRACE_LEVEL_INFORMATION, "%s Power State = LOW_POWER", m_SensorName); + } + else if (SENSOR_POWER_STATE_POWER_OFF == m_ulPowerState) + { + Trace(TRACE_LEVEL_INFORMATION, "Requesting %s Power State = POWER_OFF", m_SensorName); + // set power state to power off at the device + Trace(TRACE_LEVEL_INFORMATION, "%s Power State = POWER_OFF", m_SensorName); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Unrecognized %s power state, hr = %!HRESULT!", m_SensorName, hr); + } + } + } + else + { + Trace(TRACE_LEVEL_WARNING, "%!FUNC! Not updating reporting or power states as device is not active"); + } + + if (SUCCEEDED(hr)) + { + hr = S_OK; + } + + return hr; +} + + +FLOAT CSensor::GetRangeMaximumValue( + _In_ FLOAT fltDefaultRangeMaximum, + _In_ BOOL fSpecificMaximumSupported, + _In_ FLOAT fltSpecificMaximum, + _In_ BOOL fBulkMaximumSupported, + _In_ FLOAT fltBulkMaximum, + _In_ BOOL fGlobalMaximumSupported, + _In_ FLOAT fltGlobalMaximum) +{ + FLOAT fltMaximum = fltDefaultRangeMaximum; + + //range check against max usage specified by sensor + if (TRUE == fSpecificMaximumSupported) + { + fltMaximum = fltSpecificMaximum; + } + else if (TRUE == fBulkMaximumSupported) + { + fltMaximum = fltBulkMaximum; + } + else if (TRUE == fGlobalMaximumSupported) + { + fltMaximum = fltGlobalMaximum; + } + else + { + //do nothing + } + + return fltMaximum; +} + + +FLOAT CSensor::GetRangeMinimumValue( + _In_ FLOAT fltDefaultRangeMinimum, + _In_ BOOL fSpecificMinimumSupported, + _In_ FLOAT fltSpecificMinimum, + _In_ BOOL fBulkMinimumSupported, + _In_ FLOAT fltBulkMinimum, + _In_ BOOL fGlobalMinimumSupported, + _In_ FLOAT fltGlobalMinimum) +{ + FLOAT fltMinimum = fltDefaultRangeMinimum; + + //range check against min usage specified by sensor + if (TRUE == fSpecificMinimumSupported) + { + fltMinimum = fltSpecificMinimum; + } + else if (TRUE == fBulkMinimumSupported) + { + fltMinimum = fltBulkMinimum; + } + else if (TRUE == fGlobalMinimumSupported) + { + fltMinimum = fltGlobalMinimum; + } + else + { + //do nothing + } + + return fltMinimum; +} + +VOID CSensor::CheckLongReportIntervalTimer() +{ + if (m_ulLowestClientReportInterval >= LONG_REPORT_INTERVAL_MS) + { + StartLongReportIntervalTimer(); + } + else + { + StopLongReportIntervalTimer(); + } +} + +VOID CSensor::StartLongReportIntervalTimer() +{ + CComCritSecLock scopeLock(m_CriticalSectionLongReportInterval); + + if (FALSE == IsThreadpoolTimerSet(m_pLongReportIntervalTimer)) + { + m_fUsingLongReportIntervalTimer = true; + + m_spWdfDevice2->ResumeIdle(); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Calling ResumeIdle"); + + // Convert miliseconds to filetime. Filetime is in 100ns units. + // Negative means relative future time. + FILETIME fileTimeDue = { 0 }; + *reinterpret_cast(&fileTimeDue) = -static_cast((LONGLONG) 10000 * (LONGLONG) (m_ulLowestClientReportInterval - DEVICE_STARTUP_MS)); + + SetThreadpoolTimer( + m_pLongReportIntervalTimer, // Thread pool timer + &fileTimeDue, // Due time for timer + 0, // Period time, 0 is only signaled once + 100 // Allow delay of up to this many miliseconds on the timer callback for performance savings + ); + } +} + +VOID CSensor::StopLongReportIntervalTimer() +{ + CComCritSecLock scopeLock(m_CriticalSectionLongReportInterval); + + // Always stop timer + SetThreadpoolTimer(m_pLongReportIntervalTimer, nullptr, 0, 0); + + if (m_fUsingLongReportIntervalTimer) + { + StopIdleForLongReportIntervalTimer(); + } +} + +VOID CSensor::StopIdleForLongReportIntervalTimer() +{ + m_spWdfDevice2->StopIdle(FALSE); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Calling StopIdle"); + + m_fUsingLongReportIntervalTimer = false; +} + +VOID CALLBACK CSensor::LongReportIntervalTimerCallback( + _Inout_ PTP_CALLBACK_INSTANCE pInstance, + _Inout_opt_ PVOID pContext, + _Inout_ PTP_TIMER pTimer + ) +{ + UNREFERENCED_PARAMETER(pInstance); + UNREFERENCED_PARAMETER(pTimer); + + CSensor* pThis = static_cast(pContext); + if (nullptr != pThis) + { + CComCritSecLock scopeLock(pThis->m_CriticalSectionLongReportInterval); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Hit timer callback"); + pThis->StopIdleForLongReportIntervalTimer(); + SetThreadpoolTimer(pThis->m_pLongReportIntervalTimer, nullptr, 0, 0); + } +} \ No newline at end of file diff --git a/src/FakeGPS.Driver/Sensor.h b/src/FakeGPS.Driver/Sensor.h new file mode 100644 index 0000000..547e3b3 --- /dev/null +++ b/src/FakeGPS.Driver/Sensor.h @@ -0,0 +1,183 @@ +// 2016 OK + +/*++ + +Module: + + Sensor.h + +Description: + + Defines the CSensor container class + +--*/ + + +#pragma once + + +class CSensor; // forward declaration + +class CSensor +{ +protected: + CSensorManager* m_pSensorManager; + +public: + CSensor(); + ~CSensor(); + + HRESULT InitializeSensor( + _In_ SensorType sensType, + _In_ DWORD sensNum, + _In_ LPWSTR pwszManufacturer, + _In_ LPWSTR pwszProduct, + _In_ LPWSTR pwszSerialNumber, + _In_ LPWSTR sensorID, + _In_ IWDFDevice* pWdfDevice); + + HRESULT Uninitialize(VOID); + + HRESULT GetSupportedProperties(IPortableDeviceKeyCollection **ppKeys); + HRESULT GetSupportedDataFields(IPortableDeviceKeyCollection **ppKeys); + HRESULT GetRequiredDataFields(IPortableDeviceKeyCollection **ppKeys); + + HRESULT GetSettableProperties(IPortableDeviceKeyCollection **ppKeys); + + DWORD GetSubscriberCount(void); + SensorType GetSensorType(void) { return m_SensorType; } + ULONG GetSensorNumber(void) { return m_SensorNum; } + CComBSTR GetSensorObjectID(void) { return m_pSensorManager->m_AvailableSensorsIDs[m_SensorNum]; } + + HRESULT GetProperty(REFPROPERTYKEY key, PROPVARIANT *pValue); + HRESULT GetDataField(REFPROPERTYKEY key, PROPVARIANT *pValue); + HRESULT GetAllDataFieldValues(IPortableDeviceValues* pValues); + + HRESULT SetProperty(REFPROPERTYKEY key, const PROPVARIANT *pValue, IPortableDeviceValues* spDfVals); + HRESULT SetDataField(REFPROPERTYKEY key, const PROPVARIANT *pValue); + HRESULT SetTimeStamp(); + + HRESULT Subscribe(_In_ IWDFFile* appID); + HRESULT Unsubscribe(_In_ IWDFFile* appID); + + HRESULT RemoveProperty(REFPROPERTYKEY key); + HRESULT RemoveDataField(REFPROPERTYKEY key); + + BOOL IsInitialized(VOID) { return m_fSensorInitialized; } + + VOID RaiseDataEvent(); + BOOL HasValidDataEvent(); + + VOID SetDataEventHandle(HANDLE hEvent) { m_hSensorEvent = hEvent; } + + virtual HRESULT SetUniqueID(_In_ IWDFDevice* pWdfDevice); + + HRESULT InitPerDataFieldProperties( + _In_ PROPERTYKEY pkDataField); + + HRESULT GetPropertyValuesForSensorObject( + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _In_ IPortableDeviceValues* pValues, + _In_ LPCWSTR wszSensorName, + _In_ GUID guidSensorCategory, + _Out_ BOOL* pfError); + + FLOAT GetRangeMaximumValue( + _In_ FLOAT fltDefaultRangeMaximum, + _In_ BOOL fSpecificMaximumSupported, + _In_ FLOAT fltSpecificMaximum, + _In_ BOOL fBulkMaximumSupported, + _In_ FLOAT fltBulkMaximum, + _In_ BOOL fGlobalMaximumSupported, + _In_ FLOAT fltGlobalMaximum); + + FLOAT GetRangeMinimumValue( + _In_ FLOAT fltDefaultRangeMinimum, + _In_ BOOL fSpecificMinimumSupported, + _In_ FLOAT fltSpecificMinimum, + _In_ BOOL fBulkMinimumSupported, + _In_ FLOAT fltBulkMinimum, + _In_ BOOL fGlobalMinimumSupported, + _In_ FLOAT fltGlobalMinimum); + + static VOID CALLBACK LongReportIntervalTimerCallback( + _Inout_ PTP_CALLBACK_INSTANCE pInstance, + _Inout_opt_ PVOID pContext, + _Inout_ PTP_TIMER pTimer); + + VOID CheckLongReportIntervalTimer(); + VOID StartLongReportIntervalTimer(); + VOID StopLongReportIntervalTimer(); + VOID StopIdleForLongReportIntervalTimer(); + + + HRESULT HandleReportIntervalUpdate(); + HRESULT HandleLocationDesiredAccuracyUpdate(); + HRESULT HandleGeolocationRadioStateUpdate(); + HRESULT HandleChangeSensitivityUpdate(); + HRESULT HandleSetReportingAndPowerStates(); + + WCHAR m_pwszManufacturer[DESCRIPTOR_MAX_LENGTH]; + WCHAR m_pwszProduct[DESCRIPTOR_MAX_LENGTH]; + WCHAR m_pwszSerialNumber[DESCRIPTOR_MAX_LENGTH]; + WCHAR m_SensorID[DESCRIPTOR_MAX_LENGTH]; + CHAR m_SensorName[DESCRIPTOR_MAX_LENGTH]; + + CLIENT_MAP m_pClientMap; + SUBSCRIBER_MAP m_pSubscriberMap; + + FLOAT m_fltLowestClientChangeSensitivities[MAX_NUM_DATA_FIELDS]; + ULONG m_ulLowestClientReportInterval; + ULONG m_ulLowestClientLocationDesiredAccuracy; + ULONG m_ulCurrentGeolocationRadioState; + ULONG m_ulRequiredGeolocationRadioState; + ULONG m_ulPreviousGeolocationRadioState; + + FLOAT m_fltDefaultChangeSensitivity; + FLOAT m_fltDefaultRangeMaximum; + FLOAT m_fltDefaultRangeMinimum; + FLOAT m_fltDefaultAccuracy; + FLOAT m_fltDefaultResolution; + + ULONG m_ulDefaultCurrentReportInterval; + ULONG m_ulDefaultMinimumReportInterval; + + BOOL m_fSensorUpdated; // flag checks to see if data report was received + BOOL m_fInitialDataReceived; // flag checks to see if inital poll request was fulfilled + BOOL m_fReportingState; // tracks whether sensor reporting is turned on or off + ULONG m_ulPowerState; // tracks in what state sensor power should be + + ULONGLONG m_ullInitialEventTime; + ULONG m_ulEventCount; + + PTP_POOL m_pThreadpool; + TP_CALLBACK_ENVIRON m_ThreadpoolEnvironment; + PTP_TIMER m_pLongReportIntervalTimer; + CComAutoCriticalSection m_CriticalSectionLongReportInterval; // Critical section to synchronize long report interval timer + bool m_fUsingLongReportIntervalTimer; + +public: + + // PropertyKeys + CComPtr m_spSupportedSensorProperties; + CComPtr m_spSettableSensorProperties; + CComPtr m_spSupportedSensorDataFields; + CComPtr m_spRequiredDataFields; + + // Values + CComPtr m_spSensorPropertyValues; + CComPtr m_spSensorDataFieldValues; + + CComAutoCriticalSection m_CriticalSection; // This is used to make all calls to get/set properties thread safe + + BOOL m_fSensorInitialized; // flag checks if we have been initialized + + SensorType m_SensorType; + ULONG m_SensorNum; + + HANDLE m_hSensorEvent; // Handle to the Eventing thread proc + BOOL m_fValidDataEvent; // flag that indicates an event needs to be fired + + CComPtr m_spWdfDevice2; +}; \ No newline at end of file diff --git a/src/FakeGPS.Driver/SensorDDI.cpp b/src/FakeGPS.Driver/SensorDDI.cpp new file mode 100644 index 0000000..cffa5b3 --- /dev/null +++ b/src/FakeGPS.Driver/SensorDDI.cpp @@ -0,0 +1,3209 @@ +// 2016 OK + +/*++ + +Module: + + SensorDDI.cpp + +Description: + + This module implements the ISensorDriver interface which is used + by the Sensor Class Extension. +--*/ + +#include "Internal.h" +#include "SensorManager.h" +#include "devpkey.h" +#include "Sensor.h" +#include "GeolocationSensor.h" +#include "SensorDDI.h" +#include "SensorDDI.tmh" + +const PROPERTYKEY g_SupportedCommonProperties [] = +{ + WPD_OBJECT_ID, + WPD_OBJECT_PERSISTENT_UNIQUE_ID, + WPD_OBJECT_PARENT_ID, + WPD_OBJECT_NAME, + WPD_OBJECT_FORMAT, + WPD_OBJECT_CONTENT_TYPE, + WPD_OBJECT_CAN_DELETE +}; + +const PROPERTYKEY g_SupportedDeviceProperties [] = +{ + WPD_DEVICE_FIRMWARE_VERSION, + WPD_DEVICE_POWER_LEVEL, + WPD_DEVICE_POWER_SOURCE, + WPD_DEVICE_PROTOCOL, + WPD_DEVICE_MODEL, + WPD_DEVICE_SERIAL_NUMBER, + WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, + WPD_DEVICE_MANUFACTURER, + WPD_DEVICE_FRIENDLY_NAME, + WPD_DEVICE_TYPE, + WPD_FUNCTIONAL_OBJECT_CATEGORY +}; + + +/*++ + +CSensorDDI::CSensorDDI() + + Constructor. + +--*/ +CSensorDDI::CSensorDDI() +{ +} + +/*++ + +CSensorDDI::~CSensorDDI() + + Destructor + +--*/ +CSensorDDI::~CSensorDDI() +{ +} + +/*++ + +CSensorDDI::InitSensorDevice + +--*/ +HRESULT CSensorDDI::InitSensorDevice(_In_ IWDFDevice* pWdfDevice) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = (nullptr != pWdfDevice) ? S_OK : E_UNEXPECTED; + + // NOTE: This is where the hardware should be configured for first (or restarted) use + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} + +/*++ + +CSensorDDI::DeInitSensorDevice + +--*/ +HRESULT CSensorDDI::DeInitSensorDevice() +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + // NOTE: Any device configuration should be stored here and reused on InitSensorDevice + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} + +/*++ + +CSensorDDI::UpdateSensorValues + +--*/ +HRESULT CSensorDDI::UpdateSensorPropertyValues( + _In_ LPWSTR wszObjectID, + _In_ BOOL fSettableOnly) +{ + UNREFERENCED_PARAMETER(fSettableOnly); + + HRESULT hr = S_OK; + CSensor* pSensor = NULL; + SensorType sensType = SensorTypeNone; + DWORD sensIndex = 0; + + hr = FindSensorTypeFromObjectID((LPWSTR) wszObjectID, &sensType, &sensIndex); + + if (SUCCEEDED(hr)) + { + if (m_pSensorManager->m_pSensorList.GetCount() > 0) + { + hr = GetSensorObject(sensIndex, &pSensor); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "No sensors found, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + CGeolocationSensor* pGeolocation = nullptr; + + switch (sensType) + { + case Geolocation: + pGeolocation = static_cast(pSensor); + hr = pGeolocation->UpdateGeolocationPropertyValues(); + break; + + default: + hr = E_UNEXPECTED; + break; + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get pSensor, hr = %!HRESULT!", hr); + } + } + + return hr; +} + +/*++ + +CSensorDDI::UpdateSensorValues + +--*/ +HRESULT CSensorDDI::UpdateSensorDataFieldValues(_In_ LPWSTR wszObjectID) +{ + HRESULT hr = S_OK; + CSensor* pSensor = nullptr; + SensorType sensType = SensorTypeNone; + DWORD sensIndex = 0; + + hr = FindSensorTypeFromObjectID((LPWSTR) wszObjectID, &sensType, &sensIndex); + + if (SUCCEEDED(hr)) + { + hr = GetSensorObject(sensIndex, &pSensor); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Device is not a supported sensor, hr = %!HRESULT!", hr); + } + } + + if (SUCCEEDED(hr)) + { + CGeolocationSensor* pGeolocation = nullptr; + + switch (sensType) + { + case Geolocation: + pGeolocation = static_cast(pSensor); + hr = pGeolocation->UpdateGeolocationDataFieldValues(); + break; + + default: + hr = E_UNEXPECTED; + break; + } + } + + return hr; +} + +/*++ + +CSensorDDI::RequestDeviceInfo + + Synchronously request the report descriptor, which contains the sensor type. + +--*/ +HRESULT CSensorDDI::RequestDeviceInfo( + _Inout_ SensorType* pSensType, + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszManufacturer, + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszProduct, + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszSerialNumber, + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszDeviceID) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + if (SUCCEEDED(hr)) + { + ULONG idx = 0; + // This is a list of the sensors supported by this driver. It takes the form of: + // ULONG SensorList[] = { USAGE_SENSOR1, USAGE_SENSOR2, ..., USAGE_SENSORn }; + ULONG SensorList [] = { USAGE_GEOLOCATION_SENSOR }; + ULONG numNodes = ARRAYSIZE(SensorList); + + if ((SUCCEEDED(hr)) && (numNodes > 0)) + { + *pSensType = Collection; + + Trace(TRACE_LEVEL_CRITICAL, "Sensor collection present with %i sensors", numNodes); + + for (idx = 0; idx < numNodes; idx++) + { + switch (SensorList[idx]) + { + case USAGE_GEOLOCATION_SENSOR: + m_pSensorManager->m_AvailableSensorsTypes[idx] = Geolocation; + Trace(TRACE_LEVEL_CRITICAL, "Sensor %i is Geolocation", idx + 1); + break; + + default: + break; + } + } + + if (m_pSensorManager->m_AvailableSensorsTypes.GetCount() != (numNodes)) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Invalid sensor type discovered in a collection node, hr = %!HRESULT!", hr); + } + } + } + + if (SUCCEEDED(hr)) + { + wcscpy_s(pwszSerialNumber, DESCRIPTOR_MAX_LENGTH, DEFAULT_SERIAL_NUMBER); + Trace(TRACE_LEVEL_CRITICAL, "Device Serial Number = %ls", pwszSerialNumber); + + wcscpy_s(pwszDeviceID, DESCRIPTOR_MAX_LENGTH, DEFAULT_SERIAL_NUMBER); + Trace(TRACE_LEVEL_CRITICAL, "Device ID = %ls", pwszDeviceID); + } + + if (SUCCEEDED(hr)) + { + wcscpy_s(pwszManufacturer, DESCRIPTOR_MAX_LENGTH, DEFAULT_MANUFACTURER); + Trace(TRACE_LEVEL_CRITICAL, "Device Manufacturer = %ls", pwszManufacturer); + } + + if (SUCCEEDED(hr)) + { + wcscpy_s(pwszProduct, DESCRIPTOR_MAX_LENGTH, DEFAULT_DEVICE_MODEL_VALUE); + Trace(TRACE_LEVEL_CRITICAL, "Device Product = %ls", pwszProduct); + } + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} + +/*++ + +CSensorDDI::OnGetSupportedSensorObjects + + This method is called by Sensor Class Extension during the initialize procedure to get + the list of sensor objects and their supported properties. + +Parameters: + + ppPortableDeviceValuesCollection - A double IPortableDeviceValuesCollection pointer + that receives the set of Sensor property values + +Return Value: + + Status + +--*/ +HRESULT CSensorDDI::OnGetSupportedSensorObjects( + _Out_ IPortableDeviceValuesCollection** ppPortableDeviceValuesCollection + ) +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + // CoCreate a collection to store the sensor object identifiers. + hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(ppPortableDeviceValuesCollection)); + + CComBSTR objectId; + CComPtr spKeys; + CComPtr spValues; + + POSITION pos = m_pSensorManager->m_AvailableSensorsIDs.GetStartPosition(); + + while (NULL != pos) + { + objectId = m_pSensorManager->m_AvailableSensorsIDs.GetNextValue(pos); + + + if (hr == S_OK) + { + hr = OnGetSupportedProperties(objectId, &spKeys); + } + + if (hr == S_OK) + { + +#pragma warning(suppress: 6387) // nullptr is invalid parameter - use intentional + + hr = OnGetPropertyValues(nullptr, objectId, spKeys, &spValues); + } + + if (hr == S_OK) + { + hr = (*ppPortableDeviceValuesCollection)->Add(spValues); + } + + spKeys.Release(); + spValues.Release(); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; +} + + +/*++ + +CSensorDDI::OnGetSupportedProperties + + This method is called by Sensor Class Extension to get the list of supported properties for a particular Sensor. + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + ppKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs + +Return Value: + + Status + +--*/ +HRESULT CSensorDDI::OnGetSupportedProperties( + _In_ LPWSTR pwszObjectID, + _Out_ IPortableDeviceKeyCollection** ppKeys) +{ + Trace(TRACE_LEVEL_VERBOSE, "Get Properties supported by sensor %ls", pwszObjectID); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + // CoCreate a collection to store the supported property keys. + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(ppKeys)); + + // Add supported property keys for the specified object to the collection + if (SUCCEEDED(hr)) + { + hr = AddSupportedPropertyKeys(pwszObjectID, *ppKeys); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + Trace(TRACE_LEVEL_VERBOSE, "Properties supported by sensor %ls returned, hr = %!HRESULT!", pwszObjectID, hr); + + return hr; +} + +/*++ + +CSensorDDI::OnGetSupportedDataFields + + This method is called by Sensor Class Extension to get the list of supported data fields for a particular Sensor. + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + ppKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs + +Return Value: + + Status + +--*/ +HRESULT CSensorDDI::OnGetSupportedDataFields( + _In_ LPWSTR wszObjectID, + _Out_ IPortableDeviceKeyCollection** ppKeys) +{ + Trace(TRACE_LEVEL_VERBOSE, "Get Datafields supported by sensor %ls", wszObjectID); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + // CoCreate a collection to store the supported property keys. + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(ppKeys)); + + // Add supported property keys for the specified object to the collection + if (hr == S_OK && ppKeys != NULL) + { + hr = AddSupportedDataFieldKeys(wszObjectID, *ppKeys); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + Trace(TRACE_LEVEL_VERBOSE, "Datafields supported by sensor %ls returned, hr = %!HRESULT!", wszObjectID, hr); + + return hr; +} + +/*++ + +CSensorDDI::OnGetSupportedEvents + + This method is called by Sensor Class Extension to get the list of supported events for a particular Sensor. + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + ppSupportedEvents - A set of GUIDs that represent the supported events + pulEventCount - Count of the number of events supported + +Return Value: + Status + +--*/ +HRESULT CSensorDDI::OnGetSupportedEvents( + _In_ LPWSTR ObjectID, + _Out_ GUID **ppSupportedEvents, + _Out_ ULONG *pulEventCount) +{ + Trace(TRACE_LEVEL_INFORMATION, "Get Events supported by sensor %ls", ObjectID); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + SensorType sensType = SensorTypeNone; + DWORD sensIndex = 0; + + hr = FindSensorTypeFromObjectID((LPWSTR) ObjectID, &sensType, &sensIndex); + + if (SUCCEEDED(hr)) + { + //Return two GUIDs + GUID* pBuf = (GUID*) CoTaskMemAlloc(sizeof(GUID) * 2); + + if (pBuf != NULL) + { + *pBuf = SENSOR_EVENT_DATA_UPDATED; + *(pBuf + 1) = SENSOR_EVENT_STATE_CHANGED; + + *ppSupportedEvents = pBuf; + *pulEventCount = 2; + } + else + { + hr = E_OUTOFMEMORY; + + *ppSupportedEvents = NULL; + *pulEventCount = 0; + } + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + Trace(TRACE_LEVEL_INFORMATION, "Events supported by sensor %ls returned, hr = %!HRESULT!", ObjectID, hr); + + return hr; +} + +/*++ + +CSensorDDI::OnGetProperties + + This method is called by Sensor Class Extension to get Sensor property values for a particular Sensor. + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + + +Return Value: + Status + +--*/ +HRESULT CSensorDDI::OnGetProperties( + _In_ IWDFFile* appId, + _In_ LPWSTR ObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _Out_ IPortableDeviceValues** ppValues) +{ + Trace(TRACE_LEVEL_VERBOSE, "Get Properties supported by sensor %ls for Client %p", ObjectID, appId); + + UNREFERENCED_PARAMETER(appId); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + if ((ObjectID == NULL) || + (pKeys == NULL) || + (ppValues == NULL)) + { + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + hr = OnGetPropertyValues(appId, ObjectID, pKeys, ppValues); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + Trace(TRACE_LEVEL_VERBOSE, "Properties supported by sensor %ls returned for Client %p, hr = %!HRESULT!", ObjectID, appId, hr); + + return hr; +} + +/*++ + +CSensorDDI::OnGetRadioState + + This method is called for Radio Management IO and gets the current radio state. + +Arguments: + + pRequest - The IO Request + fPreviousState - If to get the previous radio state instead of the + current radio state. + +Return Value: + Status + +--*/ +HRESULT CSensorDDI::OnGetRadioState( + _In_ IWDFIoRequest* pRequest, + _In_ bool fPreviousState) +{ + HRESULT hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (SUCCEEDED(hr)) + { + CComPtr spOutputMemory = nullptr; + pRequest->GetOutputMemory(&spOutputMemory); + if (nullptr != spOutputMemory) + { + SIZE_T cbBufferOut = 0; + void* pOutBuffer = spOutputMemory->GetDataBuffer(&cbBufferOut); + if (sizeof(DEVICE_RADIO_STATE) == cbBufferOut && nullptr != pOutBuffer) + { + CSensor* pSensor = nullptr; + hr = GetGeolocationSensorObject(&pSensor); + if (SUCCEEDED(hr) && nullptr != pSensor) + { + CGeolocationSensor* pGeolocation = (CGeolocationSensor*) pSensor; + + if (fPreviousState) + { + *((DEVICE_RADIO_STATE*) pOutBuffer) = (DEVICE_RADIO_STATE) pGeolocation->m_ulPreviousGeolocationRadioState; + } + else + { + *((DEVICE_RADIO_STATE*) pOutBuffer) = (DEVICE_RADIO_STATE) pGeolocation->m_ulCurrentGeolocationRadioState; + } + + Trace(TRACE_LEVEL_VERBOSE, "%!FUNC! Got radio state = %d", *((DEVICE_RADIO_STATE*) pOutBuffer)); + } + } + } + } +#else + UNREFERENCED_PARAMETER(pRequest); + UNREFERENCED_PARAMETER(fPreviousState); +#endif + + ExitProcessing(PROCESSING_ISENSORDRIVER); + return hr; +} + +/*++ + +CSensorDDI::OnGetDataFields + + This method is called by Sensor Class Extension to get Sensor data fields for a particular Sensor. + +The parameters sent to us are: + wszObjectID - the object whose properties are being requested. + pKeys - the list of property keys of the properties to request from the object + pValues - an IPortableDeviceValues which will contain the property values retrieved from the object + +The driver should: + Read the specified properties for the specified object and populate pValues with the results. + +--*/ +HRESULT CSensorDDI::OnGetDataFields( + _In_ IWDFFile* appId, + _In_ LPWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _Out_ IPortableDeviceValues** ppValues) +{ + UNREFERENCED_PARAMETER(appId); + + Trace(TRACE_LEVEL_VERBOSE, "Get Datafields supported by sensor %ls for Client %p", wszObjectID, appId); + + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + if ((wszObjectID == NULL) || + (pKeys == NULL) || + (ppValues == NULL)) + { + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + // CoCreate a collection to store the property values. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(ppValues)); + + // Read the specified properties on the specified object and add the property values to the collection. + if (hr == S_OK) + { + DWORD cKeys = 0; + IPortableDeviceValues* pValues = *ppValues; // fakes this call: hr = GetDataValuesForObject(wszObjectID, pKeys, *ppValues); + + hr = pKeys->GetCount(&cKeys); + + if (hr == S_OK) + { + CAtlStringW strObjectID = wszObjectID; + CComBSTR objectId; + + POSITION posID = m_pSensorManager->m_AvailableSensorsIDs.GetStartPosition(); + POSITION posType = m_pSensorManager->m_AvailableSensorsTypes.GetStartPosition(); + SensorType sensType = SensorTypeNone; + DWORD sensIndex = 0; + BOOL fPollFailed = FALSE; + + while (NULL != posID) + { + objectId = m_pSensorManager->m_AvailableSensorsIDs.GetNextValue(posID); + sensType = m_pSensorManager->m_AvailableSensorsTypes.GetNextValue(posType); + + if (strObjectID.CompareNoCase(objectId) == 0) + { + CSensor* pSensor = nullptr; + + hr = FindSensorTypeFromObjectID((LPWSTR) wszObjectID, &sensType, &sensIndex); + + if (SUCCEEDED(hr)) + { + hr = GetSensorObject(sensIndex, &pSensor); + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + Trace(TRACE_LEVEL_INFORMATION, "Client %p Requesting Datafield Values for %s", appId, pSensor->m_SensorName); + + if ((FALSE == pSensor->m_fReportingState) || (FALSE == pSensor->m_fInitialDataReceived)) + { + { + CComCritSecLock scopeLock(m_CriticalSection); //make this thread safe + + // issue a poll to the device for this sensor and wait for updated data from sensor + pSensor->m_fSensorUpdated = FALSE; + // NOTE: m_nNotifyOnSensorUpdate should be the Sensor Number n = {1..k} + // this is derived from sensIndex = {0..k-1} so 1 is added indicating it is + // a valid sensor (anything > 0) + m_nNotifyOnSensorUpdate = sensIndex + 1; + } + + const DWORD dwInitialDataMaxTries = INITIAL_DATA_POLL_MAX_RETRIES; + DWORD dwInitialDataTryCount = 0; + + do + { + fPollFailed = FALSE; + + Trace(TRACE_LEVEL_INFORMATION, "Sending poll request to device to update Datafield Values for %s", pSensor->m_SensorName); + + hr = UpdateSensorDataFieldValues(m_pSensorManager->m_AvailableSensorsIDs[sensIndex]); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to request input report from %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + else + { + const DWORD dwDataPollTimeLimit = DEVICE_POLL_TIMEOUT; //mS + + ULONGLONG ullInitialEventTime = GetTickCount64(); + ULONGLONG ulCurrentEventTime = 0; + ULONG ulTimePassed = 0; + ULONG ulYieldCount = 0; + + do // spin here until dwDataPollTimeLimit has passed; + { + Yield(); + ulYieldCount++; + + ulCurrentEventTime = GetTickCount64(); + ulTimePassed = (ULONG) (ulCurrentEventTime - ullInitialEventTime); //time passed in milliseconds + + } while ((FALSE == pSensor->m_fSensorUpdated) && (ulTimePassed < dwDataPollTimeLimit)); + + if (ulTimePassed > dwDataPollTimeLimit) + { + fPollFailed = TRUE; + dwInitialDataTryCount++; + + // do not fail - instead, fall through and get the latest value from the API + Trace(TRACE_LEVEL_ERROR, "Failed to receive poll response from %s within %i mS, failed at %i mS and %i yields, hr = %!HRESULT!", pSensor->m_SensorName, dwDataPollTimeLimit, ulTimePassed, ulYieldCount, hr); + } + else + { + if (TRUE == pSensor->m_fSensorUpdated) + { + pSensor->m_fInitialDataReceived = TRUE; + Trace(TRACE_LEVEL_INFORMATION, "Received polled datafield update for %s after %i mS, hr = %!HRESULT!", pSensor->m_SensorName, ulTimePassed, hr); + } + } + } + + } while ((FALSE == pSensor->m_fInitialDataReceived) && (dwInitialDataTryCount < dwInitialDataMaxTries)); + + if (FALSE == pSensor->m_fInitialDataReceived) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to receive initial data from %s after %i tries, hr = %!HRESULT!", pSensor->m_SensorName, dwInitialDataTryCount, hr); + } + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to get pSensor, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", wszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", wszObjectID, hr); + } + else + { + // WPD is calling us, so ignore trace + } + } + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + hr = pKeys->GetAt(dwIndex, &Key); + + if (hr == S_OK) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Preset the property value to 'error not supported'. The actual value + // will replace this value, if read from the device. + hr = pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + + if (SUCCEEDED(hr)) + { + if (nullptr != pSensor) + { + if (SUCCEEDED(pSensor->GetDataField(Key, &var))) + { + hr = pValues->SetValue(Key, &var); + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to get datafield %!GUID!-%i value from %s", &Key.fmtid, Key.pid, pSensor->m_SensorName); + } + } + else + { + hr = E_POINTER; + Trace(TRACE_LEVEL_ERROR, "pSensor == NULL before getting datafield %!GUID!-%i value, hr = %!HRESULT!", &Key.fmtid, Key.pid, hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed on idx = %i for SetErrorValue for %!GUID!-%i value from %s, hr = %!HRESULT!", dwIndex, &Key.fmtid, Key.pid, pSensor->m_SensorName, hr); + } + + // Free any allocated resources + PropVariantClear(&var); + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to update datafield %!GUID!-%i value from %s, hr = %!HRESULT!", &Key.fmtid, Key.pid, pSensor->m_SensorName, hr); + } + } + + if (SUCCEEDED(hr)) + { + if (FALSE == pSensor->m_fReportingState) + { + if (FALSE == fPollFailed) + { + Trace(TRACE_LEVEL_INFORMATION, "Returned poll-updated datafield values for %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + else + { + Trace(TRACE_LEVEL_INFORMATION, "Returned current datafield values for %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + } + else + { + Trace(TRACE_LEVEL_INFORMATION, "Returned async-updated datafield values for %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed while updating datafield values for %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + } + else + { + if (nullptr != pSensor) + { + Trace(TRACE_LEVEL_ERROR, "Failed while polling datafield values for %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + else + { + Trace(TRACE_LEVEL_ERROR, "pSensor was NULL while polling datafield values, hr = %!HRESULT!", hr); + } + } + } + + if (sensType == SensorTypeNone) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed because sensType == SensorTypeNone, hr = %!HRESULT!", hr); + } + } + } + + //*ppValues = pValues; // fakes return of this call: hr = GetDataValuesForObject(wszObjectID, pKeys, *ppValues); + } + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + Trace(TRACE_LEVEL_VERBOSE, "Datafields supported by sensor %ls returned for Client %p, hr = %!HRESULT!", wszObjectID, appId, hr); + + return hr; +} + +/*++ + +CSensorDDI::OnSetProperties + + This method is called by Sensor Class Extension to set Sensor properties for a particular Sensor. + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + +Return Value: + + Status + +--*/ +HRESULT CSensorDDI::OnSetProperties( + _In_ IWDFFile* appId, + _In_ LPWSTR ObjectID, + _In_ IPortableDeviceValues* pValues, + _Out_ IPortableDeviceValues** ppResults) +{ + Trace(TRACE_LEVEL_VERBOSE, "Set Properties on sensor %ls for Client %p", ObjectID, appId); + + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + if ((ObjectID == NULL) || + (pValues == NULL) || + (ppResults == NULL)) + { + hr = E_INVALIDARG; + } + + // Update our cache + if (SUCCEEDED(hr)) + { + DWORD cValues = 0; + BOOL fHasError = FALSE; + SensorType sType = SensorTypeNone; + DWORD sIndex = 0; + CSensor* pSensor = nullptr; + + // CoCreate a collection to store the property set operation results. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(ppResults)); + + // Get the sensor type + if (hr == S_OK) + { + hr = FindSensorTypeFromObjectID(ObjectID, &sType, &sIndex); + } + + if (SUCCEEDED(hr) && (sType != SensorTypeNone)) + { + hr = GetSensorObject(sIndex, &pSensor); + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", ObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", ObjectID, hr); + } + else + { + // WPD is calling us, so ignore trace + } + } + + // Set the property values on the specified object + if ((hr == S_OK) && (nullptr != pSensor)) + { + hr = pValues->GetCount(&cValues); + + if (hr == S_OK) + { + + for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + PROPVARIANT var; + + PropVariantInit(&var); + + hr = pValues->GetAt(dwIndex, &Key, &var); + + if (hr == S_OK) + { + // Check if this is a supported settable property + if (TRUE == IsSettablePropertyKey(sIndex, Key)) + { + + FLOAT clientChangeSensitivities[MAX_NUM_DATA_FIELDS] = { 0 }; + ULONG clientReportInterval = 0; + ULONG clientLocationDesiredAccuracy = 0; + + if (TRUE == IsEqualPropertyKey(Key, SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL)) + { + // set the report interval for this client in the client report interval list + hr = FindClientFromAppID(pSensor, appId, &clientReportInterval, &clientLocationDesiredAccuracy, clientChangeSensitivities); + + if (SUCCEEDED(hr)) + { + if (VT_UI4 == var.vt) + { + if (0 == var.uintVal) + { + // set the report interval for this sensor client to 0 to indicate "use the default" + pSensor->m_pClientMap[appId].ulClientReportInterval = 0; + } + else if (var.uintVal >= pSensor->m_ulDefaultMinimumReportInterval) + { + pSensor->m_pClientMap[appId].ulClientReportInterval = var.uintVal; + } + else if (var.vt == VT_EMPTY) + { + // Do not allow deletion of the property + hr = (*ppResults)->SetErrorValue(Key, E_ACCESSDENIED); + fHasError = TRUE; + } + else + { + hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG); + fHasError = TRUE; + } + + if (SUCCEEDED(hr) && (FALSE == fHasError)) + { + hr = SelectClientReportInterval(pSensor); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "var.vt is not == VT_UI4, hr = %!HRESULT!", hr); + + hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG); + fHasError = TRUE; + } + } + } + else if (TRUE == IsEqualPropertyKey(Key, SENSOR_PROPERTY_LOCATION_DESIRED_ACCURACY)) + { + // set the report interval for this client in the client report interval list + hr = FindClientFromAppID(pSensor, appId, &clientReportInterval, &clientLocationDesiredAccuracy, clientChangeSensitivities); + + if (SUCCEEDED(hr)) + { + if (VT_UI4 == var.vt) + { + if (0 == var.uintVal) + { + // set the location desired accuracy for this sensor client to 0 to indicate "use the default" + pSensor->m_pClientMap[appId].ulClientLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + else if (var.uintVal <= LOCATION_DESIRED_ACCURACY_HIGH) + { + if (var.uintVal > LOCATION_DESIRED_ACCURACY_DEFAULT) + { + pSensor->m_pClientMap[appId].ulClientLocationDesiredAccuracy = var.uintVal; + } + } + else if (var.vt == VT_EMPTY) + { + // Do not allow deletion of the property + hr = (*ppResults)->SetErrorValue(Key, E_ACCESSDENIED); + fHasError = TRUE; + } + else + { + hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG); + fHasError = TRUE; + } + + if (SUCCEEDED(hr) && (FALSE == fHasError)) + { + hr = SelectClientLocationDesiredAccuracy(pSensor); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "var.vt is not == VT_UI4, hr = %!HRESULT!", hr); + + hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG); + fHasError = TRUE; + } + } + } + else if (TRUE == IsEqualPropertyKey(Key, SENSOR_PROPERTY_CHANGE_SENSITIVITY)) + { + PROPERTYKEY pkDfKey = { 0 }; + PROPERTYKEY pkDvKey = { 0 }; + DWORD cDfVals = 0; + DWORD cDfKeys = 0; + PROPVARIANT val; + + CComPtr spDfVals = nullptr; + + if ((VT_UNKNOWN == var.vt) && (var.punkVal != nullptr)) + { + IUnknown* pUnknown = var.punkVal; + + hr = pUnknown->QueryInterface(IID_PPV_ARGS(&spDfVals)); + + if (SUCCEEDED(hr) && (nullptr != spDfVals)) + { + hr = spDfVals->GetCount(&cDfVals); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "spDfValsSource == nullptr or failed, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr)) + { + hr = pSensor->m_spSupportedSensorDataFields->GetCount(&cDfKeys); + } + + if (SUCCEEDED(hr)) + { + hr = FindClientFromAppID(pSensor, appId, &clientReportInterval, &clientLocationDesiredAccuracy, clientChangeSensitivities); + } + + if (SUCCEEDED(hr)) + { + for (DWORD dwDvIdx = 0; dwDvIdx < cDfVals; dwDvIdx++) + { + DWORD dwDkIdx = 0; + + PropVariantInit(&val); + + // get the key/value from the value values + hr = spDfVals->GetAt(dwDvIdx, &pkDvKey, &val); + + if (SUCCEEDED(hr)) + { + HRESULT hrError = E_UNEXPECTED; + + if (V_VT(&val) == VT_R4 || V_VT(&val) == VT_R8 || V_VT(&val) == VT_UI4) + { + float fltVal; + + if (V_VT(&val) == VT_UI4) + { + fltVal = V_VT(&val) == static_cast(V_UI4(&val)); + } + else + { + fltVal = V_VT(&val) == VT_R8 ? static_cast(V_R8(&val)) : V_R4(&val); + } + + if (fltVal < 0.0F) + { + hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG); + fHasError = TRUE; + } + else + { + //find the value key in the supported data fields + for (dwDkIdx = 0; dwDkIdx < cDfKeys; dwDkIdx++) + { + hr = pSensor->m_spSupportedSensorDataFields->GetAt(dwDkIdx, &pkDfKey); + + if (SUCCEEDED(hr)) + { + if (IsEqualPropertyKey(pkDfKey, pkDvKey)) + { + pSensor->m_pClientMap[appId].fltClientChangeSensitivity[dwDkIdx] = fltVal; + + hrError = S_OK; + break; + } + } + } + } + } + else if (V_VT(&val) == VT_NULL) + { + //find the value key in the supported data fields + for (dwDkIdx = 0; dwDkIdx < cDfKeys; dwDkIdx++) + { + hr = pSensor->m_spSupportedSensorDataFields->GetAt(dwDkIdx, &pkDfKey); + + if (SUCCEEDED(hr)) + { + if (IsEqualPropertyKey(pkDfKey, pkDvKey)) + { + pSensor->m_pClientMap[appId].fltClientChangeSensitivity[dwDkIdx] = CHANGE_SENSITIVITY_NOT_SET; + + hrError = S_OK; + break; + } + } + } + } + + if (FAILED(hrError)) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "pkDvKey not found or pkDfVal invalid arg, hr = %!HRESULT!", hr); + + hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG); + fHasError = TRUE; + } + } + } + + if (SUCCEEDED(hr)) + { + hr = SelectClientChangeSensitivity(pSensor); + } + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "var.vt is not == VT_UNKNOWN, hr = %!HRESULT!", hr); + + hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG); + fHasError = TRUE; + } + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Property not found, hr = %!HRESULT!", hr); + + hr = (*ppResults)->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); + fHasError = TRUE; + } + } + + PropVariantClear(&var); + + if (FAILED(hr)) + { + break; + } + } + } + + // Since we have set failures for the property set operations we must let the application + // know by returning S_FALSE. This will instruct the application to look at the + // property set operation results for failure values. + if ((hr == S_OK) && (TRUE == fHasError)) + { + hr = S_FALSE; + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get pSensor, hr = %!HRESULT!", hr); + } + + //return hr; + } + + // If it returns S_OK, Send the values down to the device + // S_FALSE return indicates that the property is not supported + if (S_OK == hr) + { + hr = UpdateSensorPropertyValues(ObjectID, TRUE); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + Trace(TRACE_LEVEL_VERBOSE, "Properties on sensor %ls were set for Client %p, hr = %!HRESULT!", ObjectID, appId, hr); + + return hr; +} + +/*++ + +CSensorDDI::OnSetRadioState + + This method is called for Radio Management IO to set the radio state + +Arguments: + pRequest - The IO request + fPreviousState - If the previous radio state should be set instead of the current state. + +Return Value: + Status + +--*/ +HRESULT CSensorDDI::OnSetRadioState( + _In_ IWDFIoRequest* pRequest, + _In_ bool fPreviousState) +{ + HRESULT hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (SUCCEEDED(hr)) + { + CComPtr spInputMemory; + pRequest->GetInputMemory(&spInputMemory); + SIZE_T cbBufferIn = 0; + void* pInBuffer = spInputMemory->GetDataBuffer(&cbBufferIn); + + if (nullptr != pInBuffer && + sizeof(DEVICE_RADIO_STATE) == cbBufferIn && + *((DEVICE_RADIO_STATE*) pInBuffer) <= DRS_RADIO_MAX + ) + { + CSensor* pSensor = nullptr; + hr = GetGeolocationSensorObject(&pSensor); + if (SUCCEEDED(hr) && nullptr != pSensor) + { + if (fPreviousState) + { + pSensor->m_ulPreviousGeolocationRadioState = *((DEVICE_RADIO_STATE*) pInBuffer); + } + else + { + pSensor->m_ulRequiredGeolocationRadioState = *((DEVICE_RADIO_STATE*) pInBuffer); + } + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Radio state set to = %d", *((DEVICE_RADIO_STATE*) pInBuffer)); + + hr = UpdateSensorPropertyValues(pSensor->GetSensorObjectID(), TRUE); + } + } + else + { + hr = E_INVALIDARG; + } + } +#else + UNREFERENCED_PARAMETER(pRequest); + UNREFERENCED_PARAMETER(fPreviousState); +#endif + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + return hr; +} + +/*++ + +CSensorDDI::OnClientConnect + + This method is called by Sensor Class Extension when a client app connects to a Sensor + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + +Return Value: + + Status + +--*/ +HRESULT CSensorDDI::OnClientConnect( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID) +{ + CComCritSecLock scopeLock(m_CriticalSectionConnectDisconnect); + + HRESULT hr = S_OK; + SensorType sType = SensorTypeNone; + ULONG sIndex = 0; + CSensor* pSensor = nullptr; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + hr = FindSensorTypeFromObjectID(pwszObjectID, &sType, &sIndex); + + if (SUCCEEDED(hr)) + { + hr = GetSensorObject(sIndex, &pSensor); + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", pwszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", pwszObjectID, hr); + } + else + { + //WPD is calling us, so ignore trace + } + } + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! for Client %p to %s", pClientFile, pSensor->m_SensorName); + + BOOL fClientIsPresent = FALSE; + + hr = CheckForClient(pSensor, pClientFile, &fClientIsPresent); + + // do not allow client to connect more than one time + // however, if a client is already connected + // this must return S_OK so that the class extension + // believes the request to connect has been honored + if (SUCCEEDED(hr)) + { + if (FALSE == fClientIsPresent) + { + size_t cClients = 0; + CLIENT_ENTRY entry; + + entry.ulClientReportInterval = 0; //default + entry.ulClientLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + for (DWORD idx = 0; idx < MAX_NUM_DATA_FIELDS; idx++) + { + entry.fltClientChangeSensitivity[idx] = CHANGE_SENSITIVITY_NOT_SET; + } + + pSensor->m_pClientMap[pClientFile] = entry; + + cClients = pSensor->m_pClientMap.GetCount(); + + if (1 == cClients) + { +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (DRS_RADIO_ON == pSensor->m_ulCurrentGeolocationRadioState) + { +#endif + Trace(TRACE_LEVEL_INFORMATION, "Calling StopIdle so the device will remain in D0 as long as there are connected clients and the radio is on"); + pSensor->m_spWdfDevice2->StopIdle(FALSE); +#if (NTDDI_VERSION >= NTDDI_WIN8) + } +#endif + } + + Trace(TRACE_LEVEL_INFORMATION, "Client %p connected to %s, Client count now = %i", pClientFile, pSensor->m_SensorName, (ULONG) cClients); + + if (0 == cClients - 1) + { + DWORD cDfKeys = 0; + pSensor->m_spSupportedSensorDataFields->GetCount(&cDfKeys); + + pSensor->m_ulLowestClientReportInterval = pSensor->m_ulDefaultCurrentReportInterval; + + for (DWORD dwIdx = 0; dwIdx < MAX_NUM_DATA_FIELDS; dwIdx++) + { + pSensor->m_fltLowestClientChangeSensitivities[dwIdx] = pSensor->m_fltDefaultChangeSensitivity; + } + + } + + if (SUCCEEDED(hr)) + { + if (cClients >= 1) + { + hr = FindSensorTypeFromObjectID(pwszObjectID, &sType, &sIndex); + + if (SUCCEEDED(hr)) + { + hr = SelectClientReportInterval(pSensor); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to update report interval, hr = %!HRESULT!", hr); + } + else + { + hr = SelectClientLocationDesiredAccuracy(pSensor); + } + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to update location desired accuracy, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", pwszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", pwszObjectID, hr); + } + else + { + //WPD is calling us, so ignore trace + } + } + + if (SUCCEEDED(hr)) + { + hr = SelectClientChangeSensitivity(pSensor); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to update change sensitivity, hr = %!HRESULT!", hr); + } + } + + if (SUCCEEDED(hr) && 1 == cClients) + { + pSensor->m_ulPowerState = SENSOR_POWER_STATE_LOW_POWER; + + hr = UpdateSensorPropertyValues(pwszObjectID, TRUE); + } + + // Poll the sensors for initial data values + if (SUCCEEDED(hr)) + { + hr = UpdateSensorDataFieldValues(m_pSensorManager->m_AvailableSensorsIDs[sIndex]); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to request input report, hr = %!HRESULT!", hr); + } + } + } + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "Client is already connected, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failure while trying to connect client, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get pSensor, hr = %!HRESULT!", hr); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + return hr; +} + +/*++ + +CSensorDDI::OnClientDisconnect + + This method is called by Sensor Class Extension when a client app disconnects from a Sensor + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + +Return Value: + Status + +--*/ +HRESULT CSensorDDI::OnClientDisconnect( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID) +{ + CComCritSecLock scopeLock(m_CriticalSectionConnectDisconnect); + + HRESULT hr = S_OK; + SensorType sType = SensorTypeNone; + ULONG sIndex = 0; + CSensor* pSensor = nullptr; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + CComPtr spReportInterval = NULL; + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spReportInterval)); + + if (SUCCEEDED(hr)) + { + hr = FindSensorTypeFromObjectID(pwszObjectID, &sType, &sIndex); + } + + if (SUCCEEDED(hr)) + { + hr = GetSensorObject(sIndex, &pSensor); + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", pwszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", pwszObjectID, hr); + } + else + { + //WPD is calling us, so ignore trace + } + } + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! for Client %p from %s", pClientFile, pSensor->m_SensorName); + + BOOL fClientIsPresent = FALSE; + size_t cClients = pSensor->m_pClientMap.GetCount(); + + hr = CheckForClient(pSensor, pClientFile, &fClientIsPresent); + + if (SUCCEEDED(hr) && (TRUE == fClientIsPresent)) + { + if (SUCCEEDED(hr)) + { + if (cClients > 0) + { + //find this client in the list of clients and remove from the three client lists + hr = RemoveClient(pSensor, pClientFile); + + if (SUCCEEDED(hr)) + { + hr = CheckForClient(pSensor, pClientFile, &fClientIsPresent); + + if (TRUE == fClientIsPresent) + { + Trace(TRACE_LEVEL_ERROR, "Failed to remove client %p from %s, hr = %!HRESULT!", pClientFile, pSensor->m_SensorName, hr); + } + else + { + Trace(TRACE_LEVEL_INFORMATION, "Client %p successfully removed from %s, Client Count now = %i, hr = %!HRESULT!", pClientFile, pSensor->m_SensorName, (DWORD) cClients - 1, hr); + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "Client %p has already been removed from %s, hr = %!HRESULT!", pClientFile, pSensor->m_SensorName, hr); + } + + cClients = pSensor->m_pClientMap.GetCount(); + + if (SUCCEEDED(hr)) + { + hr = SelectClientReportInterval(pSensor); + } + + if (SUCCEEDED(hr)) + { + hr = SelectClientLocationDesiredAccuracy(pSensor); + } + + if (SUCCEEDED(hr)) + { + hr = SelectClientChangeSensitivity(pSensor); + } + + if (SUCCEEDED(hr)) + { + if (0 == cClients) + { + pSensor->m_fReportingState = FALSE; + pSensor->m_ulPowerState = SENSOR_POWER_STATE_POWER_OFF; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (DRS_RADIO_ON == pSensor->m_ulCurrentGeolocationRadioState) + { +#endif + Trace(TRACE_LEVEL_INFORMATION, "Calling ResumeIdle so the device will remain in Dx as long as there are no connected clients or the radio is off"); + pSensor->m_spWdfDevice2->ResumeIdle(); +#if (NTDDI_VERSION >= NTDDI_WIN8) + } +#endif + + Trace(TRACE_LEVEL_INFORMATION, "All Clients have been removed from %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + + hr = UpdateSensorPropertyValues(pwszObjectID, TRUE); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Attempt to reduce client count below 0 on %s, AppID %p", pSensor->m_SensorName, pClientFile); + } + } + + if (SUCCEEDED(hr) && (NULL != m_pSensorManager)) + { + size_t cSensors = m_pSensorManager->m_pSensorList.GetCount(); + BOOL fHasClients = FALSE; + cClients = 0; + + // see if any sensor has at least one client + for (sIndex = 0; sIndex < cSensors; sIndex++) + { + pSensor = nullptr; + + hr = GetSensorObject(sIndex, &pSensor); + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + cClients = pSensor->m_pClientMap.GetCount(); + + if (0 != cClients) + { + fHasClients = TRUE; + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor object, hr = %!HRESULT!", hr); + } + } + + // if no clients, then allow for device disconnect + if (SUCCEEDED(hr) && (FALSE == fHasClients)) + { + DeInitSensorDevice(); + } + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Attempt to remove non-existent client, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_POINTER; + Trace(TRACE_LEVEL_ERROR, "Sensor not found, hr = %!HRESULT!", hr); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + return hr; +} + +/*++ + +CSensorDDI::OnClientSubscribeToEvents + + This method is called by Sensor Class Extension when a client subscribes to events by calling SetEventSink + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + + +Return Value: + Status + +--*/ +HRESULT CSensorDDI::OnClientSubscribeToEvents( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID) +{ + UNREFERENCED_PARAMETER(pClientFile); + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + // Set the Client Status : denotes the status of event subscribed clients + CSensor* pSensor = nullptr; + SensorType sType; + DWORD sIndex; + + hr = FindSensorTypeFromObjectID(pwszObjectID, &sType, &sIndex); + + if (SUCCEEDED(hr)) + { + hr = GetSensorObject(sIndex, &pSensor); + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", pwszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", pwszObjectID, hr); + } + else + { + //WPD is calling us, so ignore trace + } + } + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! for Client %p to %s", pClientFile, pSensor->m_SensorName); + + hr = pSensor->Subscribe(pClientFile); + + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor object, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + size_t cSubscriberCount = pSensor->m_pSubscriberMap.GetCount(); + + Trace(TRACE_LEVEL_INFORMATION, "Client %p Subscribed to %s, Subscriber Count = %i", pClientFile, pSensor->m_SensorName, (DWORD) cSubscriberCount); + + if (cSubscriberCount == 1) + { + pSensor->m_fReportingState = TRUE; + pSensor->m_ulPowerState = SENSOR_POWER_STATE_FULL_POWER; + + pSensor->m_ulEventCount = 0; + + hr = UpdateSensorPropertyValues(pwszObjectID, TRUE); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to update property values, hr = %!HRESULT!", hr); + } + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to subscribe to sensor events, hr = %!HRESULT!", hr); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + return hr; +} + +/*++ + +CSensorDDI::OnClientUnsubscribeFromEvents + + This method is called by Sensor Class Extension when a client unsubscribes from events by calling SetEventSink(NULL) + +Arguments: + + ObjectID - String that represents the object whose supported property keys are being requested + +Return Value: + Status + +--*/ +HRESULT CSensorDDI::OnClientUnsubscribeFromEvents( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID) +{ + + UNREFERENCED_PARAMETER(pClientFile); + HRESULT hr = S_OK; + + hr = EnterProcessing(PROCESSING_ISENSORDRIVER); + + if (SUCCEEDED(hr)) + { + // Set the Client Status : denotes the status of event subscribed clients + CSensor* pSensor = nullptr; + SensorType sType; + DWORD sIndex; + + hr = FindSensorTypeFromObjectID(pwszObjectID, &sType, &sIndex); + + if (SUCCEEDED(hr)) + { + hr = GetSensorObject(sIndex, &pSensor); + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", pwszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", pwszObjectID, hr); + } + else + { + // WPD is calling us, so ignore trace + } + } + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! for Client %p from %s", pClientFile, pSensor->m_SensorName); + + hr = pSensor->Unsubscribe(pClientFile); + + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor object, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr)) + { + size_t cSubscriberCount = pSensor->m_pSubscriberMap.GetCount(); + + Trace(TRACE_LEVEL_INFORMATION, "Subscriber %p Removed from %s, Subscriber Count = %i", pClientFile, pSensor->m_SensorName, (DWORD) cSubscriberCount); + + + if (0 == cSubscriberCount) + { + Trace(TRACE_LEVEL_INFORMATION, "All Subscribers removed from %s, Subscriber Count = %i", pSensor->m_SensorName, (DWORD) cSubscriberCount); + pSensor->m_fReportingState = FALSE; + pSensor->m_ulPowerState = SENSOR_POWER_STATE_LOW_POWER; + + hr = UpdateSensorPropertyValues(pwszObjectID, TRUE); + } + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to update property values, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failure during attempt to unsubscribe from events, hr = %!HRESULT!", hr); + } + + } // processing in progress + + ExitProcessing(PROCESSING_ISENSORDRIVER); + + return hr; +} + +/*++ + +CSensorDDI::OnProcessWpdMessage + +--*/ +HRESULT CSensorDDI::OnProcessWpdMessage( + _In_ IUnknown* pPortableDeviceValuesParamsUnknown, + _In_ IUnknown* pPortableDeviceValuesResultsUnknown) +{ + UNREFERENCED_PARAMETER(pPortableDeviceValuesParamsUnknown); + UNREFERENCED_PARAMETER(pPortableDeviceValuesResultsUnknown); + + HRESULT hr = S_OK; + + return hr; +} + +/*++ + +CSensorDDI::OnGetPropertyValues + + This method is called to populate property values for the object specified. + + Read the specified properties for the specified object and populate pValues with the results. + +The parameters sent to us are: + wszObjectID - the object whose properties are being requested. + pKeys - the list of property keys of the properties to request from the object + pValues - an IPortableDeviceValues which will contain the property values retrieved from the object + +The driver should: + + Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES. If any property read failed, the corresponding value should be + set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. + S_OK should be returned if all properties were read successfully. + S_FALSE should be returned if any property read failed. + Any error return indicates that the driver did not fill in any results, and the caller will + not attempt to unpack any property values. + +--*/ +HRESULT CSensorDDI::OnGetPropertyValues( + _In_ IWDFFile* appId, + _In_ LPWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _Out_ IPortableDeviceValues** ppValues) +{ + HRESULT hr = S_OK; + + + // CoCreate a collection to store the property values. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(ppValues)); + + // Read the specified properties on the specified object and add the property values to the collection. + if (hr == S_OK && ppValues != NULL) + { + DWORD cKeys = 0; + BOOL fError = FALSE; + IPortableDeviceValues* pValues = *ppValues; // Fakes this call: hr = GetPropertyValuesForObject(wszObjectID, pKeys, *ppValues); + + CSensor* pSensor = NULL; + SensorType sensType = SensorTypeNone; + DWORD sensIndex = 0; + + CGeolocationSensor* pGeolocation = NULL; + + if ((wszObjectID == NULL) || + (pKeys == NULL) || + (pValues == NULL)) + { + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->GetCount(&cKeys); + + if (hr == S_OK) + { + hr = FindSensorTypeFromObjectID((LPWSTR) wszObjectID, &sensType, &sensIndex); + + if (SUCCEEDED(hr)) + { + hr = GetSensorObject(sensIndex, &pSensor); + + if ((NULL != pSensor) && (SUCCEEDED(hr))) + { + // depending on where OnGetPropertyValues() is being called from, appId may not be valid + if (nullptr != appId) + { + Trace(TRACE_LEVEL_INFORMATION, "Client %p Requesting Property Values for %s", appId, pSensor->m_SensorName); + } + else + { + Trace(TRACE_LEVEL_VERBOSE, "Sensor %ls is being asked for Property Values for %s", wszObjectID, pSensor->m_SensorName); + } + + switch (sensType) + { + case Geolocation: + pGeolocation = (CGeolocationSensor*) pSensor; + hr = pGeolocation->GetPropertyValuesForGeolocationObject(wszObjectID, pKeys, pValues); + break; + + default: + break; + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor object, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", wszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", wszObjectID, hr); + } + else + { + // WPD is calling us, so ignore trace + } + } + + } + } + + hr = (FALSE == fError) ? hr : S_FALSE; + + *ppValues = pValues; // Fakes return from this call: hr = GetPropertyValuesForObject(wszObjectID, pKeys, *ppValues); + + } + + + return hr; +} + +/*++ + +CSensorDDI::CopyPropertyKeys + + This method is called to copy PROPERTYKEYs from one collection to the other. + +The parameters sent to us are: + + pSourceKeys - The source collection + pTargetKeys - The target collection + +The driver should: + + Copy PROPERTYKEYs from the source collection to the target. + +--*/ +HRESULT CSensorDDI::CopyPropertyKeys( + _In_ IPortableDeviceKeyCollection *pSourceKeys, + _In_ IPortableDeviceKeyCollection *pTargetKeys) +{ + HRESULT hr = S_OK; + DWORD cKeys = 0; + PROPERTYKEY key = { 0 }; + + if (NULL != pSourceKeys && NULL != pTargetKeys) + { + hr = pSourceKeys->GetCount(&cKeys); + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; ++dwIndex) + { + hr = pSourceKeys->GetAt(dwIndex, &key); + + if (SUCCEEDED(hr)) + { + hr = pTargetKeys->Add(key); + } + } + } + } + else + { + hr = E_POINTER; + } + + return hr; +} + +/*++ + +CSensorDDI::FindClientFromAppID + + This method is called to determine the client index and the client-desired settings for settable properties. + +The driver should: + + Return the client index, the client change sensitivity and the client report interval. + +--*/ +HRESULT CSensorDDI::FindClientFromAppID( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID, + _Out_ ULONG* pClientReportInterval, + _Out_ ULONG* pClientLocationDesiredAccuracy, + _Inout_updates_(MAX_NUM_DATA_FIELDS) FLOAT* pClientChangeSensitivities) +{ + HRESULT hr = E_POINTER; + + if ((nullptr != pSensor) && + (nullptr != pClientReportInterval) && + (nullptr != pClientChangeSensitivities) + ) + { + DWORD cDfKeys = 0; + + hr = pSensor->m_spSupportedSensorDataFields->GetCount(&cDfKeys); + + if (SUCCEEDED(hr)) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + + if (cDfKeys > 0) + { + CLIENT_ENTRY entry; + + if (pSensor->m_pClientMap.Lookup(appID, entry) == FALSE) + { + Trace(TRACE_LEVEL_ERROR, "Failed to find client %p in %s, hr = %!HRESULT!", appID, pSensor->m_SensorName, hr); + } + else + { + *pClientReportInterval = pSensor->m_pClientMap[appID].ulClientReportInterval; + *pClientLocationDesiredAccuracy = pSensor->m_pClientMap[appID].ulClientLocationDesiredAccuracy; + for (DWORD dwDataField = 0; dwDataField < MAX_NUM_DATA_FIELDS; dwDataField++) + { + pClientChangeSensitivities[dwDataField] = pSensor->m_pClientMap[appID].fltClientChangeSensitivity[dwDataField]; + } + + hr = S_OK; + } + } + } + } + + return hr; +} + +/*++ + +CSensorDDI::CheckForClient + + This method is called to check for a connected client. + +The driver should: + + Return a BOOL indicating whether this client is connected. + +--*/ +HRESULT CSensorDDI::CheckForClient( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID, + _Out_ BOOL* pfClientPresent) +{ + HRESULT hr = S_OK; + + *pfClientPresent = FALSE; + + if (nullptr != pSensor) + { + CLIENT_ENTRY entry; + + if (pSensor->m_pClientMap.Lookup(appID, entry) == FALSE) + { + //Trace(TRACE_LEVEL_INFORMATION, "Client %p was not found in the %s client list, %!HRESULT!", appID, pSensor->m_SensorName, hr); + } + else + { + *pfClientPresent = TRUE; + //Trace(TRACE_LEVEL_INFORMATION, "Client %p was found in the %s client list, %!HRESULT!", appID, pSensor->m_SensorName, hr); + } + } + else + { + hr = E_POINTER; + } + + return hr; +} + +/*++ + +CSensorDDI::RemoveClient + + This method is called to remove a connected client. + +The driver should: + + Return the client index. + +--*/ +HRESULT CSensorDDI::RemoveClient( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID) +{ + HRESULT hr = S_OK; + + if (nullptr != pSensor) + { + + if (pSensor->m_pClientMap.RemoveKey(appID) == FALSE) + { + // Entry is not in client list or could not be removed + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Client %p not found in the %s client list, hr = %!HRESULT!", appID, pSensor->m_SensorName, hr); + } + } + else + { + hr = E_POINTER; + } + + return hr; +} + +/*++ + +CSensorDDI::CheckForSubscriber + + This method is called to check for a subscribed client. + +The driver should: + + Return a BOOL indicating whether this client is subscribed. + +--*/ +HRESULT CSensorDDI::CheckForSubscriber( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID, + _Out_ BOOL* pfClientIsSubscribed) +{ + HRESULT hr = S_OK; + + *pfClientIsSubscribed = FALSE; + + if (nullptr != pSensor) + { + SUBSCRIBER_ENTRY entry; + + if (pSensor->m_pSubscriberMap.Lookup(appID, entry) == FALSE) + { + //Trace(TRACE_LEVEL_INFORMATION, "Client %p was not found in the %s subscriber list, %!HRESULT!", appID, pSensor->m_SensorName, hr); + } + else + { + *pfClientIsSubscribed = pSensor->m_pSubscriberMap[appID].fSubscribed; + //Trace(TRACE_LEVEL_INFORMATION, "Client %p was found in the %s subscriber list, %!HRESULT!", appID, pSensor->m_SensorName, hr); + } + } + else + { + hr = E_POINTER; + } + + return hr; +} + +/*++ + +CSensorDDI::RemoveSubscriber + + This method is called to remove a subscribed client. + +The driver should: + + Return the client index + +--*/ +HRESULT CSensorDDI::RemoveSubscriber( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID) +{ + HRESULT hr = S_OK; + + if (nullptr != pSensor) + { + + if (pSensor->m_pSubscriberMap.RemoveKey(appID) == FALSE) + { + //Entry is not in client list or could not be removed + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Client %p not found in the %s subscriber list, hr = %!HRESULT!", appID, pSensor->m_SensorName, hr); + } + } + else + { + hr = E_POINTER; + } + + return hr; +} + +/*++ + +FindSensorTypeFromObjectID + + This method is called to determine the sensor type and index from the object ID. + +The parameters sent to us are: + + Key - A PROPERTYKEY + +The driver should: + + Return the sensor type and the sensor index. + +--*/ +HRESULT CSensorDDI::FindSensorTypeFromObjectID( + _In_ LPWSTR pwszObjectID, + _Out_ SensorType* pSensorType, + _Out_ DWORD* pSensorIdx) +{ + HRESULT hr = E_POINTER; + CAtlStringW strObjectID = pwszObjectID; + CComBSTR objectId; + + if ((nullptr != pSensorType) && + (nullptr != pSensorIdx)) + { + if (nullptr != m_pSensorManager) + { + DWORD cSensorIDs = (DWORD) m_pSensorManager->m_AvailableSensorsIDs.GetCount(); + DWORD cSensorTypes = (DWORD) m_pSensorManager->m_AvailableSensorsTypes.GetCount(); + + if ((cSensorIDs < 1) || (cSensorTypes < 1)) + { + hr = E_UNEXPECTED; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + + POSITION posID = m_pSensorManager->m_AvailableSensorsIDs.GetStartPosition(); + POSITION posType = m_pSensorManager->m_AvailableSensorsTypes.GetStartPosition(); + SensorType sensType = SensorTypeNone; + DWORD idx = 0; + + while (NULL != posID) + { + objectId = m_pSensorManager->m_AvailableSensorsIDs.GetNextValue(posID); + sensType = m_pSensorManager->m_AvailableSensorsTypes.GetNextValue(posType); + + if (strObjectID.CompareNoCase(objectId) == 0) + { + *pSensorType = sensType; + *pSensorIdx = idx; + hr = S_OK; + + break; + } + + idx++; + } + + if (SUCCEEDED(hr)) + { + if (sensType == SensorTypeNone) + { + hr = E_UNEXPECTED; + } + } + } + } + else + { + hr = E_UNEXPECTED; + } + } + + return hr; +} + +/*++ + +CSensorDDI::IsSettablePropertyKey + + This method is called to determine if the given PROPERTYKEY is a settable property of the SENSOR object. + +The parameters sent to us are: + + Key - A PROPERTYKEY + +The driver should: + + Return TRUE if the PROPERTYKEY is supported; FALSE otherwise. + +--*/ +BOOL CSensorDDI::IsSettablePropertyKey( + _In_ DWORD sIndex, + _In_ PROPERTYKEY Key) +{ + HRESULT hr = S_OK; + CSensor* pSensor = nullptr; + + CComPtr spSensorProperties; + + BOOL fResult = FALSE; + + hr = GetSensorObject(sIndex, &pSensor); + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + pSensor->GetSettableProperties(&spSensorProperties); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor object, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr)) + { + fResult = FindPropertyKey(Key, spSensorProperties); + } + + return fResult; +} + +/*++ + +CSensorDDI::FindPropertyKey + + This method is called to determine if the given PROPERTYKEY can be found in the given collection + +The parameters sent to us are: + + Key - A PROPERTYKEY + pKeys - The collection to search in. + +The driver should: + + Return TRUE if the PROPERTYKEY is found; FALSE otherwise. + +--*/ +BOOL CSensorDDI::FindPropertyKey( + _In_ PROPERTYKEY Key, + _In_ IPortableDeviceKeyCollection *pKeys) +{ + HRESULT hr = S_OK; + + DWORD cKeys = 0; + PROPERTYKEY tempKey; + BOOL fResult = FALSE; + + if (NULL != pKeys) + { + hr = pKeys->GetCount(&cKeys); + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; ++dwIndex) + { + hr = pKeys->GetAt(dwIndex, &tempKey); + + if (SUCCEEDED(hr)) + { + if (IsEqualPropertyKey(tempKey, Key)) + { + fResult = TRUE; + break; + } + } + } + } + } + + return fResult; +} + +/*++ + +CSensorDDI::AddCommonPropertyKeys + + This method is called to populate common PROPERTYKEYs found on ALL objects. + +The parameters sent to us are: + + pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs + +The driver should: + + Add PROPERTYKEYs pertaining to the ALL objects. + +--*/ +VOID CSensorDDI::AddCommonPropertyKeys( + _In_ IPortableDeviceKeyCollection* pKeys) +{ + if (pKeys != NULL) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonProperties); dwIndex++) + { + pKeys->Add(g_SupportedCommonProperties[dwIndex]); + } + } +} + +/*++ + +CSensorDDI::AddDevicePropertyKeys + + This method is called to populate common PROPERTYKEYs found on the DEVICE object. + +The parameters sent to us are: + + pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs + +The driver should: + + Add PROPERTYKEYs pertaining to the DEVICE object. + +--*/ +VOID CSensorDDI::AddDevicePropertyKeys( + _In_ IPortableDeviceKeyCollection* pKeys) +{ + if (pKeys != NULL) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedDeviceProperties); dwIndex++) + { + pKeys->Add(g_SupportedDeviceProperties[dwIndex]); + } + } +} + +/*++ + +CSensorDDI::AddSensorPropertyKeys + + This method is called to populate common PROPERTYKEYs found on sensor objects. + +The parameters sent to us are: + pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs + +The driver should: + Add PROPERTYKEYs pertaining to the sensor objects. + +--*/ +HRESULT CSensorDDI::AddSensorPropertyKeys( + _In_ SensorType sensType, + _In_ DWORD sensIndex, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + UNREFERENCED_PARAMETER(sensType); + + HRESULT hr = S_OK; + + CComPtr spSensorProperties; + + if (NULL != pKeys) + { + // Add the supported properties + CSensor* pSensor = nullptr; + + hr = GetSensorObject(sensIndex, &pSensor); + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + hr = pSensor->GetSupportedProperties(&spSensorProperties); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor object, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr)) + { + hr = CopyPropertyKeys(spSensorProperties, pKeys); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "pKeys or pSensorManager is null, hr = %!HRESULT!", hr); + } + + return hr; +} + +/*++ + +CSensorDDI::AddSensorDataFieldKeys + + This method is called to populate common PROPERTYKEYs found on sensor objects. + +The parameters sent to us are: + + pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs + +The driver should: + + Add PROPERTYKEYs pertaining to the sensor objects. + +--*/ +HRESULT CSensorDDI::AddSensorDataFieldKeys( + _In_ SensorType sensType, + _In_ DWORD sensIndex, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + UNREFERENCED_PARAMETER(sensType); + + HRESULT hr = S_OK; + + CComPtr spSensorDataFields; + + if (NULL != pKeys) + { + // Add the supported datafields + CSensor* pSensor = nullptr; + + hr = GetSensorObject(sensIndex, &pSensor); + + if (SUCCEEDED(hr) && (nullptr != pSensor)) + { + hr = pSensor->GetSupportedDataFields(&spSensorDataFields); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor object, hr = %!HRESULT!", hr); + } + + if (SUCCEEDED(hr)) + { + hr = CopyPropertyKeys(spSensorDataFields, pKeys); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "pKeys or pSensorManager is null, hr = %!HRESULT!", hr); + } + + return hr; +} + +/*++ + +CSensorDDI::AddSupportedPropertyKeys + + This method is called to populate supported PROPERTYKEYs found on objects. + +The parameters sent to us are: + + wszObjectID - the object whose supported property keys are being requested + pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs + +The driver should: + + Add PROPERTYKEYs pertaining to the specified object. + +--*/ +HRESULT CSensorDDI::AddSupportedPropertyKeys( + _In_ LPWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + CAtlStringW strObjectID = wszObjectID; + + // Add Common PROPERTYKEYs for ALL WPD objects + AddCommonPropertyKeys(pKeys); + + if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0) + { + // Add the PROPERTYKEYs for the 'DEVICE' object + AddDevicePropertyKeys(pKeys); + } + + // Add the PROPERTYKEYs for the 'SENSOR' object + SensorType sType; + DWORD sIndex; + + hr = FindSensorTypeFromObjectID(wszObjectID, &sType, &sIndex); + + if (SUCCEEDED(hr)) + { + hr = AddSensorPropertyKeys(sType, sIndex, pKeys); + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", wszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", wszObjectID, hr); + } + else + { + //WPD is calling us, so ignore trace + } + } + + return hr; +} + +/*++ + +CSensorDDI::AddSupportedDataFieldKeys + + This method is called to populate supported PROPERTYKEYs found on data objects. + +The parameters sent to us are: + + wszObjectID - the object whose supported property keys are being requested + pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs + +The driver should: + + Add PROPERTYKEYs pertaining to the specified object. + +--*/ +HRESULT CSensorDDI::AddSupportedDataFieldKeys( + _In_ LPWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + CAtlStringW strObjectID = wszObjectID; + + // Add the DATAFIELD keys for the 'SENSOR' object + SensorType sType; + DWORD sIndex; + + hr = FindSensorTypeFromObjectID(wszObjectID, &sType, &sIndex); + + if (SUCCEEDED(hr)) + { + hr = AddSensorDataFieldKeys(sType, sIndex, pKeys); + } + else + { + hr = E_UNEXPECTED; + + if (TRUE == wcscmp(L"DEVICE", wszObjectID)) + { + Trace(TRACE_LEVEL_ERROR, "Device is %ws not a supported sensor, hr = %!HRESULT!", wszObjectID, hr); + } + else + { + //WPD is calling us, so ignore trace + } + } + + return hr; +} + +/*++ + +CSensorDDI::SelectClientReportInterval + + This method is called to update choose a client Report Interval. + +The parameters sent to us are: + + pSensor - the sensor to use + +--*/ +HRESULT CSensorDDI::SelectClientReportInterval( + _In_ CSensor* pSensor) +{ + HRESULT hr = S_OK; + + if (pSensor == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + if (SUCCEEDED(hr)) + { + ULONG shortestReportInterval = UINT_MAX; + ULONG clientReportInterval = 0; + size_t cClients = pSensor->m_pClientMap.GetCount(); + IWDFFile* pClient = nullptr; + POSITION posClient = nullptr; + CLIENT_ENTRY entry; + BOOL fReportIntervalChosen = FALSE; + + if (cClients == 0) + { + pSensor->m_fReportingState = FALSE; + pSensor->m_ulPowerState = SENSOR_POWER_STATE_POWER_OFF; + + pSensor->m_ulLowestClientReportInterval = pSensor->m_ulDefaultCurrentReportInterval; + } + else + { + posClient = pSensor->m_pClientMap.GetStartPosition(); + + // find the lowest value in the client report interval list + while (posClient != NULL) + { + pClient = pSensor->m_pClientMap.GetKeyAt(posClient); + pSensor->m_pClientMap.Lookup(pClient, entry); + clientReportInterval = pSensor->m_pClientMap[pClient].ulClientReportInterval; + if (0 == clientReportInterval) + { + // exclude this client from report interval calculations; + } + else if ((clientReportInterval > 0) && (clientReportInterval < shortestReportInterval)) + { + fReportIntervalChosen = TRUE; + shortestReportInterval = clientReportInterval; + } + + pSensor->m_pClientMap.GetNextKey(posClient); + } + + // if no clients have specified a report interval, then use the default + if (UINT_MAX == shortestReportInterval) + { + shortestReportInterval = pSensor->m_ulDefaultCurrentReportInterval; + } + + // range check to be sure is not less than Min Report Interval + if (shortestReportInterval < pSensor->m_ulDefaultMinimumReportInterval) + { + shortestReportInterval = pSensor->m_ulDefaultMinimumReportInterval; + } + + pSensor->m_ulLowestClientReportInterval = shortestReportInterval; + + if (TRUE == fReportIntervalChosen) + { + pSensor->m_fReportingState = TRUE; + pSensor->m_ulPowerState = SENSOR_POWER_STATE_FULL_POWER; + } + else + { + pSensor->m_fReportingState = FALSE; + pSensor->m_ulPowerState = SENSOR_POWER_STATE_LOW_POWER; + } + + pSensor->CheckLongReportIntervalTimer(); + } + } + + return hr; +} + + +/*++ + +CSensorDDI::SelectClientLocationDesiredAccuracy + + This method is called to update choose a client location desired accuracy. + +The parameters sent to us are: + + pSensor - the sensor to use + +--*/ +HRESULT CSensorDDI::SelectClientLocationDesiredAccuracy( + _In_ CSensor* pSensor) +{ + HRESULT hr = S_OK; + + if (pSensor == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + if (SUCCEEDED(hr)) + { + ULONG shortestLocationDesiredAccuracy = UINT_MAX; + ULONG clientLocationDesiredAccuracy = 0; + size_t cClients = pSensor->m_pClientMap.GetCount(); + IWDFFile* pClient = nullptr; + POSITION posClient = nullptr; + CLIENT_ENTRY entry; + + if (cClients == 0) + { + pSensor->m_ulLowestClientLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + else + { + posClient = pSensor->m_pClientMap.GetStartPosition(); + + // find the lowest value in the client report interval list + while (posClient != NULL) + { + pClient = pSensor->m_pClientMap.GetKeyAt(posClient); + pSensor->m_pClientMap.Lookup(pClient, entry); + clientLocationDesiredAccuracy = pSensor->m_pClientMap[pClient].ulClientLocationDesiredAccuracy; + if (0 == clientLocationDesiredAccuracy) + { + // exclude this client from report interval calculations; + } + else if ((clientLocationDesiredAccuracy > 0) && (clientLocationDesiredAccuracy < shortestLocationDesiredAccuracy)) + { + shortestLocationDesiredAccuracy = clientLocationDesiredAccuracy; + } + + pSensor->m_pClientMap.GetNextKey(posClient); + } + + // if no clients have specified a location desired accuracy, then use the default + if (UINT_MAX == shortestLocationDesiredAccuracy) + { + shortestLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + + // range check to be sure is less than Max Location Desired Accuracy + if (shortestLocationDesiredAccuracy > LOCATION_DESIRED_ACCURACY_HIGH) + { + shortestLocationDesiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + + pSensor->m_ulLowestClientLocationDesiredAccuracy = shortestLocationDesiredAccuracy; + } + } + + return hr; +} + +/*++ + +CSensorDDI::SelectClientChangeSensitivity + + This method is called to choose a client Change Sensitivity. + +The parameters sent to us are: + pSensor - the sensor to use + +--*/ +HRESULT CSensorDDI::SelectClientChangeSensitivity( + _In_ CSensor* pSensor) +{ + HRESULT hr = S_OK; + + if (pSensor == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + // Update our cache + if (SUCCEEDED(hr)) + { + CComPtr spDfVals = nullptr; + FLOAT clientChangeSensitivity = 0; + FLOAT lowestChangeSensitivity = FLT_MAX; + size_t cClients = pSensor->m_pClientMap.GetCount(); + IWDFFile* pClient = nullptr; + POSITION posClient = nullptr; + CLIENT_ENTRY entry; + + PROPERTYKEY pkDfKey; + DWORD cDfKeys = 0; + + // CoCreate a collection to store the property set operation results. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spDfVals)); + + if (SUCCEEDED(hr)) + { + hr = pSensor->m_spSupportedSensorDataFields->GetCount(&cDfKeys); + } + + if (SUCCEEDED(hr)) + { + for (ULONG uDfIdx = 1; uDfIdx < cDfKeys; uDfIdx++) // ignore the timestamp, the first datafield + { + pSensor->m_spSupportedSensorDataFields->GetAt(uDfIdx, &pkDfKey); + + if (SUCCEEDED(hr)) + { + if (cClients == 0) + { + pSensor->m_fltLowestClientChangeSensitivities[uDfIdx] = pSensor->m_fltDefaultChangeSensitivity; + } + else + { + lowestChangeSensitivity = FLT_MAX; + + posClient = pSensor->m_pClientMap.GetStartPosition(); + + //find the lowest value in the client change sensitivity list + while (posClient != NULL) + { + pClient = pSensor->m_pClientMap.GetKeyAt(posClient); + pSensor->m_pClientMap.Lookup(pClient, entry); + + if (cClients > 0) + { + clientChangeSensitivity = pSensor->m_pClientMap[pClient].fltClientChangeSensitivity[uDfIdx]; + + if (clientChangeSensitivity < 0.0F) + { + //exclude this client from change sensitivity calculations + } + else if ((clientChangeSensitivity >= 0.0F) && (clientChangeSensitivity < lowestChangeSensitivity)) + { + lowestChangeSensitivity = clientChangeSensitivity; + } + } + + pSensor->m_pClientMap.GetNextKey(posClient); + } + + //if no clients have a specified change sensitivity, then use the default + if (FLT_MAX == lowestChangeSensitivity) + { + lowestChangeSensitivity = pSensor->m_fltDefaultChangeSensitivity; + } + + //range check to be sure is not less than 0.0F + if (lowestChangeSensitivity < 0.0F) + { + lowestChangeSensitivity = pSensor->m_fltDefaultChangeSensitivity; + } + + pSensor->m_fltLowestClientChangeSensitivities[uDfIdx] = lowestChangeSensitivity; + } + } + } + } + } + + return hr; +} + +HRESULT CSensorDDI::GetSensorObject( + _In_ ULONG ulIndex, + _Out_ CSensor** ppSensor) +{ + HRESULT hr = S_OK; + + if (SUCCEEDED(hr)) + { + if (nullptr != m_pSensorManager) + { + POSITION pos = NULL; + + pos = m_pSensorManager->m_pSensorList.FindIndex(ulIndex); + + if (NULL != pos) + { + *ppSensor = m_pSensorManager->m_pSensorList.GetAt(pos); + + if (nullptr == *ppSensor) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to find sensor position, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "pSensorManger is null, hr = %!HRESULT!", hr); + } + } + + return hr; +} + +HRESULT CSensorDDI::GetGeolocationSensorObject( + _Out_ CSensor** ppSensor) +{ + HRESULT hr = S_OK; + *ppSensor = nullptr; + + if (SUCCEEDED(hr)) + { + if (nullptr != m_pSensorManager) + { + for (DWORD i = 0; i < m_pSensorManager->m_pSensorList.GetCount(); i++) + { + POSITION pos = m_pSensorManager->m_pSensorList.FindIndex(i); + if (nullptr != pos) + { + CSensor* pSensorTemp = m_pSensorManager->m_pSensorList.GetAt(pos); + + if (nullptr == pSensorTemp) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor, hr = %!HRESULT!", hr); + break; + } + else + { + if (Geolocation == pSensorTemp->GetSensorType()) + { + *ppSensor = pSensorTemp; + } + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "pos is null, hr = %!HRESULT!", hr); + break; + } + } + + if (nullptr == *ppSensor) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to find sensor position, hr = %!HRESULT!", hr); + } + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "pSensorManger is null, hr = %!HRESULT!", hr); + } + } + + return hr; +} + +inline HRESULT CSensorDDI::EnterProcessing(DWORD64 dwControlFlag) +{ + return m_pSensorManager->EnterProcessing(dwControlFlag); +} + +inline void CSensorDDI::ExitProcessing(DWORD64 dwControlFlag) +{ + m_pSensorManager->ExitProcessing(dwControlFlag); +} \ No newline at end of file diff --git a/src/FakeGPS.Driver/SensorDdi.h b/src/FakeGPS.Driver/SensorDdi.h new file mode 100644 index 0000000..d148b43 --- /dev/null +++ b/src/FakeGPS.Driver/SensorDdi.h @@ -0,0 +1,239 @@ +// 2016 OK + +/*++ + +Module: + + SensorDDI.h + +Description: + + This module contains the type definitions for the + ISensorDriver interface which is used by the Sensor Class Extension. + +--*/ + +#pragma once + +class CSensorManager; //forward declaration + +class CSensorDDI : + public CComObjectRoot, + public ISensorDriver +{ +public: + CSensorDDI(); + virtual ~CSensorDDI(); + + DECLARE_NOT_AGGREGATABLE(CSensorDDI) + + BEGIN_COM_MAP(CSensorDDI) + COM_INTERFACE_ENTRY(ISensorDriver) + END_COM_MAP() + + // Public method +public: + + HRESULT InitSensorDevice(_In_ IWDFDevice* pWdfDevice); + HRESULT DeInitSensorDevice(); + HRESULT RequestDeviceInfo( + _Inout_ SensorType* pSensType, + // this SAL annotation required to be very specific about size of + // buffer that is being passed in. With just a normal _In_out or + //_Out_ annotation, these are seen as possible buffer overruns. + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszManufacturer, + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszProduct, + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszSerialNumber, + _Inout_updates_(DESCRIPTOR_MAX_LENGTH) LPWSTR pwszDeviceID); + +public: + + HRESULT UpdateSensorPropertyValues( + _In_ LPWSTR ObjectID, + _In_ BOOL fSettableOnly); + + HRESULT UpdateSensorDataFieldValues( + _In_ LPWSTR ObjectID); + + HRESULT CheckForSubscriber( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID, + _Out_ BOOL* pfClientIsSubscribed); + + HRESULT RemoveSubscriber( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID); + + inline HRESULT EnterProcessing(DWORD64 dwControlFlag); + inline void ExitProcessing(DWORD64 dwControlFlag); + + // COM Interface methods +public: + + // ISensorDriver methods. + HRESULT STDMETHODCALLTYPE OnGetSupportedSensorObjects( + _Out_ IPortableDeviceValuesCollection** ppSensorObjectCollection); + + HRESULT STDMETHODCALLTYPE OnGetSupportedProperties( + _In_ LPWSTR pwszObjectID, + _Out_ IPortableDeviceKeyCollection** ppSupportedProperties); + + HRESULT STDMETHODCALLTYPE OnGetSupportedDataFields( + _In_ LPWSTR pwszObjectID, + _Out_ IPortableDeviceKeyCollection** ppSupportedDataFields); + + HRESULT STDMETHODCALLTYPE OnGetSupportedEvents( + _In_ LPWSTR pwszObjectID, + _Out_ GUID** ppSupportedEvents, + _Out_ ULONG* pulEventCount); + + HRESULT STDMETHODCALLTYPE OnGetProperties( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID, + _In_ IPortableDeviceKeyCollection* pProperties, + _Out_ IPortableDeviceValues** ppPropertyValues); + + HRESULT STDMETHODCALLTYPE OnGetDataFields( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID, + _In_ IPortableDeviceKeyCollection* pDataFields, + _Out_ IPortableDeviceValues** ppDataValues); + + HRESULT STDMETHODCALLTYPE OnSetPropertiesOriginal( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID, + _In_ IPortableDeviceValues* pPropertiesToSet, + _Out_ IPortableDeviceValues** ppResults); + + HRESULT STDMETHODCALLTYPE OnSetProperties( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID, + _In_ IPortableDeviceValues* pPropertiesToSet, + _Out_ IPortableDeviceValues** ppResults); + + HRESULT STDMETHODCALLTYPE OnClientConnect( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID); + + HRESULT STDMETHODCALLTYPE OnClientDisconnect( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID); + + HRESULT STDMETHODCALLTYPE OnClientSubscribeToEvents( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID); + + HRESULT STDMETHODCALLTYPE OnClientUnsubscribeFromEvents( + _In_ IWDFFile* pClientFile, + _In_ LPWSTR pwszObjectID); + + HRESULT STDMETHODCALLTYPE OnProcessWpdMessage( + _In_ IUnknown* pUnkPortableDeviceValuesParams, + _In_ IUnknown* pUnkPortableDeviceValuesResults); + + // Radio Management IO functions + HRESULT OnGetRadioState( + _In_ IWDFIoRequest* pRequest, + _In_ bool fPreviousState); + + HRESULT OnSetRadioState( + _In_ IWDFIoRequest* pRequest, + _In_ bool fPreviousState); + + // Private member functions +private: + + HRESULT OnGetPropertyValues( + _In_ IWDFFile* appId, + _In_ LPWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _Out_ IPortableDeviceValues** ppPortableDeviceValues); + + HRESULT FindClientFromAppID( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID, + _Out_ ULONG* pClientReportInterval, + _Out_ ULONG* pClientLocationDesiredAccuracy, + // this SAL annotation required to be very specific about size of + // buffer that is being passed in. With just a normal _Inout_ or + //_Out_ annotation, these are seen as possible buffer overruns + _Inout_updates_(MAX_NUM_DATA_FIELDS) FLOAT* pClientChangeSensitivities); + + HRESULT CheckForClient( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID, + _Out_ BOOL* pfClientPresent); + + HRESULT RemoveClient( + _In_ CSensor* pSensor, + _In_ IWDFFile* appID); + + VOID AddDevicePropertyKeys( + _In_ IPortableDeviceKeyCollection* pKeys); + + VOID AddCommonPropertyKeys( + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT AddSensorPropertyKeys( + _In_ SensorType sensType, + _In_ DWORD sensIndex, + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT AddSupportedPropertyKeys( + _In_ LPWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT AddSensorDataFieldKeys( + _In_ SensorType sensType, + _In_ DWORD sensIndex, + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT AddSupportedDataFieldKeys( + _In_ LPWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT CopyPropertyKeys( + _In_ IPortableDeviceKeyCollection *pSourceKeys, + _In_ IPortableDeviceKeyCollection *pTargetKeys); + + BOOL FindPropertyKey( + _In_ PROPERTYKEY Key, + _In_ IPortableDeviceKeyCollection *pKeys); + + BOOL IsSettablePropertyKey( + _In_ DWORD sIndex, + _In_ PROPERTYKEY Key); + + HRESULT FindSensorTypeFromObjectID( + _In_ LPWSTR pwszObjectID, + _Out_ SensorType* pSensorType, + _Out_ DWORD* pSensorIdx); + + HRESULT SelectClientReportInterval( + _In_ CSensor* pSensor); + + HRESULT SelectClientLocationDesiredAccuracy( + _In_ CSensor* pSensor); + + HRESULT SelectClientChangeSensitivity( + _In_ CSensor* pSensor); + + HRESULT GetSensorObject( + _In_ ULONG ulIndex, + _Out_ CSensor** ppSensor); + + HRESULT GetGeolocationSensorObject( + _Out_ CSensor** ppSensor); + + friend class CSensorManager; + +public: + CSensorManager* m_pSensorManager; + + // Private data members. +private: + LONG m_nNotifyOnSensorUpdate; + + CComAutoCriticalSection m_CriticalSection; // This is used to make all calls to polled data requests thread safe + CComAutoCriticalSection m_CriticalSectionConnectDisconnect; // This is used to make all connect/disconnect calls thread safe +}; \ No newline at end of file diff --git a/src/FakeGPS.Driver/SensorManager.cpp b/src/FakeGPS.Driver/SensorManager.cpp new file mode 100644 index 0000000..241a9d8 --- /dev/null +++ b/src/FakeGPS.Driver/SensorManager.cpp @@ -0,0 +1,1119 @@ +// 2016 OK + +/*++ + +Module: + + SensorManager.cpp + +Description: + + Implements the CSensorManager container class + +--*/ + +#include "Internal.h" +#include "SensorDDI.h" +#include "FakeGPS.h" +#include "Sensor.h" +#include "GeolocationSensor.h" +#include "SensorManager.h" +#include "SensorManager.tmh" + +/*++ + +CSensorManager::CSensorManager + + Object constructor function + +--*/ +CSensorManager::CSensorManager() : + m_spWdfDevice(NULL), + m_spClassExtension(NULL), + m_pSensorDDI(NULL), + m_hSensorEvent(NULL), + m_hSensorManagerEventingThread(NULL), + m_fSensorManagerInitialized(FALSE), + m_fThreadActive(FALSE), + m_pDevice(NULL), + m_fDeviceActive(false) +{ + +} + + +/*++ + +CSensorManager::~CSensorManager + + Object destructor function + +--*/ +CSensorManager::~CSensorManager() +{ + SAFE_RELEASE(m_pSensorDDI); + SAFE_RELEASE(m_pDevice); +} + +/*++ + +CSensorManager::Initialize + + Merely store the device pointer. + The rest of the init will be done in Start(). + This is because init depends on communication with the device. + +--*/ +HRESULT CSensorManager::Initialize(_In_ IWDFDevice* pWdfDevice, _In_ CFakeGPS* pDevice) +{ + HRESULT hr = (FALSE == IsInitialized()) ? S_OK : E_UNEXPECTED; + + if (SUCCEEDED(hr)) + { + // Store the IWDF Device pointer + m_spWdfDevice = pWdfDevice; + m_fInitializationComplete = FALSE; + m_NumMappedSensors = 0; + m_pDevice = pDevice; + m_pDevice->AddRef(); + m_fDeviceStopped = FALSE; + } + + return hr; +} + +/*++ + +CSensorManager::Uninitialize + +--*/ +void CSensorManager::Uninitialize() +{ + // Free all the sensor objects + for (DWORD i = 0; i < m_pSensorList.GetCount(); i++) + { + CSensor *pSensor = m_pSensorList.GetAt(m_pSensorList.FindIndex(i)); + + if (nullptr != pSensor) + { + pSensor->Uninitialize(); + + delete pSensor; + } + } + + // Clear the Sensor list and Sensor map + m_pSensorList.RemoveAll(); + m_AvailableSensorsIDs.RemoveAll(); + m_AvailableSensorsTypes.RemoveAll(); + + // Release Sensor Class Extension and Sensor DDI + if (NULL != m_spClassExtension) + { + m_spClassExtension->Uninitialize(); + m_spClassExtension.Release(); + } + + SAFE_RELEASE(m_pSensorDDI); + + m_fSensorManagerInitialized = FALSE; + + return; +} + +/*++ + +CSensorManager::InitializeClassExtension + +--*/ +HRESULT CSensorManager::InitializeClassExtension() +{ + HRESULT hr = (NULL == m_spClassExtension) ? S_OK : E_UNEXPECTED; + + if (SUCCEEDED(hr)) + { + // CoCreate the Sensor ClassExtension + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_SensorClassExtension, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&m_spClassExtension)); + + if (REGDB_E_CLASSNOTREG == hr) + { + Trace(TRACE_LEVEL_ERROR, "Class is not registered, hr = %!HRESULT!", hr); + hr = E_UNEXPECTED; + m_spClassExtension = NULL; + } + } + + // Initialize Sensor ClassExtension + if (SUCCEEDED(hr)) + { + CComPtr spIUnknown; + hr = m_pSensorDDI->QueryInterface(IID_IUnknown, (void**) &spIUnknown); + + if (SUCCEEDED(hr)) + { + if (NULL != m_spClassExtension) + { + if (nullptr != m_spWdfDevice) + { + hr = m_spClassExtension->Initialize(m_spWdfDevice, spIUnknown); + } + else + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + hr = m_spWdfDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_GPS_RADIO_MANAGEMENT, nullptr); + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! CreateDeviceInterface for Radio management returned hr = %!HRESULT!", hr); + + if (SUCCEEDED(hr)) + { + hr = m_spWdfDevice->AssignDeviceInterfaceState(&GUID_DEVINTERFACE_GPS_RADIO_MANAGEMENT, nullptr, TRUE); + } + } + } + } + } + } + + return hr; +} + +/*++ + +CSensorManager::Start + +--*/ +HRESULT CSensorManager::Start() +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + HRESULT hr; + + // Create the sensor DDI if not done already + if (NULL == m_pSensorDDI) + { + // Create the SensorDDI object that implements ISensorDriver + hr = CComObject::CreateInstance(&m_pSensorDDI); + if ((SUCCEEDED(hr)) && (NULL != m_pSensorDDI)) + { + m_pSensorDDI->AddRef(); + } + } + + // Always initialize the sensor DDI on Start() + if (NULL != m_pSensorDDI) + { + hr = m_pSensorDDI->InitSensorDevice(m_spWdfDevice); + m_pSensorDDI->m_pSensorManager = this; + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "SensorDDI pointer is NULL, hr = %!HRESULT!", hr); + } + + // Init sensor driver the first time Start() is called. This is done + // here vs. in Initialize() because we need to contact the device to + // determine its type before init. + if (SUCCEEDED(hr) && FALSE == IsInitialized()) + { + // Get the device's report description to determine + // the sensor and report type + SensorType sensType; + + WCHAR* tempStr = nullptr; + WCHAR* sensorID = nullptr; + WCHAR* deviceID = nullptr; + WCHAR* pwszManufacturer = nullptr; + WCHAR* pwszProduct = nullptr; + WCHAR* pwszSerialNumber = nullptr; + + try + { + tempStr = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for temp string, hr = %!HRESULT!", hr); + + if (nullptr != tempStr) + { + delete [] tempStr; + } + } + + try + { + sensorID = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for sensor ID string, hr = %!HRESULT!", hr); + + if (nullptr != sensorID) + { + delete [] sensorID; + } + } + + try + { + deviceID = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for device ID string, hr = %!HRESULT!", hr); + + if (nullptr != deviceID) + { + delete [] deviceID; + } + } + + try + { + pwszManufacturer = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for manufacturer string, hr = %!HRESULT!", hr); + + if (nullptr != pwszManufacturer) + { + delete [] pwszManufacturer; + } + } + + try + { + pwszProduct = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for product string, hr = %!HRESULT!", hr); + + if (nullptr != pwszProduct) + { + delete [] pwszProduct; + } + } + + try + { + pwszSerialNumber = new WCHAR[MAX_PATH]; + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to allocate memory for serial number string, hr = %!HRESULT!", hr); + + if (nullptr != pwszSerialNumber) + { + delete [] pwszSerialNumber; + } + } + + if (SUCCEEDED(hr)) + { + int numSensors = 0; + +#pragma warning(push) + +#pragma warning(disable:26035) + + // the OACR warning is being disabled here because it expects a null-terminated string + // for this particular use. However, string termination happens in the called method + hr = m_pSensorDDI->RequestDeviceInfo(&sensType, pwszManufacturer, pwszProduct, pwszSerialNumber, deviceID); + +#pragma warning(pop) + + if (SUCCEEDED(hr)) + { + CSensor* pSensor = NULL; + CGeolocationSensor* pGeolocation = NULL; + + if (sensType == Collection) + { + numSensors = (int) m_AvailableSensorsTypes.GetCount(); + } + else + { + numSensors = 1; + } + + m_NumMappedSensors = numSensors; + + // Build the sensor objects from the sensor map + if ((SUCCEEDED(hr)) && (numSensors > 0)) + { + for (int idx = 0; idx < numSensors; idx++) + { + if (((sensType < FirstSensorType) || (sensType > LastSensorType)) && sensType != Collection) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Invalid sensor type, hr = %!HRESULT!", hr); + } + else + { + switch (m_AvailableSensorsTypes[idx]) + { + + case Geolocation: + wcscpy_s(sensorID, DESCRIPTOR_MAX_LENGTH, deviceID); + swprintf_s(tempStr, SENSOR_ID_APPENDIX_MAX_LENGTH, L"-%i", idx); + wcscat_s(sensorID, DESCRIPTOR_MAX_LENGTH, tempStr); + m_AvailableSensorsIDs[idx] = sensorID; + + try + { + pGeolocation = new CGeolocationSensor(); + } + catch (...) + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Unable to create Geolocation object, hr = %!HRESULT!", hr); + + if (nullptr != pGeolocation) + { + delete pGeolocation; + } + } + + if (SUCCEEDED(hr)) + { + hr = pGeolocation->Initialize( + Geolocation, + idx, + pwszManufacturer, + pwszProduct, + pwszSerialNumber, + sensorID, + m_spWdfDevice, + this); + } + + if (SUCCEEDED(hr)) + { + pSensor = (CSensor*) pGeolocation; + Trace(TRACE_LEVEL_INFORMATION, "Geolocation sensor successfully created, Sensor ID = %ls, hr = %!HRESULT!", sensorID, hr); + } + break; + + default: + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Invalid sensor type, hr = %!HRESULT!", hr); + break; + } + } + + // Set the unique persistent ID + if (SUCCEEDED(hr)) + { + hr = pSensor->SetUniqueID(m_spWdfDevice); + } + + if (SUCCEEDED(hr)) + { + m_pSensorList.AddTail(pSensor); + } + + if (SUCCEEDED(hr)) + { + CComPtr spReportInterval = NULL; + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &spReportInterval); + if (SUCCEEDED(hr)) + { + hr = spReportInterval->SetUnsignedIntegerValue(SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, DEFAULT_SLEEP_REPORT_INTERVAL); + } + + if (SUCCEEDED(hr)) + { + hr = m_pSensorDDI->UpdateSensorPropertyValues(sensorID, FALSE); + } + + spReportInterval.Release(); + } + } + } + + // Create and initialize the Sensor Class Extension + if (SUCCEEDED(hr)) + { + hr = InitializeClassExtension(); + } + + if (SUCCEEDED(hr)) + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Sensor Class Extension initialized"); + m_fSensorManagerInitialized = TRUE; + m_fInitializationComplete = TRUE; + } + } + else + { + Trace(TRACE_LEVEL_ERROR, "%!FUNC! Failed to get device info"); + } + } + +#pragma warning(suppress: 6001) // uninitialized memory + + if (nullptr != tempStr) + { + delete [] tempStr; + } + +#pragma warning(suppress: 6001) // uninitialized memory + + if (nullptr != sensorID) + { + delete [] sensorID; + } + +#pragma warning(suppress: 6001) // uninitialized memory + + if (nullptr != deviceID) + { + delete [] deviceID; + } + +#pragma warning(suppress: 6001) // uninitialized memory + + if (nullptr != pwszManufacturer) + { + delete [] pwszManufacturer; + } + +#pragma warning(suppress: 6001) // uninitialized memory + + if (nullptr != pwszProduct) + { + delete [] pwszProduct; + } + +#pragma warning(suppress: 6001) // uninitialized memory + + if (nullptr != pwszSerialNumber) + { + delete [] pwszSerialNumber; + } + } + + // Update each sensor state to SENSOR_STATE_NO_DATA + // Note: CSensorManager::_SensorEventThreadProc() will change sensor state to SENSOR_STATE_READY whenever a data field is ready. + if (SUCCEEDED(hr)) + { + for (DWORD i = 0; i < m_pSensorList.GetCount(); i++) + { + CSensor *pSensor; + + POSITION pos = NULL; + pos = m_pSensorList.FindIndex(i); + + if (NULL != pos) + { + pSensor = m_pSensorList.GetAt(pos); + + if (nullptr != pSensor) + { + bool fStateChanged = FALSE; + + if (Geolocation == pSensor->m_SensorType) + { + SetState(pSensor, SENSOR_STATE_INITIALIZING, &fStateChanged); + } + else + { + SetState(pSensor, SENSOR_STATE_NO_DATA, &fStateChanged); + } + } + else + { + hr = E_UNEXPECTED; + } + } + else + { + hr = E_UNEXPECTED; + } + } + } + + if (SUCCEEDED(hr)) + { + // Step 1: Create the Data Changed Event Handle + m_hSensorEvent = ::CreateEvent(NULL, // No security attributes + FALSE, // Automatic-reset event object + FALSE, // Initial state is non-signaled + NULL); // Unnamed object + + POSITION pos = NULL; + CSensor* pSensor = nullptr; + + for (DWORD i = 0; i < m_pSensorList.GetCount(); i++) + { + pos = m_pSensorList.FindIndex(i); + if (NULL != pos) + { + pSensor = m_pSensorList.GetAt(pos); + if (nullptr != pSensor) + { + pSensor->SetDataEventHandle(m_hSensorEvent); + pSensor->m_fInitialDataReceived = FALSE; + } + else + { + hr = E_UNEXPECTED; + } + } + else + { + hr = E_UNEXPECTED; + } + } + + // Step 2: Activate & Create and start the eventing thread + if (SUCCEEDED(hr)) + { + Activate(); + + m_hSensorManagerEventingThread = ::CreateThread( + NULL, // Cannot be inherited by child process + 0, // Default stack size + &CSensorManager::_SensorEventThreadProc, // Thread proc + (LPVOID)this, // Thread proc argument + 0, // Starting state = running + NULL); + + if (nullptr == m_hSensorManagerEventingThread) + { + Trace(TRACE_LEVEL_ERROR, "%!FUNC! sensor event thread failed to create"); + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + else + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! sensor event thread successfully created"); + } + + }// No thread identifier + + if (SUCCEEDED(hr)) + { + m_fSensorManagerInitialized = TRUE; + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Sensor initialization completed successfully"); + } + else + { + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Sensor initialization failed"); + } + } + + if (SUCCEEDED(hr)) + { + m_fDeviceStopped = FALSE; + } + + + return hr; +} + +/*++ + +CSensorManager::Stop + +--*/ +HRESULT CSensorManager::Stop() +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry"); + + CComCritSecLock scopeLock(m_CriticalSection); + + HRESULT hr = S_OK; + + if (FALSE == m_fDeviceStopped) + { + // Update each sensor state to SENSOR_STATE_NO_DATA + for (DWORD i = 0; i < m_pSensorList.GetCount(); i++) + { + CSensor *pSensor = nullptr; + CComBSTR objectId; + POSITION pos = NULL; + + pos = m_pSensorList.FindIndex(i); + if (NULL != pos) + { + pSensor = m_pSensorList.GetAt(pos); + + if (nullptr != pSensor) + { + if (false == pSensor->m_fUsingLongReportIntervalTimer) + { + bool fStateChanged = FALSE; + if (Geolocation == pSensor->m_SensorType) + { + // NOTE: Only geolocation devices are initialized to + // SENSOR_STATE_INITIALIZING. All other sensors are initialized + // to SENSOR_STATE_NO_DATA + SetState(pSensor, SENSOR_STATE_INITIALIZING, &fStateChanged); + } + else + { + SetState(pSensor, SENSOR_STATE_NO_DATA, &fStateChanged); + } + } + } + else + { + hr = E_UNEXPECTED; + } + } + else + { + hr = E_UNEXPECTED; + } + } + + // DeInitialize the sensor device + if (NULL != m_pSensorDDI) + { + hr = m_pSensorDDI->DeInitSensorDevice(); + } + + // Step 1: Stop the eventing thread and Close the handle + if (NULL != m_hSensorManagerEventingThread) + { + // De-activate and close the thread + DeActivate(); + WaitForSingleObject(m_hSensorManagerEventingThread, INFINITE); + CloseHandle(m_hSensorManagerEventingThread); + } + + // Step 2: Close the Data Change Event handle + if (NULL != m_hSensorEvent) + { + CloseHandle(m_hSensorEvent); + } + + m_fDeviceStopped = TRUE; + } + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit, hr = %!HRESULT!", hr); + + return hr; + +} + +/*++ + +CSensorManager::_SensorEventThreadProc + +--*/ +DWORD WINAPI CSensorManager::_SensorEventThreadProc(_In_ LPVOID pvData) +{ + CSensorManager* pParent = (CSensorManager*) pvData; + + if (NULL == pParent) + { + return 0; + } + + // Cast the argument to the correct type. + CSensorManager* pThis = static_cast(pvData); + + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to call CoInitialize on _SensorEventThreadProc thread, hr = %!HRESULT!", hr); + return 0; + } + + // Create the event parameters collection if it doesn't exist + CComPtr spEventParams; + if (spEventParams == NULL) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &spEventParams); + } + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, "Failed to CoCreateInstance for Event Parameters, hr = %!HRESULT!", hr); + return 0; + } + + while (pParent->IsActive() && + (WAIT_OBJECT_0 == WaitForSingleObject(pParent->GetSensorEventHandle(), INFINITE))) + { + hr = S_OK; + + hr = pThis->EnterProcessing(PROCESSING_ISENSOREVENT); + + if (S_OK == hr) + { + // Initialize the event parameters + spEventParams->Clear(); + + // Populate the event type + hr = spEventParams->SetGuidValue(SENSOR_EVENT_PARAMETER_EVENT_ID, SENSOR_EVENT_DATA_UPDATED); + } + // Loop through every sensor and post an event for each one if needed + if (S_OK == hr) + { + for (DWORD i = 0; i < pThis->m_pSensorList.GetCount(); i++) + { + CSensor *pSensor; + CComBSTR objectId; + + pSensor = pThis->m_pSensorList.GetAt(pThis->m_pSensorList.FindIndex(i)); + + // Check if this Sensor has valid data to post + if (TRUE == pSensor->HasValidDataEvent()) + { + // Update sensor state to ready if needed + bool fStateChanged = FALSE; + hr = pParent->SetState(pSensor, SENSOR_STATE_READY, &fStateChanged); + + // Get the All the Data Field values + // Populate the event parameters + if (SUCCEEDED(hr)) { + hr = pSensor->GetAllDataFieldValues(spEventParams); + } + + if (SUCCEEDED(hr)) + { + objectId = pSensor->GetSensorObjectID(); + pParent->PostDataEvent(objectId, spEventParams); + pSensor->CheckLongReportIntervalTimer(); + } + } + } + } + + pThis->ExitProcessing(PROCESSING_ISENSOREVENT); + } + + CoUninitialize(); + + return 0; +} + +/*++ + +CSensorManager::SetState + +--*/ +HRESULT CSensorManager::SetState( + _In_ LPVOID pvData, + _In_ SensorState newState, + _Out_ bool* pfStateChanged) +{ + + HRESULT hr = S_OK; + + *pfStateChanged = FALSE; + + CSensor* pSensor = static_cast(pvData); + + if (nullptr != pSensor) + { + CComBSTR objectId = NULL; + objectId = pSensor->GetSensorObjectID(); + + if (NULL != objectId) + { + SensorState currentState; + DWORD dwValue = 0; + + PROPVARIANT var; + PropVariantInit(&var); + + hr = pSensor->GetProperty(SENSOR_PROPERTY_STATE, &var); + + if (SUCCEEDED(hr)) + { + PropVariantToUInt32(var, &dwValue); + currentState = (SensorState) dwValue; + + if (currentState != newState) + { + PropVariantClear(&var); + InitPropVariantFromUInt32(newState, &var); + + hr = pSensor->SetProperty(SENSOR_PROPERTY_STATE, &var, nullptr); + + if (SUCCEEDED(hr) && (nullptr != m_spClassExtension)) + { + hr = m_spClassExtension->PostStateChange(objectId, newState); + + if (SUCCEEDED(hr)) + { + switch (newState) + { + case SENSOR_STATE_READY: + Trace(TRACE_LEVEL_INFORMATION, "Setting %s state, State = READY", pSensor->m_SensorName); + break; + case SENSOR_STATE_NOT_AVAILABLE: + Trace(TRACE_LEVEL_INFORMATION, "Setting %s state, State = NOT_AVAILABLE", pSensor->m_SensorName); + break; + case SENSOR_STATE_NO_DATA: + Trace(TRACE_LEVEL_INFORMATION, "Setting %s state, State = NO_DATA", pSensor->m_SensorName); + break; + case SENSOR_STATE_INITIALIZING: + Trace(TRACE_LEVEL_INFORMATION, "Setting %s state, State = INITIALIZING", pSensor->m_SensorName); + break; + case SENSOR_STATE_ACCESS_DENIED: + Trace(TRACE_LEVEL_INFORMATION, "Setting %s state, State = ACCESS_DENIED", pSensor->m_SensorName); + break; + case SENSOR_STATE_ERROR: + Trace(TRACE_LEVEL_INFORMATION, "Setting %s state, State = ERROR", pSensor->m_SensorName); + break; + default: + Trace(TRACE_LEVEL_INFORMATION, "Setting %s state, State = UNKNOWN", pSensor->m_SensorName); + break; + } + + if (SENSOR_STATE_READY != newState) + { + DWORD cDatafields = 0; + + hr = pSensor->m_spSupportedSensorDataFields->GetCount(&cDatafields); + + if (SUCCEEDED(hr)) + { + PROPERTYKEY pkDfKey = { 0 }; + PROPVARIANT value; + + Trace(TRACE_LEVEL_INFORMATION, "Setting %s datafield values = VT_EMPTY", pSensor->m_SensorName); + for (DWORD dwIdx = 0; dwIdx < cDatafields; dwIdx++) + { + if (SUCCEEDED(hr)) + { + hr = pSensor->m_spSupportedSensorDataFields->GetAt(dwIdx, &pkDfKey); + } + + if (SUCCEEDED(hr)) + { + PropVariantInit(&value); + value.vt = VT_EMPTY; + + pSensor->m_spSensorDataFieldValues->SetValue(pkDfKey, &value); + + PropVariantClear(&value); + } + } + } + } + } + } + } + } + + PropVariantClear(&var); + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "Failed to get sensor ObjectID for %s, hr = %!HRESULT!", pSensor->m_SensorName, hr); + } + + } + else + { + hr = E_UNEXPECTED; + Trace(TRACE_LEVEL_ERROR, "pSensor == nullptr, hr = %!HRESULT!", hr); + } + + return hr; +} + +/*++ + +CSensorManager::PostDataEvent + +--*/ +HRESULT CSensorManager::PostDataEvent( + _In_ LPWSTR objectId, + IPortableDeviceValues *pValues) +{ + + HRESULT hr = S_OK; + + CComPtr spEventCollection; + + if (spEventCollection == NULL) + { + // CoCreate a collection to store the sensor object identifiers. + hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&spEventCollection)); + } + + if (SUCCEEDED(hr)) + { + hr = spEventCollection->Add(pValues); + if (SUCCEEDED(hr) && (m_spClassExtension != NULL)) + { + hr = m_spClassExtension->PostEvent(objectId, spEventCollection); + } + } + + + return hr; +} + +/*++ + +CSensorManager::PostStateChange + +--*/ +HRESULT CSensorManager::PostStateChange( + _In_ LPWSTR objectId, + _In_ SensorState newState) +{ + Trace(TRACE_LEVEL_VERBOSE, "%!FUNC! Entry"); + + HRESULT hr = E_UNEXPECTED; + + if (NULL != m_spClassExtension) + { + hr = m_spClassExtension->PostStateChange(objectId, newState); + } + + return hr; +} + +/*++ + +CSensorManager::OnCleanupFile + + This method is called when the file handle to the device is closed + +Parameters: + + pWdfFile - pointer to a file object + +--*/ +void CSensorManager::CleanupFile( + _In_ IWDFFile* pWdfFile) +{ + if (NULL != m_spClassExtension) + { + m_spClassExtension->CleanupFile(pWdfFile); + } + + return; +} + +/*++ + +CSensorManager::ProcessIoControl + + This method is called to process a Device IO Control + +Parameters: + pRequest - [in] pointer to the request + +--*/ +HRESULT CSensorManager::ProcessIoControl( + _In_ IWDFIoRequest* pRequest) +{ + Trace(TRACE_LEVEL_VERBOSE, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + if (NULL != m_spClassExtension) + { + hr = m_spClassExtension->ProcessIoControl(pRequest); + } + + return hr; +} + +/*++ + +CSensorManager::ProcessIoControlRadioManagement + + This method is called to process a Device IO Control for Radio Management. + +Parameters: + pRequest - [in] pointer to the request + ControlCode - [in] the control code to process + +--*/ +HRESULT CSensorManager::ProcessIoControlRadioManagement( + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode) +{ + Trace(TRACE_LEVEL_VERBOSE, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + if (nullptr != m_pSensorDDI) + { + switch (ControlCode) + { + case IOCTL_GPS_RADIO_MANAGEMENT_GET_RADIO_STATE: + hr = m_pSensorDDI->OnGetRadioState(pRequest, false); + break; + case IOCTL_GPS_RADIO_MANAGEMENT_GET_PREVIOUS_RADIO_STATE: + hr = m_pSensorDDI->OnGetRadioState(pRequest, true); + break; + case IOCTL_GPS_RADIO_MANAGEMENT_SET_RADIO_STATE: + hr = m_pSensorDDI->OnSetRadioState(pRequest, false); + break; + case IOCTL_GPS_RADIO_MANAGEMENT_SET_PREVIOUS_RADIO_STATE: + hr = m_pSensorDDI->OnSetRadioState(pRequest, true); + break; + } + } + + return hr; +} + +VOID CSensorManager::DeActivate() +{ + CComCritSecLock scopeLock(m_CriticalSection); + + m_fThreadActive = FALSE; + SetEvent(m_hSensorEvent); + return; +} + +VOID CSensorManager::Activate() +{ + CComCritSecLock scopeLock(m_CriticalSection); + + m_fThreadActive = TRUE; + + return; +} + +inline HRESULT CSensorManager::EnterProcessing(DWORD64 dwControlFlag) +{ + return m_pDevice->EnterProcessing(dwControlFlag); +} + +inline void CSensorManager::ExitProcessing(DWORD64 dwControlFlag) +{ + m_pDevice->ExitProcessing(dwControlFlag); +} \ No newline at end of file diff --git a/src/FakeGPS.Driver/SensorManager.h b/src/FakeGPS.Driver/SensorManager.h new file mode 100644 index 0000000..7618852 --- /dev/null +++ b/src/FakeGPS.Driver/SensorManager.h @@ -0,0 +1,106 @@ +// 2016 OK + +/*++ + +Module: + + SensorManager.h + +Description: + + Defines the CSensorManager container class + +--*/ + +#pragma once + +// deprecated interface +#pragma warning(disable:4995) + +class CSensorDDI; // forward declaration +class CSensor; // forward declaration +class CFakeGPS; // forward declaration + +class CSensorManager : + public CComObjectRoot +{ + +public: + + CSensorManager(); + + virtual ~CSensorManager(); + + DECLARE_NOT_AGGREGATABLE(CSensorManager) + + BEGIN_COM_MAP(CSensorManager) + + END_COM_MAP() + + HRESULT Initialize(_In_ IWDFDevice* pWdfDevice, _In_ CFakeGPS* pDevice); + void Uninitialize(); + + HRESULT Start(); + HRESULT Stop(); + + void CleanupFile(_In_ IWDFFile *pWdfFile); + HRESULT ProcessIoControl(_In_ IWDFIoRequest* pRequest); + HRESULT ProcessIoControlRadioManagement(_In_ IWDFIoRequest* pRequest, _In_ ULONG ControlCode); + HRESULT SetState(_In_ LPVOID pvData, _In_ SensorState state, _Out_ bool* pfStateChanged); + + HRESULT FindSensorTypeFromObjectID( + _In_ LPWSTR pwszObjectID, + _Out_ SensorType* pSensorType, + _Out_ DWORD* pSensorIdx); + + inline HRESULT EnterProcessing(DWORD64 dwControlFlag); + inline void ExitProcessing(DWORD64 dwControlFlag); + +public: + + CComObject* m_pSensorDDI; + ULONG m_NumMappedSensors; + WCHAR m_wszDeviceName[MAX_PATH]; + SENSOR_LIST m_pSensorList; // the object used by the driver to get sensor data + SENSOR_ID_MAP m_AvailableSensorsIDs; // map of available sensors and their object IDs + SENSOR_TYPE_MAP m_AvailableSensorsTypes; // map of available sensors and their sensor types + DWORD m_fInitializationComplete; + CFakeGPS* m_pDevice; + bool m_fDeviceActive; + +public: + + // Methods that call into Class Extension + HRESULT PostDataEvent(_In_ LPWSTR objectId, IPortableDeviceValues *pValues); + HRESULT PostStateChange(_In_ LPWSTR objectId, _In_ SensorState newState); + +private: + + // Private Methods + HRESULT InitializeClassExtension(); + BOOL IsInitialized(void) const { return m_fSensorManagerInitialized; } + + // Methods that call into Class Extension + //HRESULT PostDataEvent(_In_ LPWSTR objectId, IPortableDeviceValues *pValues); + + // Thread Process that fires data events + static DWORD WINAPI _SensorEventThreadProc(_In_ LPVOID pvData); + + // Helper functions to activate/deactivate thread + VOID Activate(); + VOID DeActivate(); + BOOL IsActive() { return m_fThreadActive; } + HANDLE GetSensorEventHandle(VOID) { return m_hSensorEvent; } + SENSOR_LIST* GetSensorListHandle(VOID) { return &m_pSensorList; } + + // Private Data Members + CComPtr m_spWdfDevice; + CComPtr m_spClassExtension; + + mutable CComAutoCriticalSection m_CriticalSection; + BOOL m_fSensorManagerInitialized; + HANDLE m_hSensorEvent; + HANDLE m_hSensorManagerEventingThread; // Thread handle for raising data events + BOOL m_fThreadActive; // Flag denoting that driver thread is active + BOOL m_fDeviceStopped; // Flag denoting that CSensorManager->Stop() was called +}; \ No newline at end of file diff --git a/src/FakeGPS.Driver/Trace.h b/src/FakeGPS.Driver/Trace.h new file mode 100644 index 0000000..d7be628 --- /dev/null +++ b/src/FakeGPS.Driver/Trace.h @@ -0,0 +1,72 @@ +// 2016 OK + +/*++ + +Module: + + Trace.h + +Description: + + This module contains the tracing information for the driver. + +--*/ + +#ifndef _TRACE_H_ +#define _TRACE_H_ + +// Hacks around IntelliSense - someone smarter may know how best to handle this. +#if defined(__INTELLISENSE__)// && defined(Trace) +#define TRACE_LEVEL_NONE 0 +#define TRACE_LEVEL_CRITICAL 1 +#define TRACE_LEVEL_FATAL 1 +#define TRACE_LEVEL_ERROR 2 +#define TRACE_LEVEL_WARNING 3 +#define TRACE_LEVEL_INFORMATION 4 +#define TRACE_LEVEL_VERBOSE 5 +#define TRACE_LEVEL_RESERVED6 6 +#define TRACE_LEVEL_RESERVED7 7 +#define TRACE_LEVEL_RESERVED8 8 +#define TRACE_LEVEL_RESERVED9 9 +#undef Trace +#define Trace(a,b,...) +#define TRACE_DRIVER +#endif +#pragma warning(disable:6387) + + +// Tracing Information +#define MYDRIVER_TRACING_ID L"FakeGPS" + +// Tracing Definitions: +// +// Control GUID: +// (a9de1ebf-dead-beef-b3c7-5edb2f1f7b15) +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + FakeGPSTraceGuid, \ + (a9de1ebf,dead,beef,b3c7,5edb2f1f7b15), \ + WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level \ + ) + +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// FUNC FuncEntry{FLAG=MYDRIVER_ALL_INFO, LEVEL=TRACE_LEVEL_VERBOSE}(...); +// FUNC FuncExit{FLAG=MYDRIVER_ALL_INFO, LEVEL=TRACE_LEVEL_VERBOSE}(...); +// USEPREFIX(Trace, "%!STDPREFIX! [%!FUNC!] "); +// USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry"); +// USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--"); +// end_wpp + +#endif // _TRACE_H_ diff --git a/src/FakeGPS.Driver/internal.h b/src/FakeGPS.Driver/internal.h new file mode 100644 index 0000000..22ceee7 --- /dev/null +++ b/src/FakeGPS.Driver/internal.h @@ -0,0 +1,207 @@ +// 2016 OK + +/*++ + +Module: + + Internal.h + +Description: + + This module contains the local type definitions for the sensors service driver. + +--*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +// Macro letting the compiler know this is not a kernel driver (this will help suppress needless warnings) +_Analysis_mode_(_Analysis_code_type_user_driver_); + +// Common WPD and WUDF headers +#include +#include +#include +#include + +#include "PortableDeviceTypes.h" +#include "PortableDeviceClassExtension.h" +#include "PortableDevice.h" + +// Headers for Sensor specific defines and WpdCommands +#include "Sensors.h" +#include + +// Headers to include float limits +#include "float.h" + +// Headers to include tracing +#include "Trace.h" + +// Device Interface GUID used for Radio Management communication +// TODO: Create a new GUID +// {3BA2B796-FACE-FEED-9F8E-62A38E444208} +DEFINE_GUID(GUID_DEVINTERFACE_GPS_RADIO_MANAGEMENT, + 0x3ba2b796, 0xface, 0xfeed, 0x9f, 0x8e, 0x62, 0xa3, 0x8e, 0x44, 0x42, 0x8); + +#define IOCTL_INDEX 0x800 +#define FILE_DEVICE_GPS_RADIO_MANAGEMENT 0x64911 // TODO: Pick a semi-unique value + +#define IOCTL_GPS_RADIO_MANAGEMENT_GET_RADIO_STATE CTL_CODE( \ + FILE_DEVICE_GPS_RADIO_MANAGEMENT, \ + IOCTL_INDEX, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + +#define IOCTL_GPS_RADIO_MANAGEMENT_GET_PREVIOUS_RADIO_STATE CTL_CODE( \ + FILE_DEVICE_GPS_RADIO_MANAGEMENT, \ + IOCTL_INDEX + 1, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + +#define IOCTL_GPS_RADIO_MANAGEMENT_SET_RADIO_STATE CTL_CODE( \ + FILE_DEVICE_GPS_RADIO_MANAGEMENT, \ + IOCTL_INDEX + 2, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define IOCTL_GPS_RADIO_MANAGEMENT_SET_PREVIOUS_RADIO_STATE CTL_CODE( \ + FILE_DEVICE_GPS_RADIO_MANAGEMENT, \ + IOCTL_INDEX + 3, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define PROP_STORE_KEY_RADIO_STATE L"SENSOR_PROPERTY_RADIO_STATE" +#define PROP_STORE_KEY_PREVIOUS_RADIO_STATE L"SENSOR_PROPERTY_PREVIOUS_RADIO_STATE" +#define PROP_STORE_KEY_LATITUDE L"SENSOR_PROPERTY_LATITUDE" +#define PROP_STORE_KEY_LONGITUDE L"SENSOR_PROPERTY_LONGITUDE" + +// IO map +const DWORD64 SHUTDOWN_IN_PROGRESS = 0x01; +const DWORD64 PROCESSING_IPNPCALLBACK = 0x02; +const DWORD64 PROCESSING_IPNPCALLBACKHARDWARE = 0x04; +const DWORD64 PROCESSING_IFILECALLBACKCLEANUP = 0x08; +const DWORD64 PROCESSING_IQUEUECALLBACKDEVICEIOCONTROL = 0x10; +const DWORD64 PROCESSING_IREQUESTCALLBACKREQUESTCOMPLETION = 0x20; +const DWORD64 PROCESSING_ISENSORDRIVER = 0x40; +const DWORD64 PROCESSING_ISENSOREVENT = 0x80; +const DWORD64 PROCESSING_IPNPCALLBACKSELFMANAGEDIO = 0x100; +const DWORD64 PROCESSING_IN_PROGRESS = 0xFFFFFFFE; + +/////////////////////////////////////////////////////////////////// +// Common macro expansions that are used throughout the project +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +/////////////////////////////////////////////////////////////////// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// + +enum SensorType +{ + Collection = -2, + SensorTypeNone = -1, + Geolocation = 0, + FirstSensorType = Geolocation, + LastSensorType = Geolocation, + SensorTypeUnknown, +}; + +enum SensorReportingState +{ + SENSOR_REPORTING_STATE_NO_EVENTS = 0, + SENSOR_REPORTING_STATE_ALL_EVENTS, +}; + +enum SensorPowerState +{ + SENSOR_POWER_STATE_POWER_OFF = 0, + SENSOR_POWER_STATE_LOW_POWER, + SENSOR_POWER_STATE_FULL_POWER, +}; + +class CSensor; //forward declaration + +typedef CAtlList< CSensor* > SENSOR_LIST; +typedef CAtlMap< ULONG, CComBSTR > SENSOR_ID_MAP; +typedef CAtlMap< ULONG, SensorType > SENSOR_TYPE_MAP; + +const ULONG MAX_NUM_DATA_FIELDS = 32; //for use with CLIENT_ENTRY change sensitivity +typedef struct _CLIENT_ENTRY +{ + FLOAT fltClientChangeSensitivity[MAX_NUM_DATA_FIELDS]; + ULONG ulClientReportInterval; + ULONG ulClientLocationDesiredAccuracy; + +} CLIENT_ENTRY, *PCLIENT_ENTRY; + +typedef CAtlMap< IWDFFile*, CLIENT_ENTRY > CLIENT_MAP; + +typedef struct _SUBSCRIBER_ENTRY +{ + BOOL fSubscribed; + +} SUBSCRIBER_ENTRY, *PSUBSCRIBER_ENTRY; + +typedef CAtlMap< IWDFFile*, SUBSCRIBER_ENTRY > SUBSCRIBER_MAP; + +typedef struct _PROPERTYKEY_DWVALUE_PAIR +{ + PROPERTYKEY propkeyProperty; + DWORD dwPropertyValue; + +} PROPERTYKEY_DWVALUE_PAIR, *PPROPERTYKEY_DWVALUE_PAIR; + +// NOTE: following is specific to the Geolocation sample +#include + +// Module defines +const unsigned char MODULE_NAME [] = "FakeGPS.dll"; // VER_ORIGINALFILENAME_STR + +// Default values +const unsigned short DEFAULT_DEVICE_MODEL_VALUE [] = L"FakeGPS Driver"; +const unsigned short DEFAULT_SERIAL_NUMBER [] = L"57E940BB-A3ED-BEEF-9722-A605392668E4"; +const unsigned short DEFAULT_MANUFACTURER [] = L"Julian Kay"; + +const FLOAT DEFAULT_MIN_CHANGE_SENSITIVITY = 1.0F; +const FLOAT DEFAULT_MAX_CHANGE_SENSITIVITY = 100.0F; +const ULONG DEFAULT_MIN_REPORT_INTERVAL = 16; //mS +const ULONG DEFAULT_MAX_REPORT_INTERVAL = 600000; //mS +const ULONG DEFAULT_SLEEP_REPORT_INTERVAL = 0; + +// Read/Write defines +const ULONG INITIAL_DATA_POLL_MAX_RETRIES = 5; +const ULONG FEATURE_REPORT_MAX_RETRIES = 10; + +const ULONG DEVICE_POLL_TIMEOUT = 15; //mS + +const FLOAT CHANGE_SENSITIVITY_NOT_SET = -1.0F; //invalid value + +// sensor defines +const ULONG DESCRIPTOR_MAX_LENGTH = (126 * 2); +const ULONG SENSOR_ID_APPENDIX_MAX_LENGTH = 9; + +// WUDF power policy settings +const WDF_POWER_POLICY_S0_IDLE_CAPABILITIES SENSOR_POWER_POLICY_S0_IDLE_CAPABILITIES = IdleCannotWakeFromS0; + +// Set delay timeout value. This specifies the time +// delay between WDF detecting the device is idle +// and WDF requesting a Dx power transition on the +// device's behalf. +const ULONG SENSOR_POWER_POLICY_IDLE_TIMEOUT = 100; + +// Opt-in to D3Cold to allow the platform to remove +// power when the device is idle and enters D3. +const WDF_TRI_STATE SENSOR_POWER_POLICY_EXCLUDE_D3_COLD = WdfFalse; \ No newline at end of file diff --git a/src/FakeGPS.sln b/src/FakeGPS.sln new file mode 100644 index 0000000..6b05770 --- /dev/null +++ b/src/FakeGPS.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FakeGPS.Driver", "FakeGPS.Driver\FakeGPS.Driver.vcxproj", "{7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|Any CPU.ActiveCfg = Debug|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|Any CPU.Build.0 = Debug|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|ARM.ActiveCfg = Debug|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|ARM64.ActiveCfg = Debug|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|x64.ActiveCfg = Debug|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|x64.Build.0 = Debug|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|x64.Deploy.0 = Debug|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|x86.ActiveCfg = Debug|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|x86.Build.0 = Debug|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Debug|x86.Deploy.0 = Debug|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|Any CPU.ActiveCfg = Release|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|Any CPU.Build.0 = Release|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|ARM.ActiveCfg = Release|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|ARM64.ActiveCfg = Release|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|x64.ActiveCfg = Release|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|x64.Build.0 = Release|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|x64.Deploy.0 = Release|x64 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|x86.ActiveCfg = Release|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|x86.Build.0 = Release|Win32 + {7412E979-D9E9-42F6-BAAF-90F7A4D67A5F}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal