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 );
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).