Where are the WPD property keys in C#?
If you followed the exercise from our first C# post, you must have noticed that the PortableDeviceApi and PortableDeviceTypes typelibs don't expose the WPD property keys such as WPD_OBJECT_ID, WPD_OBJECT_FORMAT, etc. This can be a major blocker since for starters, we need to specify a basic set of client information properties to open a connection to the device.
If you used C++, the WPD property keys definitions could be brought in by linking against PortableDeviceGuids.lib. Linking against a C lib is not an option for a C# application. It's not even possible to generate a typelib for a C lib. The only option is to manually define each of the property keys all over again in your C# application.
The C++ PROPERTYKEY data type maps to the PortableDeviceApiLib._tagpropertykey interop data type. We can look up the PROPERTYKEY declaration in PortableDevice.h and use that to generate an equivalent C# property key object.
Let's take a look at WPD_OBJECT_ID - the C++ definition (from PortableDevice.h) is:
//
// WPD_OBJECT_ID
// [ VT_LPWSTR ] Uniquely identifies object on the Portable Device.
DEFINE_PROPERTYKEY( WPD_OBJECT_ID , 0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C , 2 );
Mapping this to C# would result in something like:
class PortableDevicePKeys
{
static PortableDevicePKeys()
{
WPD_OBJECT_ID.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
WPD_OBJECT_ID.pid = 2;
}
public static PortableDeviceApiLib._tagpropertykey WPD_OBJECT_ID;
}
We can then reference the property as PortableDevicePKeys.WPD_OBJECT_ID whenever required.
Making life easier
Since defining each of these property keys by hand may cause severe RSI, here's a little script that will generate the definitions for you given PortableDevice.h. In addition to generating the property keys, it will also generate the GUID definitions (such as for WPD_FUNCTIONAL_CATEGORY_STORAGE, WPD_OBJECT_FORMAT_WMA, etc.)
//
// Name: genCSinc.js
// Copyright: Microsoft 2006
// Revision: 1.0
//
// This script can be used to generate a C# .cs file that contains
// the equivalent WPD property-keys and GUIDs as defined in
// portabledevice.h
// This script is provided as-is and Microsoft does not assume any
// liability. This script may be redistributed as long as the file
// contains these terms of use unmodified.
//
// Usage:
// Switch to the folder where portabledevice.h is present and then run
// cscript //nologo genCSinc.js
// This will generate portabledeviceconstants.cs. This .cs file can
// then be included in a C# project.
// To use the propertykeys and the GUIDs, add "using PortableDeviceConstants;"
// to the target file.
// Propertykeys can then be referenced as PortableDevicePKeys.desired_prop_name
// e.g. PortableDevicePKeys.WPD_PROPERTY_COMMON_HRESULT
// GUIDs can be referenced as PortableDeviceGuids.desired_GUID_name
// e.g. PortableDeviceGuids.WPD_OBJECT_FORMAT_ALL
//
//
// Use FSO to read/write input/output
//
var fso = new ActiveXObject("Scripting.FileSystemObject");
var f = null;
var fOut = null;
var sIn = "portabledevice.h";
var sOut = "portabledeviceconstants.cs";
//
// Check for input file
//
try
{
f = fso.OpenTextFile(sIn);
}
catch(e)
{
WScript.Echo("Expected portabledevice.h to be in the current folder!");
}
//
// Check for output file
//
try
{
fOut = fso.OpenTextFile(sOut, 2, true);
}
catch(e)
{
WScript.Echo("Cannot open " + sOut + " for writing!");
}
//
// Write out header
//
fOut.Write("\
using System;\r\n\
\r\n\
namespace PortableDeviceConstants\r\n\
{\r\n\
class PortableDevicePKeys\r\n\
{\r\n\
static PortableDevicePKeys()\r\n\
{\r\n\
");
//
// RegEx declarations for PKEYs and GUIDs
//
//e.g. DEFINE_PROPERTYKEY( WPD_CLIENT_MINOR_VERSION , 0x204D9F0C, 0x2292, 0x4080, 0x9F, 0x42, 0x40, 0x66, 0x4E, 0x70, 0xF8, 0x59 , 4 );
var rePKEY = /\s*DEFINE_PROPERTYKEY\(\s*(\w+)\s*,\s*(.+)\s*,\s*(\d+)\s*\);/;
var arrPKEY = new Array();
//e.g. DEFINE_GUID(WPD_EVENT_DEVICE_RESET, 0x7755CF53, 0xC1ED, 0x44F3, 0xB5, 0xA2, 0x45, 0x1E, 0x2C, 0x37, 0x6B, 0x27 );
var reGUID = /\s*DEFINE_GUID\((\w+),\s(.+)\s\);/;
var arrGUID = new Array();
//
// Parse the file
//
while (!f.AtEndOfStream)
{
var l = f.ReadLine();
//
// Check for PKEYs
//
if (l.match(rePKEY))
{
//
// Write out initializations for the PKEYs
//
var sName = l.replace(rePKEY, "$1");
var sGUID = l.replace(rePKEY, "$2");
var sPID = l.replace(rePKEY, "$3");
//WPD_CLIENT_NAME.fmtid = new Guid(0x204D9F0C, 0x2292, 0x4080, 0x9F, 0x42, 0x40, 0x66, 0x4E, 0x70, 0xF8, 0x59);
//WPD_CLIENT_NAME.pid = 2;
fOut.Write(" " + sName + ".fmtid = new Guid( " + sGUID + ");\r\n");
fOut.Write(" " + sName + ".pid = " + sPID + ";\r\n");
fOut.Write("\r\n");
//
// Save the PKEY name for declaration at class level
//
arrPKEY.push(sName);
}
else if (l.match(reGUID))
{
//
// Save the GUIDs since they go into a second class
//
var sName = l.replace(reGUID, "$1");
var sGUID = l.replace(reGUID, "$2");
arrGUID.push("public static Guid " + sName + " = new Guid( " + sGUID + " );");
}
}
//
// Write out declarations for PKEYs
//
fOut.Write(" }\r\n\r\n");
for (var i = 0; i < arrPKEY.length; i++)
{
fOut.Write(" " + "public static PortableDeviceApiLib._tagpropertykey " + arrPKEY[i] + ";\r\n");
}
fOut.Write("\
} // class PortableDevicePKeys\r\n\
");
//
// Write out GUIDs
//
fOut.Write("\r\n\r\n");
fOut.Write("\
class PortableDeviceGuids\r\n\
{\r\n\
");
for (var i = 0; i < arrGUID.length; i++)
{
fOut.Write(" " + arrGUID[i] + "\r\n");
}
//
// Write out footer
//
fOut.Write("\
} // class PortableDeviceGuids\r\n\
} // namespace PortableDeviceConstants\r\n\
");
WScript.Echo("Done: " + sOut + " now contains C# WPD constants");
Copy the script from the text-box above, paste it into Notepad and save it as gencsinc.js. Copy PortableDevice.h to the same folder as gencsinc.js and then run gencsinc.js using "cscript gencsinc.js". This will generate a PortableDeviceConstants.cs file which you may add to your C# project.
Once the generated file is added to your project, add "using PortableDeviceConstants; " to your target C# source. To reference property keys, you can simply use PortableDevicePKeys.propertyname (e.g. PortableDevicePKeys.WPD_OBJECT_ID) and to reference GUIDs, you can simply use PortableDeviceGuids.guidname (e.g. PortableDeviceGuids.WPD_OBJECT_FORMAT_WMA).