Quantcast
Channel: Windows Portable Devices Team Blog
Viewing all 25 articles
Browse latest View live

WPD and WMDM Side-by-side: An Application Development Perspective

$
0
0

This post will compare WPD and the latest version of Windows Media Device Manager (WMDM), from an application developer's point of view.

Availability (aka SDK and Runtime Requirements)

The Windows SDK contains the complete build environment (headers, libs, sample code ...) for developing a WPD application for both 32 and 64-bit.

The Windows Media Format 11 SDK (WMFSDK11) provides headers and libs for WMDM 11.  Since WMFSDK11 is released in 32-bit, only 32-bit WMDM applications can be built.  On 64-bit Windows, a 32-bit WMDM application will run in SysWOW mode. 

 


WMDM 11 WPD
Operating Systems Windows XP + Windows Media Format 11 Redistributable, Windows Vista Windows XP + Windows Media Format 11 Redistributable, Windows Vista
Processors X86, AMD64 (SysWOW mode only) X86, AMD64
SDKs Windows Media Format SDK 11 Windows SDK

 

Common Device-level Operations

Both WPD and WMDM support basic device-level operations: enumeration of connected devices, managing device-level properties, and querying of static device capabilities.

WMDM's IWMDMDeviceControl operations internally delegate to the service provider (through IMDSPDeviceControl), and would require the service provider to support these operations. While device playback control is not natively supported by the WPD API, WPD's extensibility mechanism makes it easy to add this as a custom command to your driver. For example, your WPD driver can support a custom Playback command that your application can invoke using IPortableDevice::SendCommand.

 

Operation Example WMDM API Suggested WPD API
Enumerate attached devices IWMDeviceManager2::EnumDevices2 IPortableDeviceManager::GetDevices
Enumerating device content IWMDMDevice::EnumStorage IPortableDeviceContent::EnumObjects
Examine device capabilities IWMDMDevice::GetFormatSupport, IWMDMDevice2::GetFormatCapability IPortableDeviceCapabilities:: GetSupportedContentTypes, GetSupportFormats

Retrieve device properties

IWMDMDevice::GetManufacturer IPortableDeviceManager:: GetDeviceManufacturer OR
IPortableDeviceProperties::GetValues(WPD_DEVICE_OBJECT_ID)
Write device properties IWMDMDevice3::SetProperty IPortableDeviceProperties::SetValues(WPD_DEVICE_OBJECT_ID)
Controlling device playback IWMDMDeviceControl::Play -

 

Common Object-level Operations

A WPD object is analogous to a WMDM storage, and most WMDM storage operations have their equivalent WPD API.   WPD is ObjectID-based, so each interface method that supports object-level operations receives an Object Identifier as a parameter.   WMDM is interface-based, IWMDMStorage represents a storage or a file, and IWMDMStorageControl implements the operations on a storage or a file.   

For data object creation, both WPD and WMDM support transfer of protected Windows Media Digital Rights Management (WMDRM) content.

 

Operation Example WMDM API Suggested WPD API
Enumerating objects IWMDMDevice::EnumStorage, IWMDMEnumStorage::Next IPortableDeviceContent::EnumObjects, IEnumPortableDeviceObjectIDs::Next
Create a folder object IWMDMStorageControl::Insert3 IPortableDeviceContent::  CreateObjectWithPropertiesOnly
Create a data object IWMDMStorageControl::Insert3 IPortableDeviceContent:: CreateObjectWithPropertiesAndData
Transfer object data from device IWMDMStorageControl::Read IPortableDeviceResources::GetStream
Move objects IWMDMStorageControl::Move IPortableDeviceContent::Move
Copy objects - IPortableDeviceContent::Copy
Delete objects IWMDMStorageControl::Delete IPortableDeviceContent::Delete
Retrieve object properties IWMDMStorage4::GetSpecifiedMetadata IPortableDeviceProperties::GetValues
Write object properties IWMDMStorage3::SetMetadata IPortableDeviceProperties::SetValues
Delete object properties - IPortableDeviceProperties::Delete

 

Device Events

There are only 4 events that WMDM applications can receive: media arrival/removal, and device arrival/removal. WPD supports device-level and the more granular object-level events, plus built-in extensibility for custom events.

 

Operation Example WMDM API Suggested WPD API
Monitor object-level property changes - IPortableDeviceEventCallback::OnEvent (WPD_EVENT_OBJECT_UPDATED)
Monitor object-level arrival and removal - IPortableDeviceEventCallback::OnEvent (WPD_EVENT_OBJECT_ADDED, WPD_EVENT_OBJECT_REMOVED)
Monitor media arrival and removal IWMDMNotification::WMDMMessage (WMDM_MSG_MEDIA_ARRIVAL, WMDM_MSG_MEDIA_REMOVAL) IPortableDeviceEventCallback::OnEvent (WPD_EVENT_OBJECT_ADDED, WPD_EVENT_OBJECT_REMOVED)
Monitor device removal IWMDMNotification::WMDMMessage (WMDM_MSG_DEVICE_REMOVAL) IPortableDeviceEventCallback::OnEvent (WPD_EVENT_DEVICE_REMOVED)

Monitor device arrival

IWMDMNotification::WMDMMessage (WMDM_MSG_DEVICE_ARRIVAL) Subscribe to WPD interface arrival PnP event

 

Advanced Operations

Both WPD and WMDM provide some extensibility mechanism for sending device-specific commands.   IWMDMDevice3::DeviceIoControl is used for packaging and sending commands only to Media Transfer Protocol (MTP) portable media players.

WPD API methods that map to Command GUIDs internally delegate to IPortableDevice::SendCommand.  Applications can invoke these commands with additional options or parameters, such as the use of Read or Write IOCTLs for access control.

In addition, for better performance, WPD supports batching of property writes and retrievals across multiple objects, either per format or using a list of Object Identifiers.

 

Operation Example WMDM API Suggested WPD API
Retrieve properties of multiple objects - IPortableDevicePropertiesBulk::  QueueGetValuesByObjectList, OR QueueGetValuesByObjectFormat
Set properties of multiple objects - IPortableDevicePropertiesBulk:: QueueSetValuesByObjectList
Send device-specific commands IWMDMDevice::SendOpaqueCommand OR IWMDMDevice3::DeviceIoControl IPortableDevice::SendCommand
Cancel an operation - IPortableDevice*::Cancel, e.g. IPortableDeviceProperties::Cancel

 

Stability

WMDM loads registered service providers (SPs) within the same process as the application. Any crash or instability in the device driver or SP could cause undue crashes in the application process, or worse, trigger bluescreens if kernel mode drivers are involved.

WPD drivers are sandboxed in the User Mode Driver Framework (UMDF) host process, and gain the stability and security benefits of a UMDF driver. If a WPD driver crashes, only the sandbox process will be affected; the WPD application process is isolated from the driver instability. In a similar way, a WPD driver's process is not affected when a WPD client application crashes.

 

The Verdict

WPD and WMDM have a lot of overlap in terms of the device operations that each enables.   WMDM is geared specifically towards portable media player applications and scenarios, with media-centric metadata schema, support for device playback control, and direct access to Windows Media Digital Rights Management (WMDRM) application interfaces.   WPD supports much of what WMDM does (including WMDRM transfers), plus many key advantages over WMDM, including stability, UMDF integration, comprehensive property and content type schema (beyond portable media devices), extensibility for custom device solutions, broader device support, and not to mention, great development tools.

For applications that would like to connect to, control, and manage content across a wide range of devices (digital still cameras, portable media players, cellular phones, third-party device classes, and more), WPD is the clear choice.   

 

This posting is provided "AS IS" with no warranties and confers no rights.


Driver Dev Guide: WPP Tracing in your WPD Driver

$
0
0

WPP Software Tracing is the recommended way to log trace and error messages in your WPD driver.  WPP, short for Windows Software Trace PreProcessor, provides an efficient real-time event logging mechanism.    In addition, WPP traces include the system timestamp and can be used as a way to measure performance, for example by calculating the elapsed time between function calls. 

 

From OutputDebugString to WPP

The WPD sample drivers in the Windows Driver Kit (WDK) log error messages using a CHECK_HR function that wraps around OutputDebugString.   While OutputDebugString is simple to use during development, it necessitates an active debugger connection in order to view the traces, and should be used minimally in shipping code.   WPP tracing is much more lightweight, flexible, and preferable for a range of tracing applications: logging errors for diagnosing failures, tracing code execution during development, to name a few.    

How to switch your WPD driver's tracing from OutputDebugString to WPP transparently:

Step 1. Remove the original CHECK_HR() function definition (it can be found in WpdHelloWorldDriver.cpp or WpdWudfSampleDriver.cpp for the WPD samples) and its declaration in stdafx.h.  

 

Step 2. Add the following code (include the WPP config comment blocks) to stdafx.h:

// TODO: Choose a different trace ID for your WPD driver
#define MYDRIVER_TRACING_ID L"Microsoft\\WPD\\MyWpdDriver"

// TODO: Choose a different trace control GUID for your WPD driver
// TODO: Define trace flag bits for your WPD driver

#define WPP_CONTROL_GUIDS \
WPP_DEFINE_CONTROL_GUID( \
MyWpdDriverCtrlGuid, (da5fbdfd,1eae,4ecf,b426,a3818f325ddb), \
\
WPP_DEFINE_BIT(TRACE_FLAG_ALL) \
WPP_DEFINE_BIT(TRACE_FLAG_DRIVER) \
WPP_DEFINE_BIT(TRACE_FLAG_DEVICE) \
)

//
// Prefer trace statement that is based on
// - the standard trace LEVEL (evntrace.h), and
// - specific flag bit.
//
#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \
WPP_LEVEL_LOGGER(flags)

#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \
(WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)

//
// This comment block is scanned by the trace preprocessor to define our
// Trace and TraceEvents function.
//
// begin_wpp config
// FUNC Trace{FLAG=TRACE_FLAG_ALL}(LEVEL, MSG, ...);
// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...);
// end_wpp
//

// MACRO: CHECK_HR
// Configuration block that defines trace macro. It uses the PRE/POST macros to include
// code as part of the trace macro expansion. CHECK_HR is equivalent to the code below:
//
// {if (hrCheck != S_OK){ // This is the code in the PRE macro
// DoTraceMessage(TRACE_FLAG_ALL, "%!STDPREFIX!" MSG " hr= %!HRESULT!", ..., hrCheck)
// ;}} // This is the code in the POST macro
//

// begin_wpp config
// USEPREFIX (CHECK_HR,"%!STDPREFIX!");
// FUNC CHECK_HR{FLAG=TRACE_FLAG_ALL}(hrCheck, MSG, ...);
// USESUFFIX (CHECK_HR, " hr= %!HRESULT!", hrCheck);
// end_wpp

#define WPP_FLAG_hrCheck_PRE(FLAGS, hrCheck) {if(hrCheck != S_OK) {
#define WPP_FLAG_hrCheck_POST(FLAGS, hrCheck) ; } }
#define WPP_FLAG_hrCheck_ENABLED(FLAGS, hrCheck) WPP_FLAG_ENABLED(FLAGS)
#define WPP_FLAG_hrCheck_LOGGER(FLAGS, hrCheck) WPP_FLAG_LOGGER(FLAGS)

 

Step 3. Add a RUN_WPP directive to your sources file:

RUN_WPP= $(SOURCES) -scan:stdafx.h

 

Step 4. Add the WPP initialization and cleanup routines to DllMain:

// DLL Entry Point
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if(DLL_PROCESS_ATTACH == dwReason)
{
g_hInstance = hInstance;

//
// Initialize tracing.
//
WPP_INIT_TRACING(MYDRIVER_TRACING_ID);

}
else if (DLL_PROCESS_DETACH == dwReason)
{
//
// Cleanup tracing.
//
WPP_CLEANUP();

}

return _AtlModule.DllMain(dwReason, lpReserved);
}

Step 5. For each .cpp file, add a #include <filename>.tmh after the regular includes:

//
// WpdBaseDriver.cpp
//

#include "stdafx.h"
#include "WpdBaseDriver.h"

// Include the WPP generated Trace Message Header (tmh) file for this .cpp
#include "WpdBaseDriver.tmh"


...

Repeat Step 5 for all other .CPP files. 

 

Step 6. Rebuild your driver.  Since we replaced the CHECK_HR function with an equivalent WPP macro that has the same signature (our WPP changes were "under the hood"), no changes are needed for any code that uses CHECK_HR.   You can continue to use CHECK_HR the same way as before :).

The WPP preprocessor will process stdafx.h for tracing macros, and generate a Trace Message Header (tmh) file for each cpp file in the obj folders.  

Inspecting the preprocessor output for each .CPP file that calls CHECK_HR (generated using make WpdBaseDriver.pp), you will notice that all CHECK_HR trace statements have been wrapped by WPP function calls, while the tracing text and WPP functions are defined in WpdBaseDriver.tmh.

The most common WPP-related compilation errors you may encounter are due to mismatched format identifiers and parameters  (e.g. if your format string contains an extra %d that does not match an argument) or missing entries in the WPP_CONTROL_GUIDS macro.

 

More WPP Tracing Macros

We've also defined TraceEvents and Trace functions in stdafx.h that are general purpose WPP trace macros wrapping DoTraceMessage.   These can be used for both error and non-error trace logging using a combination of levels and flags.   For examples on how to use them in your driver, refer to the UMDF sample drivers in the WDK.

To understand the WPP declarations used in stdafx.h, customize the CHECK_HR macro, or to define your own tracing macros, visit the WPP macros guide which describes the WDK tracedrv sample in detail.

 

Generating and Viewing Traces

To generate traces, you can use Traceview  (UI) or Tracelog (command line).   TraceView also allows you to view traces in real-time as they are logged. These are available in the WDK.   A quick way to get started is to create a new log session from Traceview using the PDB file that contains the private symbols for your driver.   Alternatively, you use a .CTL file that specifies your driver as a trace provider (replace own values below):

da5fbdfd-1eae-4ecf-b426-a3818f325ddb    MyWpdDriverCtlGuid

 

Converting Binary Traces to A Human-Readable Form

WPP traces log to a binary ETL format.  To convert them to text, supply Trace Format (TMF and TMC) files that can be generated from the .PDB file using TracePDB.  For more details, refer to the tracing tools section in the MSDN docs.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Installing the Sample Drivers on XP and Vista

$
0
0

A question was raised today on how to run the WPD sample drivers on Windows XP.    WPD drivers and applications can run downlevel on Windows XP as long as the WPD and UMDF runtimes are both installed.

 

Compiling a WPD Driver for your OS

For the driver to run on Windows XP, it has to be built using a Windows XP x86 build environment provided by the Windows Driver Kit (v6000 or later).   Similarly, for running on Windows Vista, use one of the Windows Vista build environments.

If the runtime OS does not match the build environment, you may get a "Code 10 - This Device Cannot Start" in error message in Device Manager after installing the driver.

 

Installing WPD on the XP machine

Either one of these supply the WPD runtime components for Windows XP:

1. Install Windows Media Player 11, or

2. Install the Windows Media Format SDK 11 redistributable: WMFDist11-WindowsXP-X86-ENU.exe package, typically under c:\WMSDK\WMFSDK11\Redist folder from a Windows Media Format 11 SDK install.

 

Obtaining the WUDF Co-Installer

The UMDF runtime is distributed and installed by referencing its co-installer in the driver INF.   For Windows Vista and XP, the recommended co-installer to use is the version that is distributed in the Windows Driver Kits: under the \WinDDK\6001.xxxxx\redist\wdf\ folder.   Copy this to the same location as your updated driver INF before installing your driver.

 

Modifying the Sample Driver INF

The following example shows the INF changes to enable the WpdHelloWorldDriver to install in Windows XP using the v1.7 UMDF co-installer.   The actual version number would depend on what was provided in the WDK.  The modified/added entries are highlighted in red, and the same updates would apply to both WPD driver samples.

The changes involve adding a CopyFiles directive that references WUDFUpdate_01007.dll, and updating the AddReg entries to use UMDF v1.7.

[SourceDisksFiles]
WpdHelloWorldDriver.dll=1
WUDFUpdate_01007.dll=1
...

[Basic_Install.CoInstallers]
AddReg=Basic_Install.CoInstallers_AddReg
CopyFiles=CoInstallers_CopyFiles
...

; Add the co-installer DLL to the CopyFiles list
[CoInstallers_CopyFiles]
WUDFUpdate_01007.dll

...


; Update the registration to use the correct binary
[Basic_Install.CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WUDFUpdate_01007.dll"

...


; Update the UMDF version to 1.7.0
[WpdHelloWorldDriver_Install]
UmdfLibraryVersion=1.7.0

...


; Set the Co-Installer CopyFiles destination to system32
[DestinationDirs]
System32Copy=12,UMDF ; copy to system32\drivers\umdf
Basic_Install.CoInstallers_CopyFiles=11
...

 

The same updated INF will also work on Windows Vista, as long as the UMDF co-installer DLL is provided with your driver files during install AND you've compiled the driver DLL with the correct WDK build environment (see the first section of this post).  

On Windows Vista, installing the WPD runtime components is not required because WPD is available inbox for most SKUs.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Cancellation Behavior of MTP USB Devices

$
0
0

The MTP Specification contains this definition of the CancelTransaction Event:

G.2.2 CancelTransaction
Event Code: 0x4001
Parameter 1: None
Parameter 2: None
Parameter 3: None

This event is used to initiate the cancellation of a transaction over transports which do not have their own mechanism for canceling transactions. The details of how a transaction is cancelled may be transport-specific. When an initiator or responder receives this event, it should cancel the transaction identified by the TransactionID in the event dataset. If the transaction has already completed, this event should be ignored.

After receiving a CancelTransaction event from the initiator during an object transfer, the responder should send an Transaction_Cancelled response for the transfer which was in progress.


MTP USB Transport Cancellation Behavior

Why then does an MTP/USB device send a Status_OK (0x2001) instead of Transaction_Cancelled (0x201F) response when it receives a CancelTransaction (0x4001) from the application? 

The reason is that the MTP USB transport has defined how cancellation is to be implemented.   As pointed out in the MTP Specification above, CancelTransaction is intended for "transports which do not have their own mechanism for canceling transactions", and will not apply to MTP/USB devices.

Here are the MTP/USB specifications for each cancellation scenario.   In the cases below, "Host" can be a PC running Windows Media Player 11, and "Device" can be a MTP music player or a PTP camera that connects to the PC through USB.

Case A. When the Host Cancels
When the device receives a cancellation request from the host, it should return Device_Busy (0x2019) for a GetDeviceStatus query.    The device should then perform cancellation and clean up, and return Status_OK (0x2001) for subsequent GetDeviceStatus requests. The device should NOT send a response for the cancelled transaction through the BULK-IN pipe.

After the host receives a Status_OK response from a GetDeviceStatus query on the device, it may issue the next transaction through the BULK-OUT pipe. 

Case B. When the Device Cancels
If the device wants to cancel, it should STALL both BULK-IN and BULK-OUT endpoints.  When the device receives a GetDeviceStatus request from host, the device should return Transaction_Cancelled (0x201F) as the reason for the STALL, and the STALL-ed endpoint numbers.

The host may then issue the ClearFeature request to clear the STALL-ed endpoints. The device should clear, get ready for next command, and return Status_OK for next GetDeviceStatus query.

Case C. When the Host and Device Both Cancel
When the device receives a cancel request from the host while it is trying to cancel, it should clear any pending device-initiated cancellations, respond to the host with Device_Busy (0x2019) for subsequent GetDeviceStatus requests, and proceed with normal host-initiated cancellation.

When the device wants to cancel (this could be initiated by the user), it first checks if there are any pending host-initiated cancellation.  If yes, the device can simply return.  If not, the device should STALL the BULK endpoints.

 

Cancellation Examples 

For cancellation examples, refer to the Picture Transfer Protocol (PTP) Specification - ISO 15740 Annex D: Sections D.7.2.1.2 (for device behavior) and D.9, available on the I3A PTP website.

In addition, the PTP USB Transport specification also contains a definition for the GetDeviceStatus and further descriptions of the BULK cancellation behavior.
 

This posting is provided "AS IS" with no warranties, and confers no rights.

Driver Dev Guide: Supporting WPD_CLIENT_EVENT_COOKIE

$
0
0

WPD Applications can specify a unique string "cookie" in the client information when calling IPortableDevice::Open:

 

WPD_CLIENT_EVENT_COOKIE VT_LPWSTR Client supplied cookie returned by the driver in events posted as a direct result of operations issued by this client.
 

This is in addition to the client information properties described on MSDN, and declared in PortableDevice.h.

Depending on the application, this value will typically contain the application's DLL name or CLSID, or a unique application-generated identifier if the application supports multiple instances of itself running simultaneously.    The actual string contents does not matter to the driver, it is passed back to the application when events are sent.

When your driver sends an event and sets the cookie in the event parameters, the application can retrieve the cookie to determine if the event is a result of actions that originated from itself or another program.   For example, Application A creates an object on the device, and receives an ObjectAdded event containing its client event cookie, it can choose not to refresh its view of the device contents because the view has been updated at the time the object was created.    If Application B creates another object on the device, Application A will receive an ObjectAdded event with a different cookie (or no cookie), and knows that it needs to refresh the device contents view, because the object was added by some other client application.

In an environment where there can be two or more client applications communicating with your driver (Explorer is virtually guaranteed to be one client through the WPD Shell Namespace Extension), supporting the event cookie mechanism is a good WPD programming practice that may help reduce the client traffic to your device in response to events, and to streamline application-side event handling. 

 

How to support WPD_CLIENT_EVENT_COOKIE in your WPD driver:

1. Add a handler for the WPD_COMMAND_COMMON_SAVE_CLIENT_INFORMATION command.  The WpdWudfSampleDriver from the WDK contains an example of this in WpdBaseDriver::OnSaveClientInfo().

2. In OnSaveClientInfo(), if the application sets WPD_CLIENT_EVENT_COOKIE in the client information parameters, save the cookie with your context info. Some applications may choose not to send this cookie, in which case your driver does not have to do anything here.

LPWSTR pszEventCookie = NULL; 

hr = pClientInfo->GetStringValue(WPD_CLIENT_EVENT_COOKIE, &pszEventCookie);

if (hr == S_OK && pszEventCookie != NULL)
{
// Store the cookie value with the client context
pContext->EventCookie = pszEventCookie;
}

CoTaskMemFree(pszEventCookie);

 

3. When posting events, if the client event cookie is available, send it along with the event parameters.   In the sample driver, this code will be added to the PostWpdEvent() function.

HRESULT hrEventCookie = GetClientEventCookie(pCommandParams, &pszEventCookie);

if (hrEventCookie == S_OK && pszEventCookie != NULL)
{
// Add it to the event parameters
// The application's OnEvent callback will match this with its cookie
hrEventCookie = pEventParams->SetStringValue(WPD_CLIENT_EVENT_COOKIE, pszEventCookie);
}

CoTaskMemFree(pszEventCookie);

 

And here's an outline of the GetClientEventCookie() helper function.   It uses the client information context supplied in the command parameters to look up the saved client cookie in the context map.

ClientContext* pClientContext = NULL;   // This is a context helper object defined by the sample driver

hr = pCommandParams->GetStringValue(WPD_PROPERTY_COMMON_CLIENT_INFORMATION_CONTEXT, &pszClientContext);

if (hr == S_OK)
{
hr = GetClientContext(pCommandParams, pszClientContext, (IUnknown**)&pClientContext);

if (hr == S_OK && pClientContext != NULL && pClientContext->EventCookie.GetLength() > 0)
{
// Allocate the cookie string to return
*ppszEventCookie = AtlAllocTaskWideString(pClientContext->EventCookie);
}

if (pClientContext != NULL) 
    pClientContext->Release();

CoTaskMemFree(pszClientContext);
}

 

For an overview on posting events, refer to this post.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

New Driver and Application Whitepapers Are Here

$
0
0

We've published two new WPD developer-centric whitepapers at the Microsoft WHDC website for WinHEC.   

The first covers how to do WMDRM metering and license synchronization from a WPD application; the second describes how to port the existing WPD "Hello World" sample driver to communicate with a Parallax Basic Stamp II microcontroller to read temperature sensor data.   Both contain sample code.  

 

I. Accessing WMDRM APIs from a WPD Application:

http://www.microsoft.com/whdc/device/media/WMDRM_API.mspx

This whitepaper describes how to associate a WPD device instance with a WMDM device instance for MTP devices; the WMDM device instance is then used for calling Windows Media Digital Rights Management (WMDRM) application interfaces. To build the sample WPD application, you need the Windows Media Format SDK v11 and the Windows SDK (v6000).  

 

II. Creating a WPD Driver for a Microcontroller-Based Sensor:

http://www.microsoft.com/whdc/device/media/WPD_drv.mspx

This sample is based on the Parallax Basic Stamp II microcontroller to provide an example of a simple hardware device that can have a WPD driver for it.   Parallax provides an activity kit that contains the hardware components for this sample driver.  To build the driver, you need the Windows Driver Kit (v6000).  

 

References

All WPD-related whitepapers are posted here.  

More information on downloading the various SDKs.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Getting a WPD Sample Driver working with Windows Media Player 11

$
0
0

We mentioned in a previous post that the WPD sample drivers are not designed to enumerate in Windows Media Player 11 (WMP11) due to compatibility issues.   This post covers the changes needed to enable the WPD Comprehensive Sample Driver (WpdWudfSampleDriver) to appear as a portable device in WMP11 and simulate a sync.    

The WpdWudfSampleDriver is featured in this post because it contains a lot more functionality than the WpdHelloWorldDriver, and is closer to meeting the requirements of WMP11 than the WpdHelloWorldDriver.    More specifically, the WpdWudfSampleDriver simulates the following additional functionality:

1. Creating and transferring data objects on the device.

2. Rendering information profiles for creating content to the device.

3. Setting properties on objects.

4. Posting device events.

5. Bulk property operations.

In the list above, items 1-3 provide basic compatibility with WMP11.  Device events are a "good to have" feature, for application responsiveness to changes in the device content and device state.  Bulk property operations are optional, but also provide improved application responsiveness - multiple properties for objects can be sent in batches, instead of multiple per-object "GetProperty" requests.

During sync, the WpdWudfSampleDriver simulates file and folder creation by allowing content to be created and transferred to the device.   The data is not persisted by the driver, and will exist as long as the sample driver is still loaded.   Once the driver is disconnected (e.g. disable in Device Manager), the data goes away.   Typically, your WPD driver would be sending this data to your physical device and persisting it there, so this should not be an issue for real devices.

 

Modifications to the WpdWudfSampleDriver to Enumerate and Sync in WMP11

Some of the changes below involve updating the fake contents on the device for illustration.   The same property requirements will apply for your own driver or device objects.  

1.  Add WPD_DEVICE_SYNC_PARTNER as a writable property for the "Device" object.   This is needed by WMP11 for setting up a sync partnership with the device. 

In deviceobjectfakecontent.h:

class DeviceObjectFakeContent : public FakeContent
{
...

virtual HRESULT GetAllValues(
IPortableDeviceValues* pValues)
{
...
// Add WPD_DEVICE_SYNC_PARTNER
hrSetValue = pValues->SetStringValue(WPD_DEVICE_SYNC_PARTNER, SyncPartner);
if (hrSetValue != S_OK)
{
CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_SYNC_PARTNER");
return hrSetValue;
}
...
}

virtual HRESULT WriteValue(
const PROPERTYKEY& key,
const PROPVARIANT& Value)
{
...
else if(IsEqualPropertyKey(key, WPD_DEVICE_SYNC_PARTNER))
{
if (Value.vt == VT_LPWSTR)
{
SyncPartner = Value.pwszVal;
}
else
{
hr = E_INVALIDARG;
CHECK_HR(hr, "Failed to set WPD_DEVICE_SYNC_PARTNER because type was not VT_LPWSTR");
}
}
...
}
...

private:
...
CAtlStringW SyncPartner;
};

 

2. Add WPD_OBJECT_NON_CONSUMABLE as a writable property for all objects.   This allows simulating the saving of sync settings into a file on the device called WMPInfo.xml.   This file is created by WMP11 on the device and marked as "Non Consumable," with Format = WPD_OBJECT_FORMAT_UNSPECIFIED and Content Type = WPD_CONTENT_TYPE_UNSPECIFIED.  

In fakecontent.h:

class FakeContent
{

virtual HRESULT WriteValue(
const PROPERTYKEY& key,
const PROPVARIANT& Value)
{
...
else if (IsEqualPropertyKey(key, WPD_OBJECT_NON_CONSUMABLE))
{
if(Value.vt == VT_BOOL)
{
NonConsumable = Value.boolVal;
}
else
{
hr = E_INVALIDARG;
CHECK_HR(hr, "Failed to set WPD_OBJECT_NON_CONSUMABLE because type was not VT_BOOL");
}
}
...
}
...
};

 

3. Add support for WPD_OBJECT_ORIGINAL_FILE_NAME in for all objects.  This involves updating the support properties and fixed attributes tables, and overriding FakeGenericFileContent::GetAllValues() to return this property in addition to the standard properties.

In helpers.cpp:

const PROPERTYKEY* g_SupportedPropertiesForFakeContentFormat[] =
{
...
&WPD_OBJECT_ORIGINAL_FILE_NAME,
...
};

KeyAndAttributesEntry g_FixedAttributesTable [] =
{
...
{&FakeContent_Format, &WPD_OBJECT_ORIGINAL_FILE_NAME,
UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast},
// Properties for the device object
...
}

In fakecontent.h:

class FakeGenericFileContent : public FakeContent
{
public:

virtual HRESULT GetAllValues(
IPortableDeviceValues* pStore)
{
HRESULT hr = S_OK;
PropVariantWrapper pvValue;

// Call the base class to fill in the standard properties
hr = FakeContent::GetAllValues(pStore);
if (FAILED(hr))
{
CHECK_HR(hr, "Failed to get basic property set");
return hr;
}

// Add WPD_OBJECT_ORIGINAL_FILE_NAME
pvValue = FileName;
hr = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue);
if (hr != S_OK)
{
CHECK_HR(hr, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME"));
return hr;
}

return hr;
}
...
};

 

4. Add support for creation of folders.   These folders need to support WPD_OBJECT_ORIGINAL_FILE_NAME.    This allows WMP11 to create the "Album", "Artist" folders on the device during the sync operation.   

One way to do this is to define a new FakeFolderContent class that inherits from FakeContent, and return WPD_OBJECT_ORIGINAL_FILE_NAME in FakeFolderContent::GetAllValues().

Added new header file: fakefoldercontent.h

class FakeFolderContent : public FakeContent
{
public:
...

virtual HRESULT GetAllValues(
IPortableDeviceValues* pStore)
{
HRESULT hr = S_OK;
PropVariantWrapper pvValue;

// Call the base class to fill in the standard properties
hr = FakeContent::GetAllValues(pStore);
if (FAILED(hr))
{
CHECK_HR(hr, "Failed to get basic property set");
return hr;
}

// Add WPD_OBJECT_ORIGINAL_FILE_NAME
pvValue = FileName;
hr = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue);
if (hr != S_OK)
{
CHECK_HR(hr, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME"));
return hr;
}

return hr;
}
};

All top level folders (e.g. Video Folder, Music Folder) in the sample driver can then initialize a FakeFolderContent instead of a FakeContent.    The value of WPD_OBJECT_ORIGINAL_FILE_NAME is usually the same as WPD_OBJECT_NAME.  Assign the value of FileName in FakeDevice::CreateContentObject() whenever an object is created, so that it will be returned during subsequent "Get Property" queries.

In fakedevice.h:

class FakeDevice
{
...
HRESULT InitializeContent(
IPortableDeviceClassExtension *pPortableDeviceClassExtension)
{
...

// Add Media folder: Music folder
pContent = new FakeFolderContent();
if (pContent)
{
...
pContent->FileName = pContent->Name; // Set Original File Name property
...
}

// Repeat for other types of folders declared here
...

// Add generic file content objects to storage 1
for(DWORD dwIndex = 0; dwIndex < NUM_VERTICAL_OBJECTS; dwIndex++)
{
m_dwLastObjectID++;

pContent = new FakeGenericFileContent();
if (pContent)
{
...
pContent->FileName = pContent->Name;
...
}
}
...
}

HRESULT CreateContentObject(
LPCWSTR pszObjectName,
LPCWSTR pszParentID,
REFGUID guidContentType,
IPortableDeviceValues* pObjectProperties,
FakeContent** ppContent)
{
...
if(guidContentType == WPD_CONTENT_TYPE_FOLDER)
{
FakeContent* pContent = NULL;

pContent = new FakeContent();
if (pContent)
{
...
pContent->FileName = pszObjectName;
...
}
}
}
...
};

 

5.  Modify WpdWudfSampleDriver.INF to set EnableLegacySupport to 3 so that the WMDM compatibility component is registered and enabled for this device when it installs.   

; Enable support for legacy WIA and WMDM applications
HKR,,"EnableLegacySupport",0x10001,3


 

6. When returning the list of rendering information profiles in SetRenderingProfiles(), return an additional profile for WPD_OBJECT_FORMAT_WMV.  This profile contains a single WPD_PROPERTY_ATTRIBUTE_FORM_ENUMERATION entry with an IPortableDevicePropVariantCollection containing the list of valid FourCC Codec VT_UI4 values.   

This step is needed because the sample driver reports support for WMV.    In your real driver implementation, you don't need this step unless your device needs to support WPD_OBJECT_FORMAT_WMV.   WMP11 has additional requirements on the attributes of FourCC Codec property, and would not enumerate any content on the device otherwise [Symptom: you will see the device in the Sync Pane, but no content under the device].

In helpers.cpp:

HRESULT GetVideoProfile(
IPortableDeviceValues** ppProfile)
{
HRESULT hr = S_OK;
CComPtr<IPortableDeviceValues> pProfile;
CComPtr<IPortableDeviceValues> pFourCCCode;

hr = CoCreateInstance(CLSID_PortableDeviceValues,
NULL,
CLSCTX_INPROC_SERVER,
IID_IPortableDeviceValues,
(VOID**) &pProfile);
CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");

if (hr == S_OK)
{
hr = CoCreateInstance(CLSID_PortableDeviceValues,
NULL,
CLSCTX_INPROC_SERVER,
IID_IPortableDeviceValues,
(VOID**) &pFourCCCode);
CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");
}

// Set the value for WPD_OBJECT_FORMAT to indicate this profile applies to WMV objects
if (hr == S_OK)
{
hr = pProfile->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_WMV);
CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT");
}

// Set the value for WPD_VIDEO_FOURCC_CODE
if (hr == S_OK)
{
CComPtr<IPortableDevicePropVariantCollection> pFourCCCodeEnumElements;

if (hr == S_OK)
{
hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
NULL,
CLSCTX_INPROC_SERVER,
IID_IPortableDevicePropVariantCollection,
(VOID**) &pFourCCCodeEnumElements);
CHECK_HR(hr, "Failed to CoCreateInstance
CLSID_PortableDevicePropVariantCollection");
}

if (hr == S_OK)
{
hr = pFourCCCode->SetUnsignedIntegerValue(
WPD_PROPERTY_ATTRIBUTE_FORM,
WPD_PROPERTY_ATTRIBUTE_FORM_ENUMERATION);
CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM
for WPD_VIDEO_FOURCC_CODE");
}

if (hr == S_OK)
{
// Only 1 sample value is set here, add more as appropriate for your device
PROPVARIANT pvValue;
PropVariantInit(&pvValue);
pvValue.vt = VT_UI4;
pvValue.ulVal = MAKEFOURCC('W', 'M', 'V', '3');
hr = pFourCCCodeEnumElements->Add(&pvValue);
CHECK_HR(hr, "Failed to populate the FourCC Code Enumeration Elements");
}

if (hr == S_OK)
{
hr = pFourCCCode->SetIPortableDevicePropVariantCollectionValue(
WPD_PROPERTY_ATTRIBUTE_ENUMERATION_ELEMENTS,
pFourCCCodeEnumElements);
CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_ENUMERATION_ELEMENTS
for WPD_VIDEO_FOURCC_CODE");
}

// Now set the Video FourCC Code property to be pFourCCCode
if (hr == S_OK)
{
hr = pProfile->SetIPortableDeviceValuesValue(
WPD_VIDEO_FOURCC_CODE,
pFourCCCode);
CHECK_HR(hr, "Failed to add the WPD_VIDEO_FOURCC_CODE attributes
to the WPD_OBJECT_FORMAT_WMV rendering profile");
}
}

// Set the output result
if (hr == S_OK)
{
hr = pProfile->QueryInterface(IID_PPV_ARGS(ppProfile));
CHECK_HR(hr, "Failed to QI for IPortableDeviceValues");
}

return hr;
}

HRESULT SetRenderingProfiles(
IPortableDeviceValues* pValues)
{
...
CComPtr<IPortableDeviceValues> pVideoProfile;
...

// Get the video profile
if (hr == S_OK)
{
hr = GetVideoProfile(&pVideoProfile);
CHECK_HR(hr, "Failed to get video profile properties");
}

// Add the profile to the collection
if (hr == S_OK)
{
hr = pProfiles->Add(pVideoProfile);
CHECK_HR(hr, "Failed to add video profile to profile collection");
}
...
}

Known Issue with Multiple Storages

When a device that contains multiple storages is disconnected, one of the storages will still remain in the treeview.   This is a known bug in WMP11.   Since the WpdWudfSampleDriver emulates multiple storages, you'll see this bug when you disable the driver from Device Manager while WMP11 is running.

 

Update [August 7, 2009]:  The WDK 7.0.0 now contains an updated WpdWudfSampleDriver sample that supports enumeration in Windows Media Player. You can get the latest WDK here. See this WDK 7.0.0 RTM release note for the complete list of build environments and target operating systems applicable to the WPD driver samples.

 

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Which version of the WDK do I download?

$
0
0

To develop WPD drivers for Vista SP1:

- Build them using the Vista WDK (version 6000) or from the Longhorn Server Beta 3 WDK build environments.   

- WPD drivers built using Longhorn Server WDK Beta 3 will not run on Vista RTM or Windows XP, this is because the version 1.7 WDF Co-installer shipped in the Beta 3 release does not support downlevel operating systems (Vista RTM, XP, Server 2003).  

 

To develop WPD drivers for Vista RTM:

- Build them from the Vista WDK (version 6000) 

- The drivers can run on Windows XP with INF changes and Windows Media Player 11 or Format SDK Redistributable 11 installed

 

This posting is provided "AS IS" with no warranties, and confers no rights.


Driver Dev Guide: Client Context Management

$
0
0
Client Context Management in WPD Drivers

A WPD driver provides the communication channel between applications and the physical device.   There can be multiple WPD applications running at any time, and the driver needs to handle requests from different clients and identify the clients based on the queued requests.   In other words, the driver needs an efficient and easy way to store client data on a per-connection basis, and retrieve the data per request.    Fortunately, UMDF supports context areas, a generic mechanism to save data with a framework object.   A WPD driver can allocate a data structure or object to hold the data, assign it to the framework object's context area, and retrieve the context at a later time.  The appropriate per-connection WDF framework object to use is the WDF file object.

 

Step 1: Assigning the Context

The driver assigns the context when the client opens a connection to it.   When a WPD application calls IPortableDevice::Open, the WPD API creates a handle to the driver using Win32 CreateFile.   Under the hood, UMDF initializes an IWDFFile object and forwards it, along with the Creation request, to the driver's IQueueCallbackCreate::OnCreateFile method.   The IWDFFile in this case represents a Win32 HANDLE that is used for subsequent communication from this client to the driver.  

A example of a CreateFile callback implementation is WpdWudfSampleDriver's CQueue::OnCreateFile.  A driver-specific ContextMap COM object is used to store client data (application name, version, in-progress enumeration and resource contexts, etc).   Note that the use of COM objects as context data is NOT required by UMDF - UMDF sees the context data as an opaque PVOID.   If you are using a COM object for storing context data, your driver needs to maintain the reference count for that COM object, and ensure that its resources are freed in the appropriate cleanup methods.

To save context data, the driver initializes a new ContextMap object, and calls IWDFObject::AssignContext for the IWDFFile object handed in by UMDF.    The parameters for AssignContext are the pointers to an IObjectCleanup object [containing the context cleanup code], and the newly-created ContextMap [containing the data to store].   IObjectCleanup::OnCleanup will be called when the file object is destroyed during CloseHandle.   See "Step 3" for further details on how to implement OnCleanup.  

In addition, only one context can be assigned to the file object (or any UMDF framework object).  Subsequent calls to AssignContext will fail if a context has already been assigned.   To add/remove client-specific data dynamically, one way is to implement a mapping object for managing the data (e.g. WpdWudfSampleDriver's ContextMap object), and assign a pointer to that mapping object as file object's context.

 

Step 2: Retrieving and Saving Context Information

To access the client data during requests, the WPD driver gets the context from the IWDFFile object.  

The sequence is:

  1. Call IWDFIoRequest::GetFileObject to get the IWDFFile object.
  2. Call IWDFObject::RetrieveContext on the IWDFFile object to access the context area.   In the sample driver, this will be the pointer to the ContextMap object that was created in CQueue::OnCreateFile during IPortableDevice::Open.
  3. Add/remove data to the ContextMap object directly when processing the WPD commands.  Each client connection (i.e. IPortableDevice::Open) will have its own IWDFFile object and ContextMap object. 
Example code from WpdWudfSampleDriver: 
  • CQueue::OnDeviceIoControl - Retrieving the context map from the WDF request's file object
  • WpdBaseDriver::OnSaveClientInfo - Adding client information to the context map when processing the WPD_COMMAND_COMMON_SAVE_CLIENT_INFORMATION command 

 

Step 3: Cleaning up the Context

When the client application calls IPortableDevice::Close, the WPD API will in turn call CloseHandle on the Win32 handle associated with that open connection.   Before destroying the IWDFFile object in response to the CloseHandle, UMDF calls the file object's IObjectCleanup::OnCleanup method that the driver passed into AssignContext during OnCreateFile.

An example implementation of the IQueueCleanup callback is WpdWudfSampleDriver's CQueue::OnCleanup.   This method retrieves the ContextMap stored in the IWDFObject object (in this case, the instance of IWDFFile from OnCreateFile) and frees the allocated memory, including the objects that the ContextMap holds.   To avoid memory leaks, ensure that the objects are properly cleaned up, and (if applicable) decrement the reference count.

 

References

A great WDF book is Developing Drivers with WDF by Orwick/Smith. Chapter 5 (pages 124-125) covers techniques for object-specific context data storage using UMDF.    The WpdWudfSampleDriver sample code is available in the Windows Driver Kit.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Creating a Temperature-Sensor Gadget for Windows Sidebar with C#

$
0
0

This article was written for application developers who are interested in displaying portable device data in the Windows Sidebar. It describes a Microsoft .Net application written in C# as well as a gadget for Windows Sidebar. The application was written using Microsoft Visual Studio 8. The WPD gadget was written in HTML and JScript.

The .Net application retrieves temperature data from a Parallax temperature-sensor device and writes this data to a file on the local disk. A script in the gadget's HTML file reads the contents of this local file and displays the temperature in the Sidebar. 

The temperature sensor used by this application is based on the BASIC Stamp Activity Kit for Windows Portable Devices. This kit is offered by the Parallax Corporation in Rocklin, California. You can order the kit from the company’s website.

Running the Application and Viewing the Gadget

The application and gadget described in this article require that you install the WPD temperature sensor driver (WpdTempSensorDriver.dll) and that you connect the temperature sensor device over a standard RS232 port.

The WPD temperature-sensor driver exposes a Temperature Sensor object. This object, in turn, exposes three programmatic elements: two properties and an event. These elements are described in the following table.

Programmatic Element

Description

Interval property

This read/write property specifies the frequency at which the device should return the current temperature. (This property is specified in milliseconds.)

Temperature property

This read-only property specifies the current temperature in degrees Kelvin.

Temperature-reading event

This event is fired each time the device retrieves the current temperature. (The Interval property specifies the frequency at which the device will fire this event.)

Building and Installing the Temperature Sensor Device

The temperature sensor device, referenced in this article, is based on a Parallax BS2 microcontroller and an AD592 Temperature Sensor transducer.

Parallax supplies a complete hardware kit that includes all of the required components. To order the kit, see this page on the Parallax site.

You can download the Basic Stamp source code for the temperature sensor device from the WHDC site.

Building and Installing the WPD Temperature Sensor Driver

In order to install the driver, you’ll first need to download the source code and build it. For information about downloading the driver, see the following topic on the WHDC site.

Installing the Gadget

The sidebar gadget is found here at the Microsoft Download Center.

Download this file to your local machine, open Windows Explorer, and double-click on the icon. Vista will ask you whether or not you want to install the gadget. Choose the Install button.

The gadget will be installed under the Desktop\<user name>\AppData\Local\Microsoft\Windows Sidebar\Gadgets folder.

Installing the Windows SDK

Because there’s no native .Net support for the WPD API, you’ll need to use the WPD COM interfaces if you are writing a .Net application. Your application will access these interfaces through the COM Interoperability (interop) feature of .Net.

When working with the type libraries for COM objects, it’s sometimes necessary to make minor revisions to the type library metadata. Microsoft provides tools you can use to make these changes in the Windows SDK; so, you’ll want to download this SDK onto your development machine.

You can download the Windows SDK here.

A Client/Server Model

The .Net application described in this article is a server while the gadget acts as its client. The server monitors the temperature sensor device for event notifications. Each time it receives a notification, this application writes corresponding temperature data to a file on disk. The data is formatted as HTML. The client (or gadget) retrieves the data from the file created by the application and renders it in the sidebar.

The .Net application sets a timer event to fire every three seconds. Each time the event fires, the application retrieves the current temperature from the WPD temperature-sensor device. The application then writes the temperature as a string of HTML to an ASCII text file on the local disk. This string has the following appearance:

<P>Office Temperature<P>70&deg; Farenheit

 

The gadget’s script executes every 3,000 milliseconds; this script reads the string of HTML created by the application and inserts the string into the body of the gadget’s HTML

The Temperature-Sensor Application

The temperature-sensor application described in this article is a simple application that accomplishes the following tasks:

·         It searches for the temperature-sensor device and opens a connection if it’s found.

·         It sets a timer event which is fired ever 3 seconds.

·         Each time the event fires, it retrieves the current temperature from the connected device and writes it to local disk.

A Windows temperature-sensor application project, written in C# and .Net, might consist of the following files:

File

Description

Form1.cs

Searches for the temperature-sensor device and opens a connection if it’s found. Sets the timer event and retrieves the current temperature from the device, and, writes the temperature value to a file on local disk.

Form1.Designer.cs

Defines the InitializeComponent method (a method describing the application’s form and its contents).

Form1.resx

Defines the resources found on the form (a single timer resource).

Interop.PortableDeviceApiLib.dll

Revised interop assembly that correctly handles the call to the IPortableDeviceManager::GetDevices method. (See http://blogs.msdn.com/dimeby8 and the Enumerating WPD devices in C# topic.)

Portabledeviceconstants.cs

A file containing definitions for the property-keys used by the WPD API as well as the property key for the TEMP_SENSOR_READING property.

Program.cs

Defines the application’s main entry point.

WpdSidebarGadget.csproj

The Visual Studio 8 project file.

Accessing the WPD API through .Net

Because there’s no native .Net support for the WPD API, you’ll need to use the WPD COM interfaces through the COM Interoperability (interop) feature of .Net. This feature lets your .Net application “think” that it’s accessing a .Net version of WPD, when, in fact, it’s accessing the unmanaged objects instead.

COM objects (like the WPD API) are described by type libraries. These libraries give the Visual Studio 8 compiler a definition of the types, interfaces, and methods that WPD supports.

When working with the type libraries for COM objects, it’s sometimes necessary to make minor revisions to the metadata associated with these libraries. Microsoft provides the tools necessary to make these changes in the Windows SDK, so, you’ll want to download this SDK onto your development machine.

Your application will use the COM interfaces found in the WPD API to accomplish tasks like: enumerating connected devices, opening a device, closing a device, enumerating objects on a device, reading and writing properties on a device, sending a command to a device, and registering to receive events from a device.

In order to access these interfaces and their methods from your C# .Net application, you’ll need to add references to the PortableDeviceApiLib and the PortableDeviceTypes Type Libraries in your Visual Studio 8 .Net project.  You’ll add these references through the Project/Add Reference menu and the COM tab found on the associated dialog.

Once you add the references to these two type libraries, you’ll need to resolve several marshalling restrictions in the Interop assembly that Visual Studio generates for the PortableDeviceApiLib. (You’ll use a couple of tools found in the Windows SDK to resolve these restrictions.)

Resolving the Marshalling Restrictions

Create a Visual Studio 8 project for a C# Windows application and add the references to the two type libraries listed above (PortableDeviceApiLib and PortableDeviceTypes). Next, build your application.

When you built your application, Visual Studio invoked the type library importer and created what’s called an Interop Assembly for each of the type libraries that you’d added. You’ll find these assemblies in the \bin\debug folder of your project (if you built a debug version of your application); otherwise, you’ll find these assemblies in the \bin\release folder.

An Interop Assembly lists the COM types that your application can access via COM interoperability. These assemblies contain metadata that the .Net compiler uses to resolve method calls.

Unfortunately, some of the metadata generated by the type library importer is incorrect for C# applications. For example, if you examine the metadata for the IPortableDeviceManager::GetDevices method, you’ll see that the first argument (an array of string pointers) is specified to be:

[in][out] string&  marshal( lpwstr) pPnPDeviceIDs 

You’ll need to replace unary & operator with the square brackets which are used to specify an array type in C#. And, you’ll need to replace the lpwstr type after the marshal keyword with the [] operator. So, the new description of this argument would appear as below:

[in][out] string[]  marshal([]) pPnPDeviceIDs 

In general, for WPD methods that take an array or buffer as an argument, you’ll want to replace any occurrence of the unary & operator found in the type-libraries metadata with the square brackets. (And, you may also need to add the marshal keyword and arguments.)

To edit the metadata in the interop assembly, you’ll need to use the IL Disassembler (ILDASM.EXE) and the IL Assembler (ILASM.EXE). The disassembler creates an editable text file from an interop assembly file. After you’ve edited this text file (and revised the metadata), you’ll rebuild the interop assembly using the assembler.

The following steps describe how you would create an editable text file from the PortableDeviceApi interop assembly, how you would revise the metadata for the GetDevices and GetDeviceFriendlyName methods, and how you would create a new, revised assembly.

1.     Open an instance of the Windows SDK command shell.

2.     Use the “cd” command to change directory to your project’s \debug folder that contains the file Interop.PortableDeviceApiLib.dll

3.     Use the following command to disassemble the PortableDeviceAPI interop assembly into an editable text file:
ildasm Interop.PortableDeviceApiLib.dll /out:pdapi.il

4.     Open the text file pdapil.il in notepad and replace all occurrences of the pPnPDeviceIDs argument for the GetDevices method with the correct metadata. Replase the following string:
      instance void  GetDevices([in][out] string&  marshal( lpwstr) pPnPDeviceIDs,
with the revised string:
      instance void  GetDevices([in][out] string[]  marshal([]) pPnPDeviceIDs,

5.     For the sample described in this article, you’ll also want to  make the following changes to the description of the pDeviceFriendlyName argument for the GetDeviceFriendlyName method. Replace the following string:
   
[in][out] uint16& pDeviceFriendlyName
with the revised string:
   
[in][out] uint16[] marshal( []) pDeviceFriendlyName

6.     Assemble the revised metadata text file into a new interop assembly using the following command:
ilasm pdapi.il /dll /output=Interop.PortableDeviceApiLib.dll

7.     Move the newly created assembly to the root folder of your project. (If you leave in the \debug folder, Visual Studio will overwrite it the next time you build.)

8.     Remove the reference in our Visual Studio project to the original interop assembly and create a new reference to the newly modified assembly that resides in the root folder of your project.

If your application invokes other WPD methods that use arrays or buffers as arguments, you would repeat the revisions described in steps 4. and 5. above before you created the new interop assembly.

Accessing the WPD Properties and Commands

WPD properties and commands are identified by a PROPERTYKEY structure. This structure has two parts: a GUID and a DWORD. (For more information, see the discussion of WPD PROPERTYKEYS on MSDN).

In order to use the common WPD PROPERTYKEYs, you’ll need to add a file containing their definition to your C# project. For a complete description of how this is done, refer to the following link on the dimeby8 blog.

In the sample code found in this article, we refer to a file portabledeviceconstants.cs which we created using the guidelines in the dimeby8 blog (above).

 

Creating the WPD Objects

One of the first tasks a .Net will accomplish is the creation of the WPD objects that are used throughout the application to invoke methods, retrieve property values, and so on. The following snippet demonstrates how an application might create instances of the device-manager object, a device-values collection, and a portable device object.

namespace WpdSidebarGadget

{

    public partial class Form1 : Form

    {

 

        //

        // Get an instance of the device manager

        //

        PortableDeviceApiLib.PortableDeviceManagerClass devMgr

            = new PortableDeviceApiLib.PortableDeviceManagerClass();

 

        // Create our client information collection

        PortableDeviceApiLib.IPortableDeviceValues pValues =

            (PortableDeviceApiLib.IPortableDeviceValues)

                new PortableDeviceTypesLib.PortableDeviceValuesClass();

 

        // Create a new IPortableDevice instance

        PortableDeviceApiLib.PortableDeviceClass pPortableDevice =

                new PortableDeviceApiLib.PortableDeviceClass();


The following table lists the variable corresponding to each object and describes its use later in the application.

Variable

Description

devMgr

Used to retrieve a count of connected devices, to retrieve Plug and Play identifiers for those devices, and to retrieve friendly names for those devices,

pValues

Used to specify client information when opening a connection to the device.

pPortableDevice

Used to open a connection to the temperature-sensor device. Also used to retrieve the value of the TEMP_SENSOR_READING property.

 

Opening a Connection to the Temperature-Sensor Device

The process of opening a connection to a device is done in two phases: in the first phase we check to see if the device is connected to the PC; if it’s found, we open the connection in the second phase.

Searching for a Specific Device

The IPortableDeviceManager::GetDevices method serves two purposes. If the first parameter is set to null, it returns a count of connected devices. Otherwise, it returns a list of Plug and Play identifiers for each connected device.

The Plug and Play names are used internally by WPD to identify devices.  The Plug and Play name for the temperature sensor device is: “\\?\root#wpd#0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}”. This string uniquely identifies the device instance and the device interface and is defined as part of the Windows Driver Model (WDM). For more information, refer to this topic on MSDN.

The IPortableDeviceManager::GetFriendlyName method maps a given Plug and Play identifier back to the given device’s friendly name. This is the name commonly associated with a device by the user. For example: “Parallax BS2 Temperature Sensor”. For more details about the use of the GetFriendlyName method, see the following section titled: “Retrieving the Friendly Name for a Given Device”.

The following code demonstrates how an application used the GetDevices method to retrieve a count of connected devices and then, subsequently, retrieve the Plug and Play identifier for each device. This code then retrieves the friendly name for each connected device (by calling the RetrieveFriendlyName helper function), and checks to see if any of the connected devices have a friendly name that matches the string: “Parallax BS2 Temperature Sensor”. If such a device exists, its Plug and Play identifier is passed to the StartProcessing helper function which opens a connection to the device and begins retrieving temperature data.

private void Form1_Load(object sender, EventArgs e)

{

    uint i = 0;

    uint cDevices = 0;

    string strDeviceID = String.Empty;

    string strFriendlyName = String.Empty;

 

    // Retrieve a count of connected WPD devices.

    devMgr.GetDevices(null, ref cDevices);

 

    // Iterate through the connected WPD devices--searching for

    // a TemperatureSensor device. If it's found, open the device

    // and begin retrieving the temperature property.

    if (cDevices > 0)

    {

        // Retrieve the PnP identifiers for each

        // connected device.

        string[] strPnPDeviceIDs = new string[cDevices];

        devMgr.GetDevices(strPnPDeviceIDs, ref cDevices);

         // For each connected device, retrieve the friendly

         // name and compare it to the TempSensor's name.

         for (i = 0; i < cDevices; i++)

         {

             strFriendlyName = RetrieveFriendlyName(devMgr, strPnPDeviceIDs[i]);

             if (strFriendlyName == "Parallax BS2 Temperature Sensor")

             {

                 // Open the device and begin retrieving the temperature

                 StartProcessing(pPortableDevice, pValues, strPnPDeviceIDs[i]);

              }

          }

      }

  }

Retrieving the Friendly Name for a Given Device

As noted in the previous section, the IPortableDeviceManager::GetFriendlyName method maps a given Plug and Play identifier back to the given device’s friendly name. In addition to retrieving a device’s friendly name, this method also returns a count of characters in the friendly-name string--if the second parameter is set to null.

The following helper function, RetrieveFriendlyName, demonstrates how your application could use the GetFriendlyName method to retrieve the count of characters in the friendly-name string, allocate memory for the string, and then retrieve it.

string RetrieveFriendlyName(

                        PortableDeviceApiLib.PortableDeviceManagerClass PortableDeviceManager,

                        string PnPDeviceID)

{

    uint   cFriendlyName = 0;

    ushort[] usFriendlyName;

    string strFriendlyName = String.Empty;

 

    // First, pass NULL as the LPWSTR return string parameter to get the total number

    // of characters to allocate for the string value.

    PortableDeviceManager.GetDeviceFriendlyName(PnPDeviceID, null, ref cFriendlyName);

           

    // Second allocate the number of characters needed and retrieve the string value.

 

    usFriendlyName = new ushort[cFriendlyName];

    if (usFriendlyName.Length > 0)

    {

        PortableDeviceManager.GetDeviceFriendlyName(PnPDeviceID, usFriendlyName, ref cFriendlyName);

 

        // We need to convert the array of ushorts to a string, one

        // character at a time.

        foreach (ushort letter in usFriendlyName)

            if (letter != 0)

                strFriendlyName += (char)letter;

 

        // Return the friendly name

        return strFriendlyName;

    }

    else

        return null;

}

 

Opening a Connection to the Device

The IPortableDevice::Open method opens a connection to a device that’s attached to a PC. This method takes two arguments: the first is the Plug and Play identifier for the target device; the second is an IPortableDeviceValues object that specifies client information for the device. At a minimum, the client information must include the following:

Client Information

Description

Name

A friendly-name for the device.

Major Version Number

The device’s major version number.

Minor Version Number

The device’s minor version number.

Revision Number

The device’s most recent revision number.

 

The following StartProcessing helper function configures the client information in an IPortableDeviceValues object and then calls the Open method. Once the connection is opened, the StartProcessing method calls a RetrieveTemp method to retrieve the current temperature from the connected temperature-sensor device. After calling the RetrieveTemp method, a timer is enabled so that it fires an event every 3 seconds.

 

void StartProcessing(

                    PortableDeviceApiLib.PortableDeviceClass pPortableDevice,

                    PortableDeviceApiLib.IPortableDeviceValues pValues,

                    string PnPDeviceID)

{

 

   // We have to provide at the least our name, version, revision

   pValues.SetStringValue(

   ref PortableDevicePKeys.WPD_CLIENT_NAME, "Temp-Sensor Client");

   pValues.SetUnsignedIntegerValue(

   ref PortableDevicePKeys.WPD_CLIENT_MAJOR_VERSION, 1);

   pValues.SetUnsignedIntegerValue(

   ref PortableDevicePKeys.WPD_CLIENT_MINOR_VERSION, 0);

   pValues.SetUnsignedIntegerValue(

   ref PortableDevicePKeys.WPD_CLIENT_REVISION, 2);

 

   // We are now ready to open a connection to the device

   // We'll assume deviceID contains a valid WPD device path and connect to it

   pPortableDevice.Open(PnPDeviceID, pValues);

 

   // Retrieve and display the initial temperature

   strVal = RetrieveTemp(pPortableDevice, "Temperature");

   iVal = Convert.ToInt32(strVal);

 

   // Enable the timer for UI

   timer1.Interval = 3000;  // 3 second intervals

   timer1.Enabled = true;

 

}

 

Retrieving the Temperature Property

You can retrieve a collection of readable properties for a WPD device by calling the IPortableDeviceProperties::GetValues method. This method takes a collection of property keys as input and returns a collection of property values as output. Once the WPD driver returns the collection of values, you can retrieve an individual property’s value by calling one of the Getxxx methods on the IPortableDeviceValues interface. In the case of the temperature property, we’ll call the IPortableDeviceValues::GetSignedIntegerValue method. And, when we call this method, we’ll pass the corresponding property key (TEMP_SENSOR_CURRENT_VALUE) which we’ve added to the file portabledeviceconstants.cs.

The new entry in portabledeviceconstants.cs has the following form:

 

namespace PortableDeviceConstants

{

    class PortableDevicePKeys

    {

        static PortableDevicePKeys()

        {

          TEMP_SENSOR_READING.fmtid = new Guid(0xA7EF4367, 0x6550,      0x4055, 0xB6, 0x6F, 0xBE, 0x6F, 0xDA, 0xCF, 0x4E, 0x9F);

          TEMP_SENSOR_READING.pid = 2;

 

The following helper function, RetrieveTemp, retrieves the temperature property from the temperature sensor device and returns that value in string form.

string RetrieveTemp(

                    PortableDeviceApiLib.PortableDeviceClass pPortableDevice,

                    string objectID)

{

    string strTemp = String.Empty;  // Receives current temp

 

    //

    // Retrieve IPortableDeviceContent interface required to

    // obtain the IPortableDeviceProperties interface

    //

    PortableDeviceApiLib.IPortableDeviceContent pContent;

    pPortableDevice.Content(out pContent);

 

    //

    // Retrieve IPortableDeviceProperties interface required

    // to get all the properties

    //

    PortableDeviceApiLib.IPortableDeviceProperties pProperties;

    pContent.Properties(out pProperties);

 

    //

    // Create a key collection

    //

    PortableDeviceApiLib.IPortableDeviceKeyCollection pKeyCollection =

        (PortableDeviceApiLib.IPortableDeviceKeyCollection)

        new PortableDeviceTypesLib.PortableDeviceKeyCollectionClass();

 

    // Add the TEMP_SENSOR_READING property to the key collection

    // This value is defined in PortableDeviceConstans.cs

     pKeyCollection.Add(ref PortableDevicePKeys.TEMP_SENSOR_READING);

 

    //

    // Call the GetValues to retrieve the prop values for the given

    // key. Then, retrieve the TEMP_SENSOR_CURRENT_VALUE property

    // by calling the GetStringValue method.

    //

    PortableDeviceApiLib.IPortableDeviceValues pPropValues;

    pProperties.GetValues(objectID, pKeyCollection, out pPropValues);

    int iTemp = 0;

    pPropValues.GetSignedIntegerValue(ref PortableDevicePKeys.TEMP_SENSOR_READING, out iTemp);

          

    if (iTemp > 0)

        return iTemp.ToString();

    else

        return "Invalid temperature!";

 

}

 

There are two approaches to retrieving the current temperature for the temperature-sensor device: an application can register to receive a temperature-update event from the device, or, it can retrieve the temperature value directly. This article focuses on the latter. In this article we’ve used an application-defined timer event to retrieve the temperature property.

The first step in enabling an application-defined timer event requires that you add a timer object to your application’s form. (You’ll find the timer in the Components section of the Visual Studio Toolbox when viewing your application’s form in Design mode.)

Once you add the timer to your form, you can specify the interval at which the corresponding event will be fired, and you can enable the timer object. Both of these tasks were accomplished in the final lines of the StartProcessing method which was described previously in the Open a Connection to the Device section.

 

   // Enable the timer for UI

   timer1.Interval = 3000;  // 3 second intervals

   timer1.Enabled = true;

 

Each time the timer event fires, it calls the application’s Tick callback function. In our sample, the code for the timer1_Tick method retrieves the temperature from the device, converts it from degrees Kelvin to degrees Farenheit and then writes a string of HTML to disk. (The gadget script will read the contents of this file and use it to render the current temperature in the Sidebar.)

private void timer1_Tick(object sender, EventArgs e)

{

    // Use DateTime structure to retrieve timestamp

    DateTime dtNow = DateTime.Now;

 

    // Retrieve the temp prop

    strVal = RetrieveTemp(pPortableDevice, "Temperature");

    iVal = KelvinToFarenheit(strVal);

 

     // Use the StreamWriter object for file I/O

    StreamWriter sw = new StreamWriter("c:\\temp\\tempdata.txt");

 

    // Write the data to the file

    sw.WriteLine("<P>Office Temperature<P>" + Convert.ToString(iVal) + "&deg; Farenheit");

           

    // Close the streamwriter object

    sw.Close();

 

    // Update the Window text

    this.Text = dtNow.Hour + ":" + dtNow.Minute + ":" + dtNow.Second + "  " + Convert.ToString(iVal) + "°";

          

}

 

The following snippet contains the code for the KelvinToFarenheit method which converts the temperature value.

private int KelvinToFarenheit(string strTemp)

{

    double dTemp = 0.0;

    int iVal = 0;

 

    dTemp = Convert.ToDouble(strTemp);

    iVal = (int)(((dTemp - 273.15) * 1.8) + 32);

    return iVal;

}

 

The Temperature-Sensor Gadget

The temperature-sensor gadget displays the current office temperature in degrees Farenheit in the Windows Sidebar.

If you’ve never created a gadget for Windows Sidebar before, you should refer to the Gadget Development Overview.

The Temperature-Sensor Gadget Files

The temperature-sensor gadget consists of three files: an HTML file, an XML file, and a .jpg file. These files are described in the following table.

File Name

Description

UpdateTemperatureReading.htm

An HTML file that contains the script which retrieves the updated temperature data from the local disk every 3,000 milliseconds.

gadget.xml

The gadget manifest. This file specifies properties such as the name of the gadget, the name of its HTML file, a string of descriptive text, and so on.

background.jpg

A 163 x 58 pixel image that produces the gadget’s two-tone blue background color.

 

Retrieving the Current Temperature

The gadget’s HTML file, UpdateTemperatureReading.htm, contains a script which opens the file created by the application (c:\temp\tempdata.txt) and copies the contents of this file into a span element identified as “temperature_data” within the body of the gadget’s HTML.

This script creates a FileSystemObject which it uses to open and read the text file. It uses the OpenTextFile method to open the file and the ReadAll method to retrieve its contents.

The script uses the setTimeout method on the window object to recursively execute the update function every 3,000 milliseconds. (This function contains the code that retrieves the contents of c:\temp\tempdata.txt.)

<html>

 

<head>

 

<script>

var fileSystemObject = null;

 

function loaded()

{

       fileSystemObject = new ActiveXObject("Scripting.FileSystemObject");

       update();

}

 

function update()

{

   var textStream = fileSystemObject.OpenTextFile("c:\\temp\\tempdata.txt");

   try

          {

             temperature_data.innerHTML = textStream.ReadAll();

           }

         finally

           {

             textStream.Close();

             window.setTimeout(update, 3000);

           }

}

 

</script>

 

<style>

   body

        {

               margin: 0;

               width: 125px;

               height: 62px;

               font: 'normal 8pt Arial';

               line-height: 12pt;

              text-align:center;

   }

 

</style>

 

</head>

 

<body onload="loaded()">

<g:background src="background.png">

<span id="temperature_data"></span>

</g:background>

</body>

</html>

 

The HTML file also contains a description of the gadget’s window in the <style></style> tags that follow the script. Note that the width and height attributes match the width and height of the background image (background.png). In addition to specifying the size of the gadget’s window, the attributes found here specify the font used to render text, the line-height (which creates padding between the two lines of text), and the text alignment.

The g:background element specifies the source file (background.png) for the gadget’s background image.

The span element “temperature_data” identifies the location of the HTML which is copied from c:\temp\tempdata.txt by the Update function.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

 

Creating a Temperature Sensor Gadget for Windows Sidebar with C++

$
0
0

The following post describes a gadget for Windows Sidebar that displays the temperature reading from a temperature sensor device. Similar applications could be written to display: battery-level or available memory for a portable media device, contact lists from a mobile phone or PDA, route lists from a GPS device, and so on.

Introduction

Microcontrollers are used in a variety of applications from industrial to automotive to the home. One use that stretches across these boundaries is the microcontroller-based sensor which monitors parameters such as temperature, humidity, acceleration, distance, and light.

While the microcontroller device-universe has grown exponentially, there hasn’t been a common interface for integrating these devices with the PC. As a result, device and application developers were forced to write custom software to handle operations like data retrieval and display.

Microsoft recently introduced Microsoft® Windows® Portable Devices (WPD), a technology that addresses the challenges of integrating microcontroller-based devices with your PC. A microcontroller-based device can be as simple as a remote sensor or as complex as a mobile phone, a portable media player, or a digital camera. WPD consists of a driver model that runs in the user-mode driver framework (UMDF) and an API that simplifies the creation of applications for these devices.

This paper describes the use of the WPD API to create a Windows Console application that retrieves temperature data from a temperature sensor device. In addition, it describes a corresponding gadget for Windows Sidebar that consumes the data collected by the application.

The temperature sensor used by this application is based on the BASIC Stamp Activity Kit for Windows Portable Devices. This kit is offered by the Parallax Corporation in Rocklin, California. You can order the kit from the company’s Web site.

The console application obtains real-time temperature data from a Parallax temperature sensor device. It writes this data to a file on disk. A script in the gadget’s HTML file retrieves the data and renders it in Windows Sidebar.

The console application was written in Microsoft Visual Studio® 8 using the C++ programming language. The WPD gadget was written in HTML and Jscript®.

Downloading the Sample Application and Sidebar Gadget

To download the sample application, see this page on the Microsoft Downloads site.

To download the gadget, see this page on the Microsoft Downloads site.

Running the Sample Application

The sample application requires that the WPD temperature sensor driver (WpdTempSensorDriver.dll) is installed and that a temperature sensor device is connected over a standard RS232 port.

The WPD temperature sensor driver exposes a Temperature Sensor object. This object, in turn, exposes three programmatic elements: two properties and an event. These elements are described in the following table.

Programmatic Element

Description

Interval property

This read/write property specifies the frequency at which the device should return the current temperature. (This property is specified in milliseconds.)

Temperature property

This read-only property specifies the current temperature in degrees Kelvin.

Temperature-reading event

This event is fired each time the device retrieves the current temperature. (The Interval property specifies the frequency at which the device will fire this event.)

 

The sample console application, described in this white paper, registers to receive the Temperature-reading event notification each time it’s fired. Upon receiving the notification, the application writes the temperature data as HTML and saves it in an ASCII text file. The gadget, in turn, reads the HTML from this file and renders it in Sidebar.

Building and Installing the Temperature Sensor Device

The temperature sensor device referenced in this paper is based on a Parallax BS2 microcontroller and an AD592 Temperature Sensor transducer.

Parallax supplies a complete hardware kit that includes all of the required components. To order the kit, see this page on the Parallax site.

You can download the Basic Stamp source code for the temperature sensor device from the WHDC site.

Building and Installing the WPD Temperature Sensor Driver

In order to install the driver, download the source code and build it. For information about downloading the driver, see the following topic on the WHDC Web site.

Installing the Gadget

The download package referenced above in this article contains a file named WpdTemperatureSensor.gadget. Copy this file to your local machine, open Windows Explorer, and double-click this file. You will be asked whether you want to install the gadget. Click Install.

The gadget will be installed in the Desktop\\AppData\Local\Microsoft\Windows Sidebar\Gadgets folder.

Now you’re ready to build and run the sample application.

Building and Running the Sample Application

The download package included with this white paper contains the project, source, and header files for the sample console application that retrieves temperature data from the sensor.

After you’ve installed the temperature sensor device, driver, and gadget files, you’ll need to extract the contents of the download package to your development machine. This includes the following files.

·         stdafx.h

·         stdafx.cpp

·         deviceevents.cpp

·         sidebar_console.cpp

·         sidebar_console.vcproj

For a description of these files, see the Temperature Sensor Console Application section that follows.

If you haven’t installed the Windows SDK for Windows Vista, you’ll need to do so before building the project. For information about installing the Windows SDK, visit the Windows SDK web page.

 

Once the Windows SDK is installed, open the project file (sidebar_console.vcproj) in Visual Studio 8. Select the Build_Sidebar console option from the Build menu. Visual Studio 8 will build an executable file and place it in the \Debug subdirectory. You can test the gadget and verify that it works by selecting Start Debugging from the Debug menu.

WPD Application and Gadget Overview

The WPD application and gadget consists of two separate processes: a server and a client. The server is a console application which monitors the temperature sensor device for event notifications. Each time this application receives a notification, it writes corresponding temperature data formatted as HTML to a file on disk. The client is a script running in the gadget’s HTML file. This script retrieves the data from the file created by the console application and renders it in Sidebar.

The WPD Console Application registers to receive temperature events from the device. These events are fired at an interval specified by the device’s Interval property. (The default value for this property is 2,000 milliseconds.)

The event handler in the console application writes the temperature as a string of HTML to an ASCII text file on the local disk. This string has the following appearance.

<P>Office Temperature<P>304&deg; Kelvin

The gadget’s script executes every 3,000 milliseconds. The script reads the string of HTML created by the console application and inserts that string into the body of the gadget’s HTML.

The Temperature Sensor Console Application

The temperature sensor console application is a simple application that accomplishes the following tasks.

·         Searches for the temperature sensor device and opens a connection if the device is found.

·         Registers to receive event notifications from the device.

·         Enters a loop and responds to any one of three inputs from the user.

·         While listening for user input, and for as long as the event registration is intact, writes temperature data to the local disk.

The temperature sensor application project consists of the following files:

File

Description

stdafx.h

Contains #defines for the target platform as well as #includes for the standard system include file.

deviceevents.cpp

Implements IPortableDeviceEventCallback which is required by any WPD application that registers to receive device events.

sidebar_console.cpp

Implements the entry point for the console application and the helper functions which perform tasks like searching for the device and establishing a connection.

stdafx.cpp

Contains #include for stdafx.h

sidebar_console.vcproj

The Visual Studio 8 project file.

 

Most of work accomplished by the application is found in the module named sidebar_console.cpp; the exception is the event handling code which is found in the module deviceevents.cpp.

Opening a Connection to the Temperature Sensor Device

The WPD API provides a set of interfaces that a Windows programmer can use to accomplish tasks like enumerating connected devices, opening a device, closing a device, enumerating objects on a device, reading and writing properties on a device, sending a command to a device, and registering to receive events from a device.

One of the primary interfaces is the IPortableDevice interface which supports the methods a programmer calls to open a device, receive events, send a command, and so on.

The first task the sample application accomplishes is to open (establish a connection to) the temperature sensor device. It does this by calling the IPortableDevice::Open method. This method takes two parameters: a pointer to a string that specifies a special identifier for the device and a pointer to an array of key/value pairs that specify information about the calling application.

 

hr = CoCreateInstance(CLSID_PortableDevice,

                         NULL,

                         CLSCTX_INPROC_SERVER,

                         IID_IPortableDevice,

                         (VOID**) &pIPortableDevice);

   if (SUCCEEDED(hr))

   {

       if (pIPortableDevice != NULL)

       {

            hr = (pIPortableDevice)->Open(DeviceID, pClientInformation);

           if (FAILED(hr))

           {

               // Release the IPortableDevice interface

               // because we cannot proceed with an

               // unopen device.

               pIPortableDevice = NULL;

            }

           else

           {

               printf("Device successfully opened.\n\r\n\r");

           }

        }


In order to retrieve the device identifier DeviceID, which is passed as the first parameter to IPortableDevice::Open, the application first needs to determine whether the temperature sensor device is connected to the PC and, if it is, retrieve the identifier.

The FindDevice helper function in the Sidebar_Console.cpp module accomplishes this work. The FindDevice function has two arguments: the first is a “friendly” name for the target device; the second is the special Plug and Play identifier that the WPD API returns as a match for this friendly name.

The FindDevice helper function accomplishes the following tasks.

1.       Retrieves a count of devices connected to the PC.

2.      Retrieves an array of friendly names for each device.

3.      Compares the friendly name passed in its first argument to the friendly names for each of the connected devices.If a match is found, it retrieves the Plug and Play identifier for the target device.

Retrieving a Count of Connected Devices

The first task of the FindDevice function is the retrieval of a count of connected devices. This is done in two stages: first, by creating an IPortableDeviceManager object, and second, by calling the IPortableDeviceManager::GetDevices method. (In order to retrieve a count of connected devices, the first argument to GetDevices is set to NULL.)

 

// CoCreate the IPortableDeviceManager interface to enumerate

// portable devices and to get information about them.

hr = CoCreateInstance(CLSID_PortableDeviceManager,

              NULL,

              CLSCTX_INPROC_SERVER,

              IID_IPortableDeviceManager,

              (VOID**) &pPortableDeviceManager);

if (FAILED(hr))

{

     printf("! Failed to CoCreateInstance CLSID_PortableDeviceManager\n\r\n\r");

     return hr;

}

 

// First, pass NULL as the LPWSTR array pointer to get the total

// number of devices found on the system.

if (SUCCEEDED(hr))

{

    hr = pPortableDeviceManager->GetDevices(NULL, &cPnPDeviceIDs);

    if (FAILED(hr))

    {

        printf("! Failed to get number of devices on the system\n\r\n\r");

        return hr;

    }

}

Retrieving a Plug and Play name for the Temperature Sensor

WPD supports two types of device names: friendly names and Plug and Play names. The friendly names are the names applications display to the user. The friendly name for the temperature sensor device is: "Parallax BS2 Temperature Sensor". The Plug and Play names are used internally by WPD to identify devices.  The Plug and Play name for the temperature sensor device is:

\\?\root#wpd#0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}

This string uniquely identifies the device instance and the device interface, and it is defined as part of the Windows Driver Model (WDM). For more information, refer to this MSDN topic.

 

After retrieving a count of connected devices and storing it in the cPnPDeviceIDs variable, FindDevice iterates through the available devices until it finds a device whose friendly name matches the string “Parallax BS2 Temperature Sensor”. If this match is made, FindDevice returns the corresponding Plug and Play name in the DeviceID argument.

 

        if (cPnPDeviceIDs > 0)

        {

            pPnpDeviceIDs = new LPWSTR[cPnPDeviceIDs];

            if (pPnpDeviceIDs != NULL)

            {

                DWORD dwIndex = 0;

 

                hr = pPortableDeviceManager->GetDevices(pPnpDeviceIDs, &cPnPDeviceIDs);

                if (SUCCEEDED(hr))

                {

                    // For each device found, retrieve the friendly

                    // name and compare it to the submitted name.

                    for (dwIndex = 0; dwIndex < cPnPDeviceIDs; dwIndex++)

                    {

                        CAtlStringW FriendlyName;

                        hr = RetrieveFriendlyName(pPortableDeviceManager, pPnpDeviceIDs[dwIndex], FriendlyName);

                        if ((hr == S_OK) && (DeviceName.CompareNoCase(FriendlyName) == 0))

                        {

                             DeviceID = pPnpDeviceIDs[dwIndex];

                             bFoundDevice = TRUE;

                             break;

                        }

                    }

                }

                else

                {

                    printf("! Failed to get the device list from the system\n\r\n\r");

                }

 

                if (SUCCEEDED(hr) && (!bFoundDevice))

                {

                    printf("! Failed to find a matching device\n\r\n\r");

                    hr = E_FAIL;

                }

 

 

                // Free all returned PnPDeviceID strings by using

                // CoTaskMemFree.

                // NOTE: CoTaskMemFree can handle NULL pointers, so

                //       no NULL check is needed.

                for (dwIndex = 0; dwIndex < cPnPDeviceIDs; dwIndex++)

                {

                    CoTaskMemFree(pPnpDeviceIDs[dwIndex]);

                    pPnpDeviceIDs[dwIndex] = NULL;

                }

 

                // Delete the array of LPWSTR pointers.

                delete [] pPnpDeviceIDs;

                pPnpDeviceIDs = NULL;

        }

        else

        {

            printf("! Failed to allocate memory for LPWSTR array\n\r\n\r");

            hr = E_OUTOFMEMORY;

        }

       }

 

    return hr;

}

 

The application passes the returned Plug and Play name as the first argument to IPortableDevice::Open when it opens the device.

Note that the FindDevice function calls the RetrieveFriendlyName helper function to retrieve a friendly name for each Plug and Play name returned by IPortableDeviceManager::GetDevices. The RetrieveFriendlyName function calls the IPortableDeviceManager::GetFriendlyName method twice to retrieve the friendly name string. The first time, it calls this method to retrieve a count of characters in the friendly name string; the second time, it calls this method to retrieve the actual string.

 

    // First, pass NULL as the LPWSTR return string parameter to get

    // the total number of characters to allocate for the string

    // value.

    hr = pPortableDeviceManager->GetDeviceFriendlyName(pPnPDeviceID, NULL, &cchFriendlyName);

    if (FAILED(hr))

    {

        printf("! Failed to get number of characters for device friendly name.\n\r\n\r");

        return hr;

    }

 

    // Second, allocate the number of characters needed and retrieve

    // the string value.

    if ((hr == S_OK) && (cchFriendlyName > 0))

    {

        wszFriendlyName = new WCHAR[cchFriendlyName];

        if (wszFriendlyName != NULL)

        {

            hr = pPortableDeviceManager->GetDeviceFriendlyName(pPnPDeviceID, wszFriendlyName, &cchFriendlyName);

            if (SUCCEEDED(hr))

            {

                FriendlyName = wszFriendlyName;

            }

            else

            {

                printf("! Failed to get device friendly name\n\r\n\r");

            }

 

            // Delete the allocated friendly name string.

            delete [] wszFriendlyName;

            wszFriendlyName = NULL;

        }

 

Receiving Device Events

The WPD API and driver model were designed so devices can issue events and applications can register to receive notifications when these events occur. Applications that receive events must implement the IPortableDeviceEventCallback interface. This interface supports a single OnEvent method. The driver calls this method each time an event is fired. However, in order for a driver to call this method, an application first needs to register with the driver. An application registers with the driver by calling the IPortableDevice::Advise method. The third argument for this method, pCallback, is a pointer to the IPortableDeviceEventCallback interface that the application implemented.

The temperature sensor device issues a single temperature-reading event that the sample application registers to receive. Each time the driver calls the OnEvent method, the sample application writes the received temperature to a local file on disk. The gadget’s script then reads from this file to update the gadget with the most recent office temperature.

Implementing IPortableDeviceEventCallback

The sample application implements the IPortableDeviceEventCallback interface in the DeviceEvents.cpp module. Most of the code in this module was taken from the WpdApiSample application that ships with the Windows SDK. The following changes were made to the original file.

·         The TEMPERATURE_SENSOR_READING property key was defined and declared.

·         The OnEvent method was modified to process the TEMPERATURE_SENSOR_READING property and write the new temperature value to disk.

The IPortableDeviceEventCallback interface is implemented in the CPortableDeviceEventsCallback class in DeviceEvents.cpp. This class supports the following methods.

Method

Description

CPortableDeviceEventsCallback

Object constructor

~CPortableDeviceEventsCallback

Object destructor

QueryInterface

Returns a pointer to the IPortableDeviceEventCallback interface.

AddRef

Increments the object’s reference count.

Release

Decrements the object’s reference count.

OnEvent

Event handler. WPD will call this method if the application is registered to receive event notifications.

 

In addition to implementing IPortableDeviceEventCallback, the DeviceEvents.cpp module also contains two functions that the application calls in order to register and unregister for event notifications. These functions are described in the following table.

Function

Description

RegisterForEventNotifications

Registers the application to receive event notifications. (This method is called when the application first starts.It’s also called if the user requests registration by entering the number 2 at the command prompt.)

UnregisterForEventNotifications

Unregisters the application from receiving event notifications. (This method is called if the user requests cancellation of registration by entering the number 1 at the command prompt.)

 

Implementing the IPortableDeviceEventCallback::OnEvent Method

Once an application registers to receive event notifications, the WPD device driver will call the IPortableDeviceEventCallback::OnEvent method whenever the device fires an event. Each time the driver calls this method, it passes data associated with the given event in an IPortableDeviceValues interface. The pEventParameters argument is a pointer to this interface.

The temperature sensor device fires one event per temperature reading update, and the accompanying data is a single, signed integer value which specifies the current temperature in degrees Kelvin. The sample application retrieves this value by calling the IPortableDeviceValues::GetSignedIntegerValue method and passing the property key for the TEMPERATURE_SENSOR_READING property as the first argument. (This property key was declared and defined at the top of DeviceEvents.cpp.)

 

HRESULT __stdcall OnEvent(

    IPortableDeviceValues* pEventParameters)

{

    HRESULT hr = S_OK;

    LONG i=0;

    HANDLE hFile;

    DWORD dwBytesWritten;

    char strHtmlPrefix[]="<P>Office Temperature<P>";

    char strHtmlSuffix[]="&deg; Kelvin";

    char strHtml[40];

 

    if (pEventParameters != NULL)

    {

        pEventParameters->GetSignedIntegerValue(TEMPERATURE_SENSOR_READING, &i);

        sprintf_s(strHtml, "%s%d%s\0", strHtmlPrefix, i, strHtmlSuffix);

 

        hFile = CreateFileW(_T("c:\\temp\\tempdata.txt"),

            GENERIC_WRITE,

            0,

            NULL,

            CREATE_ALWAYS,

            FILE_ATTRIBUTE_NORMAL,

            NULL);

 

        if (WriteFile(hFile, strHtml, sizeof(strHtml), &dwBytesWritten, NULL))

        {

            printf("Current temperature: %d Kelvin\n\r", i);

            printf("Temperature written to target file.\n\r\n\r");

        }

        else

        {

            printf("Temperature not written to target file.\n\r");

            printf("WriteFile failed with error %d.\n\r\n\r", GetLastError());

        }

        CloseHandle(hFile); // We need to close the handle.

    }

     return hr;

}

 

Upon retrieving the current temperature, the OnEvent method writes a string of HTML to a temporary file on disk (c:\temp\tempdata.txt). The gadget reads this string of HTML from the temporary file and incorporates it into the gadget’s own HTML. In addition, the OnEvent method writes the current temperature to the console window.

Implementing the RegisterForEventNotifications Function

The sample application supports a single function, RegisterForEventNotfications, which it calls at startup and if the user explicitly requests registration. This function performs the following tasks.

4.      Examines the event registration cookie (g_strEventRegistrationCookie) to determine whether the application is already registered.

5.      If not, creates an instance of the CPortableDeviceEventsCallback object.

6.      Calls the IPortableDevice::Advise method to complete the registration.

7.      If registration is successful, assigns the event cookie returned by the Advise method to the g_strEventRegistrationCookie variable.

 

void RegisterForEventNotifications(IPortableDevice* pDevice)

{

    HRESULT                        hr = S_OK;

    LPWSTR                         wszEventCookie = NULL;

    CPortableDeviceEventsCallback* pCallback = NULL;

 

    if (pDevice == NULL)

    {

        return;

    }

 

    // Check to see if we already have an event registration cookie.

    // If so, then avoid registering again.

    // NOTE: An application can register for events as many times as.

    //       wanted. This sample only keeps a single registration

    //       cookie around for simplicity.

    if (g_strEventRegistrationCookie.GetLength() > 0)

    {

        printf("This application has already registered to receive device events.\n\r\n\r");

        return;

    }

 

    // Create an instance of the callback object. This will be

    // called when events are received.

    if (hr == S_OK)

    {

        pCallback = new CPortableDeviceEventsCallback();

        if (pCallback == NULL)

        {

            hr = E_OUTOFMEMORY;

            printf("Failed to allocate memory for IPortableDeviceEventsCallback object, hr = 0x%lx\n\r\n\r",hr);

        }

    }

 

    // Call Advise to register the callback and receive events.

    if (hr == S_OK)

    {

        hr = pDevice->Advise(0, pCallback, NULL, &wszEventCookie);

        if (FAILED(hr))

        {

            printf("! Failed to register for device events, hr = 0x%lx\n\r\n\r",hr);

        }

    }

 

    // Save the event registration cookie if event registration was

    // successful.

    if (hr == S_OK)

    {

        g_strEventRegistrationCookie = wszEventCookie;

    }

 

    // Free the event registration cookie if one was returned.

    if (wszEventCookie != NULL)

    {

        CoTaskMemFree(wszEventCookie);

        wszEventCookie = NULL;

    }

 

    if (hr == S_OK)

    {

        printf("This application has registered for device event notifications.\r\n\r\n");

    }

 

    // If a failure occurs, remember to delete the allocated callback

    // object if one exists.

    if (pCallback != NULL)

    {

        pCallback->Release();

    }

}

Implementing the UnregisterForEventNotifications Function

The sample application supports a single function, UnregisterForEventNotfications, which it calls if the user explicitly cancels registration. This function performs the following tasks.

8.      Calls the IPortableDevice::Unadvise method and passes the registration cookie (which is also the event cookie returned by the IPortableDevice::Advise method).

9.      Sets the internal event registration-cookie variable g_strEventRegistrationCookie to an empty string.


void UnregisterForEventNotifications(IPortableDevice* pDevice)

{

    HRESULT hr = S_OK;

 

    if (pDevice == NULL)

    {

        return;

    }

 

    hr = pDevice->Unadvise(g_strEventRegistrationCookie);

    if (FAILED(hr))

    {

        printf("! Failed to unregister for device events using registration cookie '%ws', hr = 0x%lx\n\r\n\r",g_strEventRegistrationCookie.GetString(), hr);

    }

 

    if (hr == S_OK)

    {

        printf("This application used the registration cookie '%ws' to unregister from receiving device event notifications\n\r\n\r", g_strEventRegistrationCookie.GetString());

    }

 

    g_strEventRegistrationCookie = L"";

}

 

Handling User Input

The sample application displays a simple menu in the console window when it begins execution. This menu has three options corresponding to three integer values.

Option/Value

Description

1

Unregister for device notifications. (The application calls the UnregisterForEventNotifications function.)

2

Register for device notifications. (The application calls the RegisterForEventNotifications function.)

99

Exit. (The application exits.)

 

The code that supports the menu and its options is found in the ProcessInput function in the Sidebar_Console.cpp module.

 

void ProcessInput(CComPtr<IPortableDevice> pIPortableDevice)

    {

        HRESULT hr              = S_OK;

        UINT    uiSelection     = 0;

        CHAR    szSelection[81] = {0};

 

        printf("\n\n");

        printf("===============================================\r\n");

        printf("1.  Unregister for device notifications.\r\n");

        printf("2.  Register for device notifications.\r\n");

        printf("99. Exit.\r\n");

        printf("===============================================\r\n");

        printf("\n\n");

 

        while (uiSelection != 99)

        {

            ZeroMemory(szSelection, sizeof(szSelection));

   

            hr = StringCbGetsA(szSelection,sizeof(szSelection));

 

            if (SUCCEEDED(hr))

            {

                uiSelection = (UINT) atoi(szSelection);

                switch (uiSelection)

                {

                    case 1:

                        // Unregister to receive device events

                        UnregisterForEventNotifications(pIPortableDevice);

                    break;

                    case 2:

                        // Register to receive device events

                        RegisterForEventNotifications(pIPortableDevice);

                    default:

                    break;

                }

            }

            else

            {

                printf("! Failed to read menu selection string input, hr = 0x%lx\n\r\n\r",hr);

            }

        }

    }

 

The Temperature Sensor Gadget

The temperature sensor gadget displays the current office temperature in degrees Kelvin in Windows Sidebar.

If you’ve never created a gadget for Windows Sidebar before, refer to the Gadget Development Overview.

 

The Temperature Sensor Gadget Files

The temperature sensor gadget consists of three files: an HTML file, an XML file, and a JPEG file. These files are described in the following table.

File Name

Description

UpdateTemperatureReading.htm

An HTML file that contains the script which retrieves the updated temperature data from the local disk every 3,000 milliseconds.

gadget.xml

The gadget manifest. This file specifies properties such as the name of the gadget, the name of its HTML file, a string of descriptive text, and so on.

background.jpg

A 163 x 58 pixel image that produces the gadget’s two-tone blue background color.

 

Retrieving the Current Temperature

The gadget’s HTML file, UpdateTemperatureReading.htm, contains a script which opens the file created by the console application (c:\temp\tempdata.txt) and copies the contents of this file into a span element identified as “temperature_data” within the body of the gadget’s HTML.

This script creates a FileSystemObject which it uses to open and read the text file. It uses the OpenTextFile method to open the file and the ReadAll method to retrieve its contents.

The script uses the setTimeout method on the window object to recursively execute the update function every 3,000 milliseconds. (This function contains the code that retrieves the contents of c:\temp\tempdata.txt.)

 

<html>

 

<head>

 

<script>

var fileSystemObject = null;

 

function loaded()

{

       fileSystemObject = new ActiveXObject("Scripting.FileSystemObject");

       update();

}

 

function update()

{

      var textStream = fileSystemObject.OpenTextFile("c:\\temp\\tempdata.txt");

      try

          {

             temperature_data.innerHTML = textStream.ReadAll();

           }

         finally

           {

             textStream.Close();

             window.setTimeout(update, 3000);

           }

}

 

</script>

 

<style>

      body

        {

               margin: 0;

               width: 125px;

               height: 62px;

               font: 'normal 8pt Arial';

               line-height: 12pt;

               text-align:center;

      }

 

</style>

 

</head>

 

<body onload="loaded()">

<g:background src="background.png">

<span id="temperature_data"></span>

</g:background>

</body>

</html>

 

The HTML file also contains a description of the gadget’s window between the <style> and </style> tags that follow the script. Note that the width and height attributes match the width and height of the background image (background.png). In addition to specifying the size of the gadget’s window, the attributes found here specify the font used to render text, the line height (which creates padding between the two lines of text), and the text alignment.

The g:background element specifies the source file (background.png) for the gadget’s background image.

The span element “temperature_data” identifies the location of the HTML which is copied from c:\temp\tempdata.txt by the Update function.

 

More Information

·         For general information about developing gadgets for Windows Sidebar, see the Gadget Development Overview.

·         To learn more about the BASIC Stamp Activity Kit for Windows Portable Devices, refer to the Parallax site.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

 

Visual Basic .Net Sample for WPD

$
0
0

We've created a Visual Basic .Net sample for WPD and posted it to CodePlex: Microsoft's open source project-hosting web site. Take a look at http://www.codeplex.com/wpdtempsensor

In addition to the Visual Basic project and source files, we've posted a document that describes the sample. This document also describes how you can address and resolve the COM Interop issues associated with .Net and WPD.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

New Features for Windows 7

$
0
0

In Windows 7 we introduced several new features that improve how users discover and use devices connected to their PC. These new features simplify how device manufacturers present their devices, applications, and services in Windows. This behavior is exposed in the new Device Stage™ experience (see Better Device Management here).

In the Windows Portable Device (WPD) space the Windows team made significant investments to address concerns raised by device manufacturers. One of the key improvements was the addition of the Device Service. A Device Service is an extension of the functional object that was supported in prior versions of WPD. Applications can use Device Services to discover device capabilities and to interact more efficiently with content.

In addition to the Device Service, WPD was further improved by investing in additional transports: support for MTP over Bluetooth has been added. With multiple transports (USB, IP, and Bluetooth) now available, the next step was to enable a seamless data transfer scenario when the device supports more than one transport.

Now that we have introduced some of the key Windows 7 features for WPD, we’ll take a closer look.

MTP Device Services for Windows 7

MTP has proven to be an excellent protocol for exchanging files and performing device command and control. However, it is not well-suited for newer, more advanced scenarios.  These scenarios are now supported with the introduction of the Device Services architecture. This architecture allows applications to locate and use various services and content types on a device. 

The new Device Services architecture allows applications to locate, consume, and interact with both content and functionality on the device.  Our flagship use of Device Services in Windows 7 is to support Personal Information Management (PIM) data, settings, and restricted-capabilities content types on a device. The resulting architecture is also the means for future extensibility by 3rd parties: An IHV can implement an application (or web script) and a custom service that interoperate (without having to wait for new functionality in the next OS release).

Device Services are identified by Globally Unique Identifiers (GUIDS). These GUIDS allow hardware vendors to specify new device services independently of each other without the risk of conflicts.

The native Windows 7 supported services defined for portable devices are:

Calendar Service - The calendar service contains the data for a calendar, and uses a synchronization service to facilitate synchronization with the PC. 

Contact Service - The contact service contains the data for contacts, and can optionally use a synchronization service to facilitate synchronization with the PC. 

Notes Service - The notes service uses a synchronization service to sync notes to and from the device. 

Task Service - The task service uses a synchronization service to synchronize tasks to and from the device. 
 
Status Service - The status service reports various status properties about the device to the PC.

Hints Service - The hints service enables the device to select preferred storage locations for various content types in legacy storages.

Device Metadata Service - The device metadata service permits the delivery of Device Stage™ metadata by the device during device installation.

Ringtone Service - The ringtone service provides a method to copy ringtones from the PC to the target ringtone storage on the device.   

Enumeration Synchronization Service - The enumeration synchronization service describes basic synchronization capabilities for a storage-based device. This is an “abstract” Device Service, which, analogous to an interface in C++, describes the capabilities of a “data” service that supports basic synchronization.  For example, a device that supports basic synchronization of contacts will contain a Contacts Service that implements the Enumeration Synchronization Service.

Anchor Synchronization Service - The anchor sync service is another abstract MTP Device Service defined to enable other services to opt in to a sync relationship using anchor-based synchronization semantics.  Anchor-based synchronization uses a simple tick-count based mechanism for change detection (e.g. timestamp, tick counts).

Sample implementations of some of these services along with device simulation software is provided in the 7R2 version of the Windows 7 Portable Device Enabling Kit and is now available here.  

MTP Device Services Extension

In addition to the pre-defined device services mentioned above, Windows is also capable of supported custom device services that use the newly defined MTP Device Services Extension to MTP. The MTP Device Services Extension enables Windows to locate and use various services and content types located on a device.

In the recent applications of MTP without device services, content is distributed throughout a device, which makes it difficult for the operating system to determine where the useful assets are and how to deploy them. With the creation of the Device Services extension to the MTP protocol, Windows now has the ability to locate, consume, and interact with this content in practical ways based on Microsoft and manufacturer defined services. This feature is particularly useful in accessing device content that is not based on file system data, accessing settings, or that has restricted capabilities. For example, a device manufacturer could define a custom SMS service.

While MTP already supports enumeration by format, it is cumbersome when applying rich semantics to simple hierarchical storage. With Device Services, content and functionality can be combined under a single functional object, resulting in well-scoped access to only the content and functions specific to that service.

Windows Portable Devices (WPD) Platform Enhancements

For Windows 7, in addition to the legacy behavior, Windows Portable Devices (WPD) supports a new Device Services model. A Device Service is an extension of the functional object supported in prior versions of WPD. Applications will use service objects to discover device capabilities and to interact more efficiently with content.
 
Device Services are available to applications as part of the WPD API. Some Device Services include built-in support by Windows 7. Two examples of operating-system components that use Device Services are the Windows Portable Devices Sync Provider and the Windows Portable Device Status Provider. 

The Windows Portable Devices Sync Provider determines whether a cellular phone supports basic contacts synchronization by querying for its implementation of the Enumeration Sync device service on the device. The provider then uses this service to locate all contacts stored on the device without having to search the entire storage hierarchy.  

The Windows Portable Device Status Provider retrieves real-time status information from a cellular phone that supports the Status device service, and relays information including battery life, signal strength, unread text messages, and number of missed calls to Device Stage™.

Note that although cellular phones are featured in both sceanrios above, Device Services are not limited to a specific WPD device class. Further examples of Device Services will be covered in a later post.

Device Stage™

Device Stage provides additional customization of the user’s device experience by providing a new way for users to interact with their cell phones, cameras, printers, and portable media players. Device Stage provides a visual interface that makes it easy for users to find and use applications and services for a given device.

Through a new set of XML schemas supported in Windows 7, device makers can:

  • Provide rich branding and customization for their device
  • Define applications and services that are specific to each device

Device Stage can be accessed by double clicking a supported device from within the Devices and Printers folder, or for portable devices, by clicking the device icon which appears on the taskbar when the device is connected.

The Device Stage user interface follows the model of any shell view in Windows, and Device Stage devices appear on the Windows taskbar whenever the Device Stage is open.

Device makers may enable additional features by supporting standard and vendor-unique Device Services, and authoring Device Stage tasks that make use of the service. When a device is connected which supports the any of the authored tasks, the tasks will appear within the Device Stage experience. Optionally, additional Partner applications and hosted web tasks may be specified within the Device Stage XML without modifications to device firmware.  These custom device experiences (via Hosted Site with Device option) within Device Stage are made possible by enhancements to the WPD API (mentioned above) and driver model which may now be extended via scripting and custom MTP Device Service extensions on the device. To assist device makers in all of this work, Microsoft has released a Portable Device Enabling Kit with documentation and device code samples. The 7R2 version of the kit is now available here.  

In the Windows 7 Control Panel, there is a new experience known as the Devices and Printers folder through which users can see all the devices that are connected to their PC. The devices in this folder are physically connected via USB or paired via Wi-Fi or Bluetooth interfaces.  (As a general rule, internal PC devices or device functions will not be exposed in this folder.)

Using a new set of XML schemas, device makers can customize how their device is described and presented within the Devices and Printers folder. For more information on developing metadata packages for both the Devices and Printers folder and Device Stage in Windows 7, see the Windows Device Experience topic on WHDC.

Extended Transport Support

In Windows 7 we have expanded support with WPD over other transports. A new class driverhas been introduced to enable MTP over Bluetooth.  With the support in Windows 7 for multiple transports the next step was to make Windows multi-transport aware, and the addition of the WPD composite driver provides the bridge between a logical device and the transports it simultaneously supports. The new features and functionality mentioned above are supported across all three of the MTP transports (Bluetooth, IP, and USB).

Resources:

Information on MTP can be found at http://www.usb.org/developers/devclass_docs/MTP_1.0.zip

Details on MTP over Bluetooth and MTP over IP are provided in the documentation that ship in the Windows 7 Portable Device Enabling Kit for MTP and on MSDN. The kit can be downloaded from http://www.microsoft.com/whdc/device/wpd/MTP-DEK_Win7.mspx.

Information on the WPD APIs can also be found on MSDN.

That’s it for now. Stay tuned for more posts diving into the details of the new Windows 7 WPD features.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

Introducing Device Services

$
0
0

What are Device Services?

"Device Services" is a brand new concept introduced in Windows 7 that provides a framework for device manufacturers to extend device functionality, and new APIs for applications to discover and access that extended functionality using WPD. We decided to call these extensions to device functionality "Device Services" because they foster rich interaction between the PC and devices by providing a model for devices to formalize extension capabilities and make these capabilities accessible to applications.

Device Services are available in Windows 7 and Windows Vista with the Platform Update for Windows Vista.

Why do we need Device Services?

First, a quick historical perspective. Media Transfer Protocol (MTP - one of the protocols supported within WPD) was introduced beginning with Windows Media Player 10 as an extension of the Picture Transfer Protocol (PTP). MTP became a USB standard, and was widely adopted by a myriad of device classes: media players, mobile phones, cameras, Xbox 360s, etc.  True to its PTP origins, MTP 1.0 was very media-centric; the scenarios it excelled in revolved around browsing device content and exchanging media files (images, music, and video, etc) between device and the PC.

Enabling PIM Scenarios

With Windows 7, we shifted our focus to enabling more device-to-PC scenarios centered around managing and accessing Personal Information Management (PIM) data (contacts, tasks, calendar, etc.) on the device. These involved more complex operations such as SMS, email and advanced synchronization with Outlook™. Non-media files present additional challenges in accessing and enumerating them efficiently on the device. Unlike an MP3 or a JPEG file, a contact or task may not be represented by a physical file on the device storage.  Depending on device implementation, these objects could be stashed anywhere: in databases, in SIM cards, etc.

Honey, where did you put my contacts (and music, and photos, and ...)?

Another common problem is that there was no standardized method for a device to figure out where to put its content.  Most applications would have to guess at a location, choose or specify an application-specific location, or crawl the entire device, often just to locate a subset of content. [Aside: In Vista, WPD does support the concept of "Device Hints", where devices/drivers provision a list of known folders that would accept particular content types. Unfortunately, this was not uniformly adopted by devices/drivers, and was limited to known WPD content types].

Crowded MTP 1.0 Extension Space

Last but not the least, MTP 1.0 had inherent limitations for growth and new functionality. The operation codes and property codes were constrained by a DWORD (4 byte) numerical space, with only a small subset allocated for extension codes. Without a notion of vendor-specific namespaces, new extensions must be careful not to collide with existing extensions. This would usually add the overhead of having to publish the codes or risk incompatibilities with applications that rely on existing extensions.  And, if the device/application needs to support new types of content, adding a new object format involves updating the MTP Specification, which is not easily revised.

How do Device Services help?

To solve the problems and overcome the limitations described above, we introduced the concept of Device Services to MTP. In a nutshell, "MTP Device Services" is an MTP Extension that defines a set of MTP operations revolving around a new type of object known as a "service."

A service is composed of 3 parts:

  1. The core definition of that service and what it does, including its globally unique identifier (GUID).
  2. The properties of the service itself, and
  3. The properties and capabilities of objects and methods of that service.

Properties are represented by PROPERTYKEYS, and formats are represented by GUIDs, which eliminate the operation and format number space restrictions. Together, these provide the structure that applications would rely on to programmatically discover and use services in a consistent way. 

The following describes the most common ways in which Device Services are used today:

  1. Provide a set of properties and events associated with a common scenario. One example is the Status Service, which defines properties for the battery level, the number of missed calls, the number of new voicemail messages, and the number of new pictures acquired by a device. These may be custom properties, and any application that discovers this service can programmatically discover these properties.

  2. Provide a known source and/or sink for content. The Contacts, Calendar, Task, and Notes services are all services that allow applications to access and store PIM data. These are analogous to "virtual storages."  The device represents the content in a standard location (as child objects of these services) so that applications do not have to grovel through the device hierarchy to find them. At the same time, this abstraction allows a device implementation greater control over how objects are actually stored on the device. Services are not limited to the known MTP or WPD formats, and may support any arbitrary type of data. For example, devices that wish to provide a Device Stage™ experience upon install can implement a Device Metadata Service that stores *.devicemetadata-ms files.

  3. Provide a set of functionality associated with a common scenario.  The Full Enumeration and Anchor Synchronization services support "methods."  Methods are like extensions to WPD commands, with the added benefits of being easily discoverable and supporting both synchronous and asynchronous invocation. The Anchor Synchronization Service features a method that allows a synchronization application to retrieve the set of changes to content on the device from a given anchor value (which can be a timestamp or a tick count). Methods also provide a flexible way to access content residing on multiple services. For instance, the Ringtones Service contains a method that allows an application to associate a ringtone (an object within the Ringtones Service) with a contact (an object within the Contacts Service).

How are Device Services used in Windows 7?

After creating Device Services, we put it through its paces in real-life scenarios that are part of Windows 7.

Device Stage™- Uses the Status service to display status on the Branding bar, and on the Taskbar and preview pane.

Windows 7 Synchronization – The Portable Devices Sync Provider uses the Contact Service, Calendar Service, Task Service, and Notes Service to access PIM data on the device. In addition, the Enumeration Synchronization Service and Anchor Synchronization Service are used to determine the level of synchronization capabilities that a device supports.

Windows Media Player and Windows Explorer – Both these applications use WPD Device Hints to determine content locations more efficiently.  For MTP devices, WPD Device Hints is now implemented at the MTP layer as a Hints Service. Devices that implement the Hints Service get WPD Device Hints for free.

Ringtone Editor – Uses the Ringtones Service to transfer user-created ringtones to the device, and to associate a ringtone with a contact that is located using the Contacts service.

WPD Class Installer – Uses the Device Metadata Service to retrieve and transfer metadata stored on the device, so that the richer, device-manufacturer-customized, Device Stage experience gets presented to the user after the first time the device is connected and installed.

More Information

We hope this post has given you a taste of what Device Services can do. Here are some additional links for further information.

Device Services:

Media Transfer Protocol: http://www.usb.org/developers/devclass_docs/MTP_1.0.zip

MTP Device Services Extension Specification: http://www.microsoft.com/whdc/device/wpd/MTPDevServExt_Spec.mspx

Windows Portable Device Enabling Kit for MTP: http://www.microsoft.com/whdc/device/wpd/MTP-DEK_Win7.mspx

Application Development:

Using the WPD API for Device Services: http://msdn.microsoft.com/en-us/library/dd319325(VS.85).aspx

The WPD Services API Sample: (available from the Windows 7 SDK): http://msdn.microsoft.com/en-us/library/dd389002(VS.85).aspx

The WPD Automation Object Model: http://msdn.microsoft.com/en-us/library/dd389295(VS.85).aspx

Driver Development:

The WPD Services Sample Driver (available from the Windows Driver Kit): http://msdn.microsoft.com/en-us/library/dd573849.aspx

This posting is provided "AS IS" with no warranties and confers no rights.

Introducing WPD Automation

$
0
0

In Windows 7, we introduced WPD Automation, which is a layer above the WPD API that exposes access to common WPD functionality through IDispatch.

This post introduces this new component, and describes what you can and cannot do with WPD Automation, and how you, the developer, can access it from ActiveX and Device Stage™.

What WPD Automation Is

A simple way to access WPD from IDispatch/JScript

WPD provides a C++/COM API that allows you to access a portable device in a protocol-independent manner. Each interface represents a logical grouping of related functionality (e.g. IPortableDeviceProperties contains methods for retrieving and setting object properties). A WPD application would typically acquire and use multiple WPD interfaces to accomplish most tasks. For example, the application would use an IPortableDeviceProperties to get properties of any object on the device, and IPortableDeviceResources to read data from that same object, and so on.

WPD Automation seeks to simplify the programming model of WPD by wrapping the device and its contents as IDispatch objects which are then accessible from JScript. The complexity of WPD is hidden by employing an object-based model: each object is encapsulated by an IDispatch object; properties of that object are mapped to IDispatch properties; tasks on each object are available as IDispatch methods. More details on the WPD Automation Object Model are available on MSDN.

Let’s contrast the two APIs is with an example. The first code snippet demonstrates how to read some properties for an object on the device using the WPD API in C++:

// Sample code for illustration purposes only, may contain typos.
HRESULT ReadPropertiesFromObject(
         PCWSTR   pszObjectID,
         IPortableDeviceProperties* pProperties)     
{
    // 1. Create an IPortableDeviceKeyCollection to hold the keys we wish to read
    IPortableDeviceKeyCollection* pPropertiesToRead;
    HRESULT hr =  CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pPropertiesToRead));

    if (SUCCEEDED(hr))
    {
        // 2. Populate the IPortableDeviceKeyCollection.
        pPropertiesToRead ->Add(WPD_OBJECT_NAME);
        pPropertiesToRead ->Add(WPD_OBJECT_PERSISTENT_UNIQUE_ID);
        pPropertiesToRead ->Add(WPD_OBJECT_FORMAT);

        // 3. Request the properties from the device.
        hr =  pProperties ->GetValues(pszObjectID,  // The object whose properties we are reading
                                         pPropertiesToRead,   // The properties we want to read
                                         &pObjectProperties); // Result property values for the specified object
         
        // 4. Read the properties we just retrieved.
        if (SUCCEEDED(hr))
        {
            // Read the object name
            LPWSTR pszObjectName = NULL;
            hr = pObjectProperties->GetStringValue(WPD_OBJECT_NAME, &pszObjectName);
            CoTaskMemFree(pszObjectName);
 
            // Read the object format, e.g. WPD_OBEJCT_FORMAT_MP3
            GUID Format;
            hr = pObjectProperties->GetGuidValue(WPD_OBJECT_FORMAT, &Format); 
                 
            // and it goes on . . .
         }

Now, to accomplish the same task in WPD Automation, we call a few lines of JScript on an IDispatch object (wpdObject):

function ReadPropertiesFromObject(wpdObject)
{
    var name = wpdObject.ObjectName;                 
    var puid = wpdObject.ObjectPersistentUniqueID;  
    var format = wpdObject.ObjectFormat; // “MP3”
    alert(“The format of “ + name + “ is “ + format);
}

As you can see, WPD Automation makes it very quick and easy to programmatically access the device.

A simple way to write custom code to access a device from Device Stage™

One requirement for bringing the power of the WPD API to Device Stage is to provide a way for device vendors (IHVs) to create custom WPD applications without writing, packaging, and delivering a complete Windows application to users. Device Stage integrates with an Internet Explorer™ web browser control that allows IHV-created web content to be viewed from the Device Stage UI. Using WPD Automation, IHVs can embed JScript into their web pages that can then access connected devices on the user’s Windows 7 PCs. These WPD Automation-enabled web pages are referenced by a special category of web tasks known as the HostedSiteWithDevice task.

For example, the following "Register your phone" sample task is a web page (hosted on an IHV’s server) that contains JScript client-side code to query the device properties (name, model, serial number, ...) of the user's cellular phone and populate the results in the "Product Information" section.

Sample HostedSiteWithDevice Task

You can find more details about the above example in the "Windows 7 Device Stage Portable Device Class Development Guide" from the Device Experience Development Kit.

What WPD Automation Isn't

Now that we've described what WPD Automation can do, let’s spend some time describing what it can't do.

Not a way to access WPD from Managed Code

In spite of the use of "Automation" in its name, WPD Automation is not a way to access WPD from managed code.  WPD Automation is designed for allowing a web application to programmatically interact with device functionality through script.

This does not mean that all is lost.  An application can still call the WPD API from managed code, using COM Interop.  Details are covered in earlier blog posts (C# and VB.NET).

Not a way to access WPD from non-JScript scripting languages

WPD Automation can be called from either JScript or Javascript. Other scripting languages (such as VBScript) are not supported.

Not a way to write a generic WPD application

One big difference between the WPD API and WPD Automation is the absence of support in WPD Automation to programmatically discover device capabilities (i.e. the supported properties, supported formats, supported commands, etc.). This was intentional for two reasons: 1) to optimize and simplify the programming model of WPD Automation; and 2) the target developer audience - IHVs writing custom applications only for their devices – know their device capabilities best, and can customize their scripts accordingly.

Not a way to access all functionality of the WPD API

WPD Automation supports accessing the storage and device service functionality on a portable device, including property management, object enumeration, object transfer, and invoking device service methods. WPD Automation does not support querying of capabilities (see above), bulk properties, updating secondary resources, and accessing functional objects other than services or storages (such as the rendering information or image capture functional objects).

Accessing WPD Automation

There are two ways to access WPD Automation: 1) from Device Stage and 2) from an ActiveX control. Both methods retrieve the Device Object, the entry point object for WPD Automation.

Using WPD Automation from Device Stage™

Device Stage integrates with WPD Automation by providing access to the Device Object through the window.external property.

var deviceObject = window.external;

// Once the deviceObject is retrieved, we can proceed with using WPD Automation
var manufacturer = deviceObject.DeviceManufacturer;
var storages = deviceObject.Storages;

It is very important to note that the Device Stage secure scripting host imposes additional requirements on the definition of a HostedSiteWithDeviceTask. In particular, the webpage must be authenticated with an SSL certificate trusted by the client machine for the allowedDomain attribute specified in the task; otherwise accessing any WPD Automation properties and methods from script will result in "access denied" errors.

These requirements are covered in the "Windows 7 Device Stage Reference Guide" of the Device Experience Development Kit.

Using WPD Automation from an ActiveX™ Control

WPD Automation can also be instantiated outside of Device Stage by writing and registering an ActiveX object that contains a utility method that can be invoked in script return the Device Object. For more details on writing ActiveX objects, click here.

In the utility method:

  1. Call the WPD APIs to retrieve the device's Plug-and-play (PnP) identifier.
  2. CoCreate CLSID_PortableDeviceDispatchFactory and call the GetDeviceDispatch method, providing the PnP identifier, and retrieving an IDispatch object representing the Device Object.
  3. Return the Device Object in the method results.
// Sample code for illustration purposes only, may contain typos.
// Returns the IDispatch object so that the device can be accessed from WPD Automation
IFACEMETHODIMP CMyDeviceFactory::GetMyDeviceObject(IDispatch** ppDevice)
{
    IPortableDeviceManager *pDeviceManager = NULL;

    HRESULT hr = CoCreateInstance(CLSID_PortableDeviceManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceManager);
    if (SUCCEEDED(hr))
    {
       // To simplify this example, we are only looking for the first device encountered
       // (A real-life example may, for example, search all connected devices to find a match using device properties.)
       PWSTR pszPnPID = NULL;
       DWORD cPnPIDs = 1;
       hr = pDeviceManager->GetDevices(&pszPnPID, &cPnPIDs);

       if (SUCCEEDED(hr) &&  cPnPIDs == 1)
       {
           // CoCreate CLSID_PortableDeviceDispatchFactory
           IPortableDeviceDispatchFactory *pDispatchFactory = NULL;

           hr = CoCreateInstance(CLSID_PortableDeviceDispatchFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDispatchFactory);
           if(SUCCEEDED(hr))
           {
             // Pass the PnP identifier as a parameter to IPortableDeviceDispatchFactory::GetDeviceDispatch
             // to retrieve the Device Object’s as an IDispatch object.
             hr = pDispatchFactory->GetDeviceDispatch(pszPnPID, ppDevice); 
           }
       . . .

After installing and registering the ActiveX Control, you can now create a new ActiveX object in JScript and call the utility method to get to the Device Object.

// Initialize our custom ActiveX control
var deviceFactory = new ActiveXObject("MyTestControl.MyDeviceFactory");
// Get the WPD Automation Device Object from our custom ActiveX control by calling GetMyDeviceObject.
var deviceObject = deviceFactory.GetMyDeviceObject();

// Once the deviceObject is retrieved, we can proceed with using WPD Automation
var manufacturer = deviceObject.DeviceManufacturer;
var storages = deviceObject.Storages;

References

The Device Experience Development Kit: http://www.microsoft.com/whdc/device/DeviceExperience/Dev-Kit.mspx

The WPD Automation Object Model: http://msdn.microsoft.com/en-us/library/dd389295(VS.85).aspx

Using IPortableDeviceDispatchFactory to instantiate WPD Automation: http://msdn.microsoft.com/en-us/library/dd389215(VS.85).aspx

 

This posting is provided "AS IS" with no warranties and confers no rights.


MTP Over Various Transports

$
0
0

Introduction

The Media Transfer Protocol (MTP) was originally created as an extension to the Picture Transfer Protocol but today it also supports media transfer, Device Services, command & control, and many other scenarios. MTP supports three transports: USB, IP, and Bluetooth.

One of most common drivers that WPD applications will interact with is the Media Transfer Protocol (MTP) driver.  MTP was introduced in Windows XP with an inbox user mode driver allowing for effortless device installation and connectivity.  MTP is standardized through the USB Implementer’s Forum (www.usb.org) and also features Windows-specific extensions.

We introduced a MTP driver for USB in Windows XP.  In Windows Vista, we added support for MTP over TCP/IP for devices that use WiFi and LAN connections.  In Windows 7, we continue the parade of MTP support with the introduction of Bluetooth.

3 transports, one protocol

MTP includes a protocol definition and several transport definitions.  As devices and PCs pick and choose which connectivity transports they support, device & application developers can be assured that the MTP protocol and WPD API will be consistent.   Three-transport support gives device manufacturers flexibility to choose wired or wireless, high or low power, faster or slower transfer speed, etc.  The MTP driver is also the first multi-transport driver in Windows, which means that a device manufacturer can feel free to implement all three and Windows will choose the best.  We’ll talk about that more in our next entry Multi-transport Devices in Windows 7.

USB

Our first MTP transport driver is also the fastest and most powerful way to connect an MTP-enabled device.  Windows XP through Windows 7 support USB 1.1 or higher.  The binding of MTP to USB is described in the MTP specification itself (http://www.usb.org/developers/devclass_docs/MTP_1.0.zip)  and in the Still Imaging Capture Device Definition specification (http://www.usb.org/developers/devclass_docs/usb_still_img10.zip).  Windows XP uses the Microsoft OS Descriptor to identify MTP devices; Windows Vista and Windows 7 can identify MTP devices by either the OS Descriptor or the USB Still Image Class Code (Class 6, Subclass 1, Protocol 1).  Much of this is detailed in the Windows Portable Device Enabling Kit.

In Windows 7, we also enabled the USB Selective Suspend for the MTP driver, although this option is not enabled by default to preserve maximum ecosystem compatibility.

USB is great for fast file transfer, media synchronization, and charging the device while you’re using it.

IP

In Windows Vista, we implemented MTP over TCP/IP to enable WiFi and networked devices to interact with WPD applications.  We used the PTP/IP binding (http://www.cipa.jp/ptp-ip/index_e.html), then used UPnP for pairing and discovery.  When devices report their presence on a network via UPnP after the initial pairing, Windows will automatically connect and make the device available to the user.

MTP/IP is ideal for devices with WiFi that can be connected and synchronized anywhere in the home.

Bluetooth

In Windows 7, we introduced Bluetooth as the third transport for MTP. We implemented MTP over the L2CAP protocol for fast, efficient, and secure connections. The specification is available in the Windows Portable Device Enabling Kit. PC users can switch the connection on and off through the Devices & Printers folder, or devices themselves can initiate and terminate connections to the PC.

We also implemented an "auto connect" feature in which Windows attempts to connect to your device every few minutes to maintain a low power connection for automatic synchronization and status updates.  When you are holding the device near your PC, you’re connected; when you walk away, the connection breaks and waits silently to reconnect when you return and your device gets back into Bluetooth range.

Applications can use the IPortableDeviceConnect interface to issue connect/disconnect requests to an MTP/Bluetooth device.  When a connection request is made, the MTP/Bluetooth link is established, the WPD MTP driver stack will load, and the device becomes visible to WPD applications to connect to using the WPD API.

Windows requires Bluetooth 1.2 or higher with the Microsoft Bluetooth stack installed.  For maximum security, Windows also requires that the device is paired securely with a pin code.

MTP/BT is great for low power, low speed scenarios like synchronizing PIM data or a quick file transfer.

Operating System Support

The MTP driver is present in Windows XP with Windows Media Player 10 or higher installed, Windows Vista, and Windows 7.

MTP Driver Support by Operating System

Windows Version

USB 1.1 or higher

IP

Bluetooth 1.2 or higher

Windows XP

Yes, with Windows Media Player 10 or higher

No

No

Windows Vista

Yes

Yes

No

Windows Vista + Platform Update for Window Vista

Yes

Yes

Yes

Windows 7

Yes

Yes

Yes

 

 

This posting is provided "AS IS" with no warranties and confers no rights.

Multi-Transport Devices in Windows 7

$
0
0

In Windows Vista, the WPD team shipped an MTP driver with support for two transports: USB and TCP/IP (typically WiFi). This expanded support also introduced an interesting problem: to Windows, the same device connected twice looks like two different devices. This is because device identity in Windows is based on information derived from the underlying transport (or bus). Why is this a problem? Consider the case of an MTP camera. Windows has a photo import application that remembers which photos have already been imported. If the user leaves photos on the camera storage after import, the next import will skip those and only pick up the new ones. However, the photo import application stores this information about imported photos based on the device identity. If the device identity changes when it connects to the same PC using a different transport, those old photos will be imported again because the OS presents this as a different device to the application. Similar problems can occur with music players or mobile phones, where two different synchronization partnerships would be created for the same device.

One way to address this would be to have the device provide its own identity that could be retrieved and examined by each application. This approach can work, but it requires that applications that rely on device identity be modified to use this new identity instead. It also wouldn’t account for future scenarios where multiple transports might be active at the same time.

There have been proprietary solutions, where a custom WPD driver was written to communicate with the device over any of its transports via a multi-transport-aware service. But to make this work, the hardware vendor was required to install and run a Windows service and a kernel mode bus enumerator that communicate with each other to load and unload drivers based on device presence.

The Composite Driver Solution

With Windows 7, there is now built-in support for devices with multiple transports. This support is currently limited to WPD devices, but the groundwork has been laid for other device classes in future OS releases. Now, when a multi-transport device is connected with a Windows 7 PC, it has a single identity. This is even true when multiple transports are simultaneously active, which with the added support for MTP over Bluetooth, will be a common occurrence, such as when a device already connected over Bluetooth is plugged into a USB port for charging.

The Windows 7 solution is a combination of the approaches mentioned above. The following diagram shows the general relationship between the components involved:

 

Multi-Transport Diagram

 

Each transport will continue to have a driver loaded that knows how to talk to the device over that transport. But this “transport” driver won’t be seen by applications. Instead, the transport driver will inform a system service (the Composite Bus Enumerator) that a transport is available. The Composite Bus Enumerator will keep track of the transports announced for any one device, and will cause a single Composite Driver to be loaded for that device. Only the Composite Driver will be seen by applications, and it will be the single point of contact with the device. This will be true whether the device has one transport active, or many, either sequentially, or simultaneously. When the last active transport is disconnected, the Composite Bus Enumerator will remove the Composite Driver.

Composite Driver

The Composite Driver has three primary functions. One, it chooses the best underlying transport when an application opens a handle to the device (typically via IPortableDevice::Open). Two, it acts as a pass-through for WPD requests and responses between the application and the transport drivers. And three, it acts as a proxy for registering and unregistering PnP interfaces on behalf of the underlying transport drivers. This latter functionality is how the transport drivers remain hidden from applications: since no PnP interfaces are registered by the transport driver, there is no (easy) way for an application to obtain a symbolic link directly to the transport. This latter functionality was also the most complex aspect of the implementation, where multiple transport drivers interacting with the Composite Driver to dynamically register and unregister interfaces must be made to look like a single underlying device.

How does the Composite Driver determine the “best” transport when multiple transports are simultaneously active? In Windows 7, it’s a simple comparison of reported theoretical maximum bandwidths reported by the transport driver to the Composite Driver: USB is faster than IP is faster than Bluetooth. While this is generally true, it is also theoretically possible for USB and IP to actually be slower than Bluetooth depending on conditions at the time. All are asynchronous protocols that rely on some form of bandwidth sharing.

Transport Drivers

What’s the difference between a transport driver and a regular driver? Not much. A transport driver has the added responsibility of communicating transport status with the Composite Bus Enumerator and the Composite Driver, but it is otherwise just a normal WPD driver. For instance, the Windows 7 MTP driver fulfills either duty depending on the capability of the underlying device. Much of the complexity is encapsulated in the WPD Class Extension (a helper object that manages the communication with the Composite Bus Enumerator and the Composite Driver).

Multi-Transport Capable Devices

What makes a device a multi-transport device? Simple: it just supplies a device property known as the Functional Unique ID (FUID). This is a 128-bit GUID (Globally Unique ID). For MTP devices, this property is defined by the new MTP Device Services Extension (see the MTP Device Services Extension Specification). This does not mean that the device must support services. It is perfectly okay for a device to indicate support for this extension, but only implement the parts that it needs (in this case, the “Functional ID” 0xD301 device property code).

The FUID must be unique across all devices of the same manufacturer, model and version. If two devices of the same model also had the same FUID, they would be treated as different transports of the same device. To avoid this, and to further simplify things for the device manufacturer, if the MTP driver finds an empty value for the FUID property on the device, it will automatically provision the device with a FUID generated at runtime. The only responsibility of the device manufacturer is to ensure that the FUID value is persisted across connections and available over all transports.

Writing your own Transport Driver

To help simplify the explanation so far, I’ve been describing how this works for an MTP device, where the system-provided MTP Class driver does most of the bookkeeping. However, this is supported for 3rd-party WPD drivers as well. Because most of the complexity is hidden from the driver by the WPD Class Extension, the driver just has to make a few simple adjustments to become multi-transport capable. This is covered in more detail in the documentation for the WPD Multi-Transport Sample driver, but the key changes are as follows:

  1. In single-transport WPD drivers, the default I/O queue is sequential (a UMDF term for a queue that delivers one request at a time). The default I/O queue for a multi-transport driver must be parallel (a UMDF term for asynchronous delivery of I/O requests). WPD requests received in this queue shall be forwarded to the sequential WPD queue. All other requests are to be forwarded to a queue obtained from the WPD Class Extension.
  2. On driver initialization (in the OnPrepareHardware callback), the driver must provision the device with a FUID (if necessary), and it must use the WPD Class Extension to inform the Composite Bus Enumerator of the Manufacturer, Model, Version and FUID values for the device.

Application Development Considerations

While applications are now largely isolated from the complexities of multi-transport devices, there are some things to be aware of, and things an application can do, to provide a better experience. When one transport goes away and another arrives for the same device, the Composite Driver may remain loaded during the transition, but it will not automatically “fail over” to the new transport. There are several reasons why this was not feasible, with limitations of the current MTP protocol being chief among them. The handle associated with the departed transport will no longer function (attempts to use it will result in an ERROR_DEVICE_REMOVED response). If other transports for the device are active, the Composite Driver will re-advertise the PnP interfaces available for the new best transport. But this does not lend itself to a seamless transition if the application treats it as a device departure followed by a device arrival.

The application can mitigate transport departures in a couple ways:

  1. The application can close and reopen its handle at some scenario-specific interval (e.g., between object transfers when synchronizing with the device, or between user interactions with the device). This happens to be the way both Windows Media Player and the Windows Shell already behaved. This approach has the added benefit of guaranteeing that the fastest underlying transport will be used when multiple transports are simultaneously active.
  2. The application can recognize that an ERROR_DEVICE_REMOVED failure might be transient and that it should try reopening the handle once before giving up on the device. Because the device has the same identity regardless of transport, the same symbolic link is used (and reused) across all transports.

 

This posting is provided "AS IS" with no warranties and confers no rights.

Windows 7 Portable Device Development Kits

$
0
0

There are several different resources made available to portable device manufacturers to assist in device firmware and driver development, and to application developers wishing to create applications that interact with these portable devices. These resources are provided and maintained by members of the Windows Portable Devices team.

This post provides a description for the following development kits:

  •  Windows 7 Portable Device Enabling Kit for MTP
  • Windows Portable Devices SDK
  • Windows Portable Devices Driver Kit
  • Windows Media Device Manager SDK
  • Microsoft Device Experience Development Kit
  • Windows Logo Kit

Windows 7 Portable Device Enabling Kit for MTP

This development kit is designed to assist portable device manufacturers in the development of Media Transfer Protocol (MTP) device firmware that is compatible with the various MTP device transports that Windows supports, and with Device Services. There are two packages provided for download:

Windows 7 Portable Device Enabling Kit for MTP - Tools
The tools kit includes design documentation and implementation guidelines, a Windows-based reference design, development tools, and the updated MTP Device Simulator.

Windows 7 Portable Device Enabling Kit for MTP - Reference Code
The reference code kit includes the core responder reference source code that is being made available under a separate Berkeley Software Distribution (BSD)–style license.

To download both packages of the Windows 7 Portable Device Enabling Kit for MTP, go to: http://www.microsoft.com/whdc/device/wpd/MTP-DEK_Win7.mspx

You are not required to license the reference code in order to use the tools and resources provided in the tools kit. This kit is free to download and use.

Windows Portable Devices SDK

The WPD SDK includes the WPD Application Programming Interface (API) that is supported in Windows 7, Windows Vista, and Windows XP operating Systems. Developers will use this API to write Windows applications in C++.  Being a COM API, the WPD API is also accessible from Managed code, so C# .Net, and Visual Basic .Net applications can use COM Interoperability to invoke the same functionality that the C++ API provides. WPD applications can: explore a device, send and receive content, and even control the device, for example, take a picture or send a text message.

The WPD SDK includes two sample applications for C++/COM developers. The first, WpdApiSample, is a command-line desktop application that demonstrates how you would: enumerate connected devices, explore those devices, query device-objects for properties and attributes, send and retrieve data, and so on. The second sample, WpdServicesApiSample, is a command-line desktop application that demonstrates how you would explore a Contacts service on a device that is attached to your computer.

The WPD SDK also includes an Automation Object Model that is supported in Windows 7. Developers can use the Automation model to write web applications that accomplish tasks similar to those accomplished with the API. We provided an introduction to WPD Automation in this recent post.

The WPD SDK is included in the Windows SDK which can be downloaded from http://www.microsoft.com/downloads/details.aspx?FamilyID=c17ba869-9671-4330-a63e-1fd44e0e2505&displaylang=en

The documentation for the WPD SDK (both the API and Automation Object Model) is found on MSDN at: http://msdn.microsoft.com/en-us/library/dd388998(VS.85).aspx

Windows Portable Devices Driver Kit

The WPD Driver Kit includes a Device Driver Interface (DDI) that is supported in Windows 7, Windows Vista, and Windows XP operating Systems. Developers will use this DDI to write drivers for devices that are not already supported by the MTP or Mass Storage class drivers that Microsoft distributes.

The WPD Driver Kit includes five sample device drivers which are described in the following table. In the next post, we covered each sample in more detail.

Sample Driver

Description

WpdHelloWorldDriver

This is the most basic WPD driver that emulates interaction with hardware. Supports four objects: device, storage, folder, and file. This provides the scaffolding or skeleton for developing a WPD driver.

WpdBasicHardwareDriver

Builds upon the WpdHelloWorldDriver and demonstrates interaction with simple hardware devices. Supports nine sensor devices: 2-axis accelerometer, 3-axis accelerometer, temperature/humidity sensor, pressure sensor, distance sensor, passive infrared sensor, compass, vibration sensor, and light sensor. These sensor devices are simple devices that are controlled by a Parallax BS2 microcontroller attached to the PC.

If you plan to develop drivers that integrate sensors with Windows 7, we recommend using the Sensor API and Sensor Driver Model. If you need to develop drivers to integrate sensors with Windows Vista or Windows XP, WPD provides a viable solution.

WpdServiceSampleDriver

Demonstrates how a developer would extend the WpdHelloWorldDriver to support a simulated device with a Contacts device service.(Using this service, WPD applications can discover events, methods, and properties associated with the Contacts stored on the device. This sample emulates interaction with hardware.

WpdMultiTransportDriver

Demonstrates how developers can take advantage of the Windows 7 support for multiple transports. The sample emulates interaction with hardware and demonstrates support for the following transports: IP, Bluetooth, and USB. We covered an overview of Multi-transport support in Windows 7 here.

WpdWudfSampleDriver

A comprehensive sample driver that demonstrates what to enable in the WPD DDI for media transfer scenarios (such as music and photos). This sample supports media synchronization with Windows Media Player, and also emulates interaction with hardware.

The WPD Driver Kit is included in the Windows Driver Kit (WDK). For information about downloading the WDK, see: http://www.microsoft.com/whdc/devtools/wdk/WDKpkg.mspx

The documentation for the WPD Driver Kit is found on MSDN at: http://msdn.microsoft.com/en-us/library/dd419925.aspx

Windows Media Device Manager SDK

The Windows Media Device Manager (WMDM) SDK enables you to build desktop applications and components that can communicate with connected portable devices. WMDM enables your application or component to enumerate, explore, and exchange files with a device, query for metadata, and request play count information. Applications or components built on WMDM have a consistent API for communicating with a wide range of devices including Media Transfer Protocol (MTP), Mass Storage Class (MSC), RAPI, and other devices built on both the latest and previous versions of Windows Media technology.

The Windows Media Device Manager SDK is available at: http://msdn.microsoft.com/en-us/windows/bb190309.aspx.

Microsoft Device Experience Development Kit

The Microsoft Device Experience Development Kit provides detailed information and material that you would need to know in order to develop Device Stage™ experiences for Portable Devices. The development kit includes device development guides, planning worksheets, tutorials, and development samples that you can use when developing a Device Stage experience on Windows 7. For portable devices, Device Stage supports the industry-standard Media Transfer Protocol (MTP) 1.0 with device services that enable a new rich set of features in Windows 7.

The development kit along with other pertinent information to build a complete Windows Device Experience is available at: http://www.microsoft.com/whdc/device/DeviceExperience/default.mspx

The kit is free to download and use.

Windows Logo Kit

The primary use of the Windows Logo Kit (WLK) is for Windows Logo Program device certification. This kit can also be used for product development. We encourage its use during the product development cycle of hardware in order to ensure device compliance with both Windows and industry standards. The kit includes several tests designed to validate device implementation and behavior at multiple levels. There are tests for USB compliance, Media Transfer Protocol (MTP) compliance, and Windows Portable Devices (WPD) platform compliance that also cover device services validation. 

There is a significant amount of information available about the WLK, including user guides and video tutorials. For more information about the WLK see: http://www.microsoft.com/whdc/winlogo/WLK/default.mspx

The kit is free to download and use.

Other Resources

Windows Portable Devices node on Windows Hardware Developer Central

http://www.microsoft.com/whdc/device/wpd/default.mspx

Windows Portable Devices on Microsoft Developer Network

http://msdn.microsoft.com/en-us/library/dd388998(VS.85).aspx

 

This posting is provided "AS IS" with no warranties and confers no rights.

Windows 7 Driver Samples

$
0
0

The Windows Portable Devices Driver Kit in the Windows Driver Kit (WDK) comes bundled with five WPD sample drivers. Two of these drivers, WpdHelloWorldDriver and WpdWudfSampleDriver, were available in previous versions of the WDK. The remaining three drivers, WpdBasicHardwareDriver, WpdServiceSampleDriver, and WpdMultiTransportDriver, are new additions in the Windows 7 WDK. Together, these samples illustrate how to build a WPD driver for the different scenarios that WPD enables, including interaction with basic hardware, media synchronization, device services, and multi-transport.

We had mentioned these samples in a previous post on the Windows 7 Portable Devices Development Kits. The purpose of this post is to spend some time covering each sample in greater detail. Hopefully, this will help driver developers navigate the various WPD samples, and figure out which sample(s) best fit their driver requirements.

The following sections will describe each driver, the WPD commands that each implements, and the operating systems that each supports. In addition, we will provide the links to instructions for building and installing.

WpdHelloWorldDriver

Of all the sample drivers, the WpdHelloWorldDriver is the most simple. This driver supports and emulates four objects in software: a device object, a storage object, a folder object, and a text file object. WpdHelloWorldDriver was designed to provide the most basic scaffolding or skeleton required for developing a WPD driver; similar to the canonical "Hello World" program used for introducing a framework or programming language. If you are new to WPD driver development and need a kick start, we recommend starting with this sample.

The WpdHelloWorldDriver sample supports 22 commands. These commands cover the basic functionality that enables a WPD device to be browsed in Windows Explorer as a read-only device. The commands fall into four categories: object enumeration, object properties, object resources, and device capabilities. Like all WPD drivers, when this driver is installed and a WPD application calls a method to enumerate objects, set a property, open a resource, and so on, the driver will invoke a corresponding command handler for one of these supported commands.

This driver is supported on Windows XP, Windows Vista (x86, amd64), and Windows 7 (x86, amd64).

For instructions on how to build and install this driver, see: http://msdn.microsoft.com/en-us/library/dd573842.aspx

WpdBasicHardwareDriver

The WpdBasicHardwareDriver sample is new for Windows 7 and builds upon the WpdHelloWorldDriver. This sample stands apart from the other WPD samples in that it accesses real device hardware instead of emulating a software-only device. We provided this sample to demonstrate how a basic WPD driver can be built to interact with simple devices.

For our demonstration device, we targetted a “hobbyist” microcontroller that reads data from a variety of sensors. The sensors include: a 2-axis accelerometer, a 3-axis accelerometer, a temperature/humidity sensor, a pressure sensor, a distance sensor, a passive infrared sensor, a compass, a vibration sensor, and a light sensor. These sensor devices are controlled by a Parallax BS2 microcontroller attached to the PC via an RS232 port. In addition to the driver source code, we also provided some sample firmware code that retrieves the sensor data and sends them over the serial port to the PC.

The WpdBasicHardwareDriver sample supports an even smaller set of commands than the WpdHelloWorldDriver. In the interests of only providing the code that is necessary to demontrate the concepts, we removed some commands for object resources because the WpdBasicHardwareDriver does not need to support reading of files from the device. The remaining commands fall under these three categories: object enumeration, object properties, and device capabilities. To see this driver in action, you can use WpdMon to view the events corresponding to each sensor reading.

This sample also showcases how WPP Software Tracing can be used for error checking macros. A while ago, we had a post that describes how you can migrate your existing WPD driver from OutputDebugString to WPP Tracing. The WpdBasicHardwareDriver is now bundled with the macros. 

This driver is supported on Windows XP, Windows Vista (x86, amd64), and Windows 7 (x86, amd64). If you plan to develop drivers that integrate sensors with Windows 7, we recommend using the Sensor API and Sensor Driver Model. If you need to develop drivers to integrate sensors with Windows Vista or Windows XP, WPD provides a viable solution. 

For instructions on how to build and install this driver, see: http://msdn.microsoft.com/en-us/library/dd573829.aspx

WpdWudfSampleDriver

The WpdWudfSampleDriver is a comprehensive driver sample that demonstrates using the WPD DDI for media transfer scenarios.  In addition to file browsing and transfer via Windows Explorer, this sample contains additional functionality that supports media synchronization with Windows Media Device Manager (WMDM) applications (e.g. Windows Media Player), and photo acquisition with Windows Image Acquisition (WIA) applications (e.g. Paint). All interactions with hardware are emulated in software (meaning that this driver talks to a "fake" device).

To enable the media transfer scenarios, the WpdWudfSampleDriver sample had to support a large set of commands, including the commands supported by the WpdHelloWorldDriver. These commands can be divided into eight categories: object enumeration, object management, object properties, bulk properties, object resources, capabilities, and storage.

In this version, we fixed a compatibility issue in the Vista WDK version of the sample driver code that prevents it from being properly enumerated in Windows Media Player.

This driver is supported on Windows XP, Windows Vista (x86, amd64), and Windows 7 (x86, amd64).

For instructions on how to build and install this driver, see: http://msdn.microsoft.com/en-us/library/dd573843.aspx

WpdMultiTransportDriver

The WpdMultiTransportDriver builds upon the WpdHelloWorldDriver and demonstrates how driver developers can take advantage of the Windows 7 support for Multiple Transports. For an overview on what multi transport is all about, check out our post two weeks ago about Multi-Transport support in Windows 7.

This driver emulates a transport driver and illustrates how to set the relevant multi-transport settings and generate the Functional Unique Identifier (FUID) during device arrival, in order to perform handshaking with the WPD Composite Driver. In addition, this sample supports multiple Windows Driver Framework I/O queues, where all the other WPD samples only support a single I/O queue. 

Other than the multi-transport support, the WpdMultiTransportDriver sample is virtually identical as the WpdHelloWorldDriver, and supports the same set of WPD commands. A quick way to zero-in on the code modifications needed to support Multi-Transport is to use your favorite diff program to compare the WpdHelloWorldDriver and WpdMultiTransportDriver source folders.

Because multi-transport support is a new feature in Windows 7, this sample driver is only supported on Windows 7 (x86, amd64).

For instructions on how to build and install this driver, see: http://msdn.microsoft.com/en-us/library/dd573843.aspx

WpdServiceSampleDriver

The WpdServiceSampleDriver demonstrates how a driver can support Windows 7 Device Services.  We covered an introduction to Device Services in another recent post. This driver emulates a basic Contacts device service that uses the Full Enumeration Sync device service. To support Device Services, this driver implements device services-specific WPD command sets: WPD_CATEGORY_SERVICE_COMMON, WPD_CATEGORY_SERVICE_CAPABILITIES, and WPD_CATEGORY_SERVICE_METHODS. These commands would allow an application to determine the capabilities of a service and invoke methods supported by the service. This driver also uses the new functionality in the WPD Class Extension (IPortableDeviceClassExtension) to register and unregister the Contacts device service interface. Last but not the least, to support object and property management, this sample also supports commands for object enumeration, object properties, bulk properties, object resources, and device capabilities.

The WpdServiceSampleDriver does not emulate a Media Transfer Protocol (MTP) device. Like most of the other WPD samples, it simulates a software-only device that is not tied to a specific protocol or transport. If you are looking to build firmware for an MTP device that supports device services, you should start with the Device Enabling Kit.

Device Services is a new feature we introduced in Windows 7, therefore this sample is only supported on Windows 7 (x86, amd64).

For instructions on how to build and install this driver, see: http://msdn.microsoft.com/en-us/library/dd573846.aspx


This posting is provided "AS IS" with no warranties and confers no rights. 

Windows 7 Application Samples

$
0
0

The Windows Portable Devices (WPD) SDK includes two sample command-line applications written in C++. The first application, WpdApiSample, lets the user accomplish tasks like: enumerating connected devices, exploring objects on these devices, and enumerating object properties. The second application, WpdServiceApiSample, is designed to explore a Contacts Device Service. The user can enumerate connected device services, retrieve properties and formats for a given service, and invoke methods on that service. The WPD SDK is included in the Windows SDK which can be downloaded from http://www.microsoft.com/downloads/details.aspx?FamilyID=c17ba869-9671-4330-a63e-1fd44e0e2505&displaylang=en

The following sections describe each sample application, the modules found in that application, and the operating systems that it will run on.

WpdApiSample

Of the two sample applications, the WpdApiSample, is the most generalized. It demonstrates how a developer would use the WPD API to accomplish tasks common to most applications. For example, this application demonstrates how a developer would:

· Enumerate the devices connected to a computer.

· Establish a connection to a connected device.

· Enumerate device content.

· Retrieve the properties for a given object on a device.

· Set the properties for a given object on a device.

· Retrieve general device information (for example, a description of the operations that a device can perform).

· Operate on device content (for example, move a file from one folder on the device to another).

The WpdApiSample application includes the following files:

File

Description

ContentEnumeration.cpp

Contains functions that enumerate all the objects on a device.

ContentProperties.cpp

Contains functions that read and write object properties and make bulk property set/get requests.

ContentTransfer.cpp

Contains functions that transfer content to or from the device, read object type requirements, and create a folder on the device.

DeviceCapabilities.cpp

Contains functions to list the functional object types on the device, list the content types supported by each functional object type, and display rendering object profiles.

DeviceEnumeration.cpp

Lists the friendly names, manufacturers, and descriptions of all connected devices.

DeviceEvents.cpp

Contains functions that log device events and their parameters whenever events are fired.

Stdafx.cpp

Includes the standard files.

WpdApiSample.cpp

Displays a list of available devices and tasks available to the user.

This application is supported on Windows XP, Windows Vista, and Windows 7.

Even if you have no WPD devices connected to your computer, you can familiarize yourself with the WPD API and this application by installing the WpdHelloWorldDriver sample from the Windows Driver Kit (WDK), which emulates a simple WPD device. For information about downloading the WDK, see http://www.microsoft.com/whdc/resources/downloads.mspx

WpdServiceApiSample

The WpdServiceApiSample sample demonstrates how a developer would use the WPD API to explore a Contacts Device Service on a device that’s attached to a computer. For example, this application demonstrates how a developer would:

· Enumerate the services on a devce.

· Open a service.

· Enumerate service content.

· Retrieve the events and methods supported by a service.

· Access the service object properties.

· Invoke service methods (both synchronously and asynchronously).

The WpdServiceApiSample application includes the following files:

File

Description

ContentEnumeration.cpp

Contains methods that enumerate the content on a given Contacts service.

ContentProperties.cpp

Contains methods that read and write properties on a given Contacts service.

ServiceCapabilities.cpp

Contains the methods that retrieve the supported formats, events, and abstract services that are supported by a given Contacts service.

ServiceEnumeration.cpp

Contains the helper functions that retrieve device information such as the device-friendly name or the supported Contacts services.

ServiceMethods.cpp

Contains the methods that retrieve and invoke methods supported by a given Contacts service.

Stdafx.cpp

Includes the standard files.

WpdServiceApiSample.cpp

Displays a list of available device services and tasks available to the user

Because Device Services is new for Windows 7, this application is only supported in the Windows 7 operating system.

Even if you have no WPD devices connected to your computer, you can familiarize yourself with the WPD API and this application by installing the WpdServiceSampleDriver that’s included as a sample in the Windows Driver Kit (WDK). This sample driver emulates a simple WPD device service.

Other Samples

In addition to the two samples that ship in the SDK, the WPD team created three other samples that demonstrate using WPD to display the current office temperature in the Windows Sidebar. One of these samples is written using unmanaged-code and C++; another is written using the COM interop and Visual Basic .Net; and, the third is written using the COM interop and Visual C# .Net.

All three of these samples rely on a simple temperature sensor device, device-firmware, and a sample WPD driver. You can download the circuit description, device-firmware, and sample driver from here: http://www.microsoft.com/whdc/device/media/WPD_drv.mspx

Once you build the device, download the firmware, and install the driver, you can begin exploring the WPD API samples. You’ll find a description of the unmanaged application at: http://blogs.msdn.com/wpdblog/archive/2007/12/19/creating-a-temperature-sensor-gadget-for-windows-sidebar-with-c.aspx.

You’ll find a description of the COM interop and Visual Basic .Net application at: http://wpdtempsensor.codeplex.com/.

And, you’ll find a description of the COM interop and the C# application at: http://blogs.msdn.com/wpdblog/archive/2007/11/26/creating-a-temperature-sensor-gadget-for-windows-sidebar.aspx.

This posting is provided "AS IS" with no warranties and confers no rights.

Viewing all 25 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>