支持 WPD 属性命令 (WpdBasicHardwareDriverSample)

示例驱动程序支持六个属性命令。 这些命令最初由 WpdObjectProperties::D ispatchMessage 方法处理,该方法反过来调用相应的命令处理程序。 DispatchMessage 方法和各个处理程序都在 WpdObjectProperties.cpp 文件中找到。

下表中的信息描述了每个受支持的属性命令,以及 WpdObjectProperties::D ispatchMessage 在处理给定命令时调用的处理程序的名称。

命令 Handler 说明
WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED OnGetSupportedProperties 返回给定对象的属性键数组。
WPD_COMMAND_OBJECT_PROPERTIES_GET OnGetPropertyValues 返回属性值的集合,该值对应于提供给驱动程序的属性键。
WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL OnGetAllProperties 返回给定对象的所有属性值。
WPD_COMMAND_OBJECT_PROPERTIES_SET OnSetPropertyValues 设置设备上的指定属性值。
WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES OnGetPropertyAttributes 返回给定对象上一个或多个属性的属性集合。
WPD_COMMAND_OBJECT_PROPERTIES_DELETE OnDeleteProperties 删除由给定属性键标识的属性。

WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED

驱动程序调用 WpdObjectProperties::OnGetSupportedProperties 处理程序以响应 WPD_COMMAND_OBJECT_PROPERTIES_GET_ SUPPORTED 命令。 处理程序反过来会调用 AddSupportedPropertyKeys 方法来检索所请求对象的受支持键。

由于示例设备不支持 WpdHelloWorldSample 驱动程序中的 Storage、Folder 或 File 对象,并且新驱动程序支持在原始驱动程序中找不到的对象,因此需要更新 AddSupportedPropertyKeys 方法。

以下代码取自修改后的 AddSupportedPropertyKeys 方法。 此方法调用两个支持方法 (AddDevicePropertyKeysAddSensorPropertyKeys) 来检索所请求对象的密钥:

HRESULT AddSupportedPropertyKeys(
    LPCWSTR                        wszObjectID,
    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 other PROPERTYKEYs for other supported objects...
    if (
           (strObjectID.CompareNoCase(SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(TEMP_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(FLEX_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(PIR_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(PING_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(QTI_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(MEMSIC_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(HITACHI_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(PIEZO_SENSOR_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(COMPASS_SENSOR_OBJECT_ID) == 0)
       )
    {
        // Add the PROPERTYKEYs for the Sensor object
        AddSensorPropertyKeys(pKeys);
    }

    return hr;
}

WPD_COMMAND_OBJECT_PROPERTIES_GET

驱动程序调用 WpdObjectProperties::OnGetPropertyValues 处理程序以响应 WPD_COMMAND_OBJECT_PROPERTIES_GET 命令。 处理程序又调用 GetPropertyValuesForObject 方法来检索所请求属性的当前值。 由于传感器设备不支持在 WpdHelloWorldSample 驱动程序中找到的 Storage、Folder 或 File 对象,并且由于新驱动程序支持在原始驱动程序中找不到的对象,因此必须更新此方法。

返回 Device 对象的属性的代码保持不变。 这是返回固件版本、电源级别、电源、设备协议、设备型号等的代码。

尽管此代码保持不变,但示例驱动程序返回的设备模型 (和其他类似属性) 不同于 WpdHelloWorldSample 驱动程序返回的属性值。 这是因为我们更新了 WpdObjectProperties.h 中的定义:

#define DEVICE_PROTOCOL_VALUE                L"Sensor Protocol ver 1.00"
#define DEVICE_FIRMWARE_VERSION_VALUE        L"1.0.0.0"
#define DEVICE_POWER_LEVEL_VALUE             100
#define DEVICE_MODEL_VALUE                   L"RS232 Sensor"
#define DEVICE_FRIENDLY_NAME_VALUE           L"Parallax BS2 Sensor"
#define DEVICE_MANUFACTURER_VALUE            L"Windows Portable Devices Group"
#define DEVICE_SERIAL_NUMBER_VALUE           L"01234567890123-45676890123456"
#define DEVICE_SUPPORTS_NONCONSUMABLE_VALUE  FALSE

下表列出了 WpdObjectProperties.h 中的定义、WpdHelloWorldSample 中的原始值,以及示例驱动程序中指定的新值。

定义 原始值 新值
DEVICE_PROTOCOL_VALUE Hello World 协议 v1.00 传感器协议 v1.00
DEVICE_FIRMWARE_VERSION 1.0.0.0 1.0.0.0
DEVICE_POWER_LEVEL 100 100
DEVICE_MODEL_VALUE Hello World! RS-232 传感器
DEVICE_FRIENDLY_NAME Hello World! 视差 BS2 传感器
DEVICE_MANUFACTURER_VALUE WPD 组 WPD 组
DEVICE_SERIAL_NUMBER_VALUE 012345678901234567890123456 012345678901234567890123456
DEVICE_SUPPORTS_NONCONSUMABLE_VALUE FALSE FALSE

对 GetPropertyValuesForObject 方法的最重要更改发生在检索传感器属性的 部分中。 此代码检索 在 WpdObjectProperties.h 中定义的多个值,例如对象名称、格式、内容类型以及是否可以删除它。

此外,此代码还会检索当前传感器读数和传感器更新间隔。 通过调用两个帮助程序函数( GetSensorReadingGetUpdateInterval)检索最后两个属性。

GetPropertyValuesForObject 方法的以下摘录包含检索传感器对象属性的代码:

// Retrieve the sensor properties

        else if (
                    (strObjectID.CompareNoCase(SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(TEMP_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(FLEX_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(PIR_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(PING_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(QTI_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(MEMSIC_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(HITACHI_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(PIEZO_SENSOR_OBJECT_ID) == 0) ||
                    (strObjectID.CompareNoCase(COMPASS_SENSOR_OBJECT_ID) == 0)
                 )
        {
            for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
            {
                PROPERTYKEY Key = WPD_PROPERTY_NULL;
                hr = pKeys->GetAt(dwIndex, &Key);
                CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex);

                if (hr == S_OK)
                {
                    // 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));
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_ID, strObjectID);
                        CHECK_HR(hr, "Failed to set WPD_OBJECT_ID");
                    }

                    else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID))
                    {

                        // Retrieve the ID of the sensor using the m_SensorType member of the
                        // basedriver that is set during the data-read operation.
                        hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, strObjectID);
                        CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID");
                    }

                    else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, WPD_DEVICE_OBJECT_ID);
                        CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID");
                    }

                    else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME))
                    {
                        // Retrieve the name of the sensor using the m_SensorType member of the
                        // basedriver that is set during the data-read operation.
                        if (m_pBaseDriver->m_SensorType == 0)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 2)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, TEMP_SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 3)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, FLEX_SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 4)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, PING_SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 5)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, PIR_SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 6)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, MEMSIC_SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 7)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, QTI_SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 8)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, PIEZO_SENSOR_OBJECT_NAME_VALUE);
                        else if (m_pBaseDriver->m_SensorType == 9)
                            hr = pValues->SetStringValue(WPD_OBJECT_NAME, HITACHI_SENSOR_OBJECT_NAME_VALUE);
                        CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME");
                    }

                    else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT))
                    {
                        hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED);
                        CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT");
                    }

                    else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE))
                    {
                        hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT);
CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE");
                    }

                    else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE))
                    {
                        hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE);
                        CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE");
                    }

                    else if (IsEqualPropertyKey(Key, SENSOR_READING))
                    {
                        hr = pValues->SetUnsignedLargeIntegerValue(SENSOR_READING, GetSensorReading());
                        CHECK_HR(hr, "Failed to set SENSOR_READING");
                    }

                    else if (IsEqualPropertyKey(Key, SENSOR_UPDATE_INTERVAL))
                    {
                        hr = pValues->SetUnsignedLargeIntegerValue(SENSOR_UPDATE_INTERVAL, GetUpdateInterval());
                        CHECK_HR(hr, "Failed to set SENSOR_UPDATE_INTERVAL");
                    }
                    else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY))
                    {
                        hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, FUNCTIONAL_CATEGORY_SENSOR_SAMPLE);
                        CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY");
                    }
                }
             } // end for
        } // end else if

GetSensorReading 帮助程序函数检索数字 (DWORD) 格式的最新传感器读数:

LONGLONG WpdObjectProperties::GetSensorReading()
{    
    // Ensure that this value isn't currently being accessed by another thread
    CComCritSecLock<CComAutoCriticalSection> Lock(m_SensorReadingCriticalSection);

    return m_llSensorReading;
}

注意 需要一个关键节来阻止 m_llSensorReading 成员变量的并发访问。 此值在每次 RS232 读取异步完成时被覆盖,每当 WPD 应用程序检索 SENSOR_READING 属性时,此值都会被读取。

GetUpdateInterval 帮助程序函数执行相同的操作:它访问 m_dwUpdateInterval 成员变量,并返回数值 (DWORD) 格式的值(如果可用):

DWORD WpdObjectProperties::GetUpdateInterval()
{    
    return m_dwUpdateInterval;
}

请注意,如果多个线程访问此值 ((例如,使用 WDF 并行调度队列而不是顺序调度队列),或者将 WpdBaseDriver::P rocessReadData 修改为在初始化) 期间多次设置更新间隔,则m_dwUpdateInterval成员变量必须受到关键部分的保护。 为简单起见,省略了关键部分。

WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL

驱动程序调用 WpdObjectProperties::OnGetAllPropertyValues 处理程序以响应 WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL 命令。 处理程序又检索给定对象的所有属性键,然后调用 GetPropertyValuesForObject 帮助程序函数来检索所请求属性的当前值。

WPD_COMMAND_OBJECT_PROPERTIES_SET

驱动程序调用 WpdObjectProperties::OnSetPropertyValues 处理程序以响应 WPD_COMMAND_OBJECT_PROPERTIES_SET 命令。 唯一可以在示例驱动程序中 (写入或设置) 的属性是SENSOR_UPDATE_INTERVAL。

处理程序首先检查给定属性的对象标识符,然后检查属性键本身。 如果对象标识符设置为 SENSOR_OBJECT_ID 并且属性键SENSOR_UPDATE_INTERVAL,则处理程序将调用 SendUpdateIntervalToDevice 帮助程序函数来更新值。

SendUpdateIntervalToDevice 帮助程序函数通过检查有效输入值、使用该值格式化写入请求,然后将写入请求发送到设备来执行写入操作。

HRESULT WpdObjectProperties::SendUpdateIntervalToDevice(DWORD dwNewInterval)
{
    HRESULT      hr                           = S_OK;
    RS232Target* pDeviceTarget                = NULL;

    CHAR  szInterval[INTERVAL_DATA_LENGTH+1]  = {0};

    // Check the input value
    if (IsValidUpdateInterval(dwNewInterval) == FALSE)
    {
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        CHECK_HR(hr, "Invalid update interval: %d", dwNewInterval);
    }

    // Format a write request with the input value
    if (hr == S_OK)
    {
        hr = StringCchPrintfA(szInterval, 
                              ARRAYSIZE(szInterval), "%d", dwNewInterval);
        CHECK_HR(hr, "Failed to convert the new interval to a CHAR string");
    }

    // Send the write request to the device
    if (hr == S_OK)
    {
        pDeviceTarget = m_pBaseDriver->GetRS232Target();

        if (pDeviceTarget->IsReady())
        {
            hr = pDeviceTarget->
                 SendWriteRequest((BYTE *)szInterval, sizeof(szInterval));
            CHECK_HR(hr, 
                     "Failed to send the write request to 
                      set the new temperature update interval");

            if (hr == S_OK)
            {
                TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_FLAG_DRIVER, 
                            "%!FUNC! Sent new interval: %s", szInterval);
            }
        }
        else
        {
            hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
            CHECK_HR(hr, "Device is not ready to receive write requests");
        }
    }

    if (hr == S_OK)
    {
        // Update the cached value on the driver
        SetUpdateInterval(dwNewInterval);
    }

    return hr;
}

WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES

驱动程序调用 WpdObjectProperties::OnGetPropertyAttributes 处理程序以响应 WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES 命令。 处理程序又调用 GetPropertyAttributesForObject 帮助程序函数来检索给定对象的属性。

在原始 WpdHelloWorldSample 驱动程序中,每个属性的属性都相同,并且所有属性都是只读的。 但是,在更新的驱动程序中,SENSOR_UPDATE_INTERVAL是可读/写的,SENSOR_UPDATE_INTERVAL和SENSOR_READING都具有WPD_PROPERTY_ATTRIBUTE_FORM_RANGE格式。 因此,需要对此帮助程序函数进行细微更改。


The WpdBasicHardwareDriverSample

WPD 驱动程序示例