Blog Hero Image

Using WMDRM on your Application: Part 2

Nov 09, 2011

Getting data form your device using WMDRM API

To initialize a WMDRM interface, the application calls CoCreateInstance to obtain an instance of IWMDRMDeviceApp or IWMDRMDeviceApp2 for the extended DRM queries, as shown in the following example:

hr = CoCreateInstance(CLSID_WMDRMDeviceApp,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_IWMDMDeviceApp2,
    (void**)ppWMDRMDeviceApp2);    
 
if (hr != S_OK)
{
     printf (“! Failed to CoCreate IWMDRMDeviceApp2\n”);
}

The next step is to obtain an IWMDMDevice instance to use as an input parameter for the IWMDRMDeviceApp and IWMDRMDeviceApp2 methods.

The process of deriving the WPD device instance is covered in detail by the WPD Sample Application in the Windows SDK and MSDN® documentation and will not be repeated here.

Each WPD device is identified by a unique Plug and Play Device Identifier (PnPDeviceID) string. Similarly, each WMDM device is also identified by a unique string known as the WMDM canonical name. If the two strings can be correlated for a given WPD MTP device, a matching IWMDMDevice instance can be retrieved from the list of enumerated WMDM devices.

To associate the PnPDeviceID string of a WPD device with its WMDM canonical name we have to follow the next steps:

1. Replace the device interface globally unique identifier (GUID) with the WMDM interface GUID for the current WPD PnPDeviceID.

//
// Step 1: Converting a WPD PnP Device ID to a WMDM PnP Device ID
// (Error checking is omitted for brevity)
//
 
hr = pIPortableDevice->GetPnPDeviceID(&pszPnPDeviceID);
 
CAtlStringW strWMDMPnPID = pszPnPDeviceID;
 
// Truncate the WPD interface GUID
int pos = strCurrentWMDMPnPID.ReverseFind(L’{‘);
strWMDMPnPID = strWMDMPnPID.Left(pos);
 
// Append the WMDM interface GUID
strWMDMPnPID += L”{f33fdc04-d1ac-4e8e-9a30-19bbd4b108ae}”;

2. Enumerate devices by using the WMDM API and get the canonical name for each. Convert the canonical name to a WMDM PnPDeviceID.

//
// Step 2: Enumerating the WMDM Canonical Names
// (Error checking is omitted for brevity)
//
 
hr = pWMDeviceManager2->EnumDevices2(&pWMDMEnumDevice);
if (hr == S_OK)
{
  IWMDMDevice* pWMDMDevice = NULL;
  ULONG        ulFetched   = 0;
 
  // Traverse the list of enumerated WMDM devices
 
  while(pWMDMEnumDevice->Next(1, &pWMDMDevice, &ulFetched) == S_OK)
  {
     WCHAR          wszCanonicalName[MAX_PATH] = {0};
     IWMDMDevice2*  pWMDMDevice2 = NULL;
 
     hr = pWMDMDevice->QueryInterface(IID_PPV_ARGS(&pWMDMDevice2));
 
     // Get the canonical name for the current WMDM device
 
     hr = pWMDMDevice2->GetCanonicalName(wszCanonicalName,
                   ARRAYSIZE(wszCanonicalName);
 
     CAtlStringW   strTestPnPID = wszCanonicalName;
 
     // Truncate the WMDM virtual device index
 
     int pos = strTestPnPID.ReverseFind(L’$’)
     strTestPnPID = strTestPnPID.Left(pos);
 
     //
     // Do Step 3 here …
     //
  }
}

3. Call IPortableDevice::Open on the WMDM PnPDeviceID obtained from enumerating the WMDM canonical names to get the PnPDeviceID.

Although the PnPDeviceID returned from IPortableDevice::GetPnPDeviceID is the same literal string as the original WMDM PnPDeviceID from Step 1, this test “closes the loop” by ensuring that both WPD and WMDM can access the same WMDM device identifiers.

//
// Step 3: Verify that the WMDM Device ID converted from the
// Canonical Name can be accessed by WPD
// (Error checking is omitted for brevity)
//
 
IPortableDevice* pPortableDeviceTest = NULL;
IPortableDeviceValues* pClientInfo   = NULL;
 
LPWSTR           pszWPDPnPDeviceID   = NULL;
 
//
// CoCreate a new instance of IPortableDevice (not shown) …
// CoCreate and populate client information (not shown) …
//
 
hr = pIPortableDeviceTest->Open(strTestPnPID, pClientInfo);
 
if (hr == S_OK)
{
    hr = pIPortableDeviceTest->GetPnPDeviceID(&pszWPDPnPDeviceID);
 
     // Compare the result with our WMDM PnP ID
 
     if (strWMDMPnPID.CompareNoCase(pszWPDPnPDeviceID) == 0)
     {
         // Found a matching IWMDMDevice instance
     }
}

4. Now that you have found the IWMDMDevice pointer that has the correct canonical name, you can use that IWMDMDevice as the input parameter to call IWMDRMDeviceApp or IWMDRMDeviceApp2 methods on that device.

For example, the following code snippet queries if the current device supports WMDRM:

hr = pWMDRMDeviceApp2->QueryDeviceStatus2(pWMDMDevice,
                      WMDRM_QUERY_DEVICE_ISWMDRM,&dwStatus);
 
if (hr == S_OK)
{
   bool bSupportsWMDRM = (dwStatus > 0)?true:false;
}

Latest insights