WPD: Transfer Content To A Device
April 17, 2012
Mayhap came here sooner than I thought. The previous article on the WPD API dealt with deleting resources. Too destructive perhaps, let’s create some resources this time.
My Kindle’s is hooked up to my PC. Let’s see if I can transfer a book to it.
TransferContentToDevice Method
Get the source code of the fourth article, #69: WPD: Deleting Resources, from the download page. Unzip it and open it up in Visual Studio.
In the Solution Explorer select the Program.cs file and open it. Clear the Main(…) method and add the following code to it.
var devices = new PortableDeviceCollection(); devices.Refresh(); var kindle = devices.First(); kindle.Connect(); kindle.TransferContentToDevice( @"d:\temp\Kindle_Users_Guide.azw", @"g:\documents"); kindle.Disconnect();
First we establish a connection to the Kindle, copy the book to it and finally disconnect from it
The magic happens in the TransferContentToDevice(…) method, which takes two parameters:
- fileName: Full path of the file which you want to copy to the device
- parentObjectId: The ID of the parent folder into which you want to copy the file
Modify the values of these parameters to suit your situation.
Before we can start to implement the TransferContentToDevice(…) method we first need to add another private method to the PortableDevice class type.
private IPortableDeviceValues GetRequiredPropertiesForContentType( string fileName, string parentObjectId) { //... }
Before you can upload a resource to a device you need to collect some information about it. This information is stored in a IPortableDeviceValues collection.
Depending on the resource type you may need to collect more data (or properties) that describe the resource. Keep that it mind, the number of required properties might differ for your situation.
For adding a book to a Kindle you need to know the following information:
- WPD_OBJECT_PARENT_ID: The ID of the folder into which you want to upload the resource
- WPD_OBJECT_SIZE: The size of the object (in bytes)
- WPD_OBJECT_ORIGINAL_FILE_NAME: The original filename of the object
- WPD_OBJECT_NAME: The name of the object as you wish to represent it on the device
Time to implement the method. First you need to create a IPortableDeviceValues collection.
IPortableDeviceValues values =
new PortableDeviceTypesLib.PortableDeviceValues() as IPortableDeviceValues;
Then you can add the WPD_OBJECT_PARENT_ID to the collection:
var WPD_OBJECT_PARENT_ID = new _tagpropertykey(); WPD_OBJECT_PARENT_ID.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C); WPD_OBJECT_PARENT_ID.pid = 3 ; values.SetStringValue(ref WPD_OBJECT_PARENT_ID, parentObjectId);
Adding the rest of the properties is similar.
FileInfo fileInfo = new FileInfo(fileName); var WPD_OBJECT_SIZE = new _tagpropertykey(); WPD_OBJECT_SIZE.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C); WPD_OBJECT_SIZE.pid = 11; values.SetUnsignedLargeIntegerValue(WPD_OBJECT_SIZE, (ulong) fileInfo.Length); var WPD_OBJECT_ORIGINAL_FILE_NAME = new _tagpropertykey(); WPD_OBJECT_ORIGINAL_FILE_NAME.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C); WPD_OBJECT_ORIGINAL_FILE_NAME.pid = 12; values.SetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, Path.GetFileName(fileName)); var WPD_OBJECT_NAME = new _tagpropertykey(); WPD_OBJECT_NAME.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C); WPD_OBJECT_NAME.pid = 4; values.SetStringValue(WPD_OBJECT_NAME, Path.GetFileName(fileName));
Remark: To keep it easy I extracted the filename and path of the book and used that to set both the WPD_OBJECT_ORIGINAL_FILE_NAME and WPD_OBJECT_NAME properties.
Last but not least, don’t forget to return the collection.
return values;
Now that you can gather all the required properties you can start transferring content to the device. Let’s implement the TransferContentToDevice(…) method.
Get an IPortableDeviceContent interface to access resource specific methods.
IPortableDeviceContent content; this._device.Content(out content);
Now using the GetRequiredPropertiesForContentType(…) method retrieve the required properties for the resource you want to upload.
IPortableDeviceValues values =
GetRequiredPropertiesForContentType(fileName, parentObjectId);
Then get an IStream and the optimal buffer size to transfer the resource. This can be done by using the CreateObjectWithPropertiesAndData(…) method of the IPortableDeviceContent instance.
PortableDeviceApiLib.IStream tempStream; uint optimalTransferSizeBytes = 0; content.CreateObjectWithPropertiesAndData( values, out tempStream, ref optimalTransferSizeBytes, null);
Convert the IStream to an System.Runtime.InteropService.ComTypes.IStream.
Top of page
System.Runtime.InteropServices.ComTypes.IStream targetStream =
(System.Runtime.InteropServices.ComTypes.IStream) tempStream;
All that remains is to open a FileStream, read the source file and write it to the target stream. Make sure you close the IStream afterwards (try…finally).
try { using (var sourceStream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { var buffer = new byte[optimalTransferSizeBytes]; int bytesRead; do { bytesRead = sourceStream.Read( buffer, 0, (int)optimalTransferSizeBytes); IntPtr pcbWritten = IntPtr.Zero; targetStream.Write( buffer, (int)optimalTransferSizeBytes, pcbWritten); } while (bytesRead > 0); } targetStream.Commit(0); } finally { Marshal.ReleaseComObject(tempStream); }
And that is all she wrote. As always it’s best to backup your resources before you start experimenting with this code! You can download the source code accompanying this article from the download page. If you have any questions or suggestions please drop me an e-mail or submit a comment.





April 27, 2012 at 10:52u
Many thanks for your WPD posts! Has been very useful for my understanding of how to use it from VB.NET.
May 1, 2012 at 12:37u
Good articles n but the project attached is throwing error at line
targetStream.Write(buffer, (int)optimalTransferSizeBytes, pcbWritten);
Error : “Value does not fall within the expected range.”
Help me out for removing the error.
Pulkit
May 1, 2012 at 16:12u
What are you trying to do? What kind of device do you use and what kind of files are you trying to transfer to it?
May 2, 2012 at 8:38u
I am using nokia-c2 and I want to transfer a xml file from PC to my phone.
I am able to perform transfer using WPDApiSample of windows sdk 7 but it is in C++.
I have change the WPD_OBJECT_CONTENT_TYPE, WPD_OBJECT_FORMAT guid’s correctly but now i am getting COMexception unhandled error “The data area passed to a system call is too small. (Exception from HRESULT: 0x8007007A)”
So do you have any idea what is happening?
PS:Pervious error is due to as WPD_OBJECT_CONTENT_TYPE, WPD_OBJECT_FORMAT is not defined in your function.
May 8, 2012 at 17:45u
I am tried to copy a text file to Acer A100 device and get the same error at the same line:
targetStream.Write(buffer, (int)optimalTransferSizeBytes, pcbWritten);
Error : “Value does not fall within the expected range.”
I did try to use another public folder on the device – the same error.
Do you have any idea what can be wrong?
Thank you,
Semyon
May 17, 2012 at 9:57u
No, not off the top of my head. There are so many WPD compatible devices. A while ago a reader mailed me to inform that he was working on a WPD .NET library:
https://github.com/slowmonkey/WPD-.NET-Wrapper
It might be of help to you. Otherwhise check out the C++ samples on MSDN.
http://msdn.microsoft.com/en-us/library/windows/desktop/dd389001(v=vs.85).aspx
June 27, 2012 at 15:16u
Do you have a sample on how to Create a Folder on a Windows Portable device? I’m using the library to delete content and to transfer content to and from the device – but creating a folder would be a winner.
July 20, 2012 at 15:28u
Did you figure out how to do this? I have the same need to create a folder on a WPD.
July 10, 2012 at 8:41u
hello
excellent work on the wpd projects, you have helped me alot.
i am working on galaxy s2 device ,i am using the WPDTransferringContent project and i combine the method for uploading file to a device, while looping in inside the do while loop
it seems like its working fine but in the end when the ‘bytesRead’ is not equals to the ‘optimalTransferSizeBytes’ i get an exption:
The data area passed to a system call is too small. (Exception from HRESULT: 0x8007007A)
can u help me please?!
ps
i found a beautiful class that enumeration all GUID send me an e-mail back and i will give it to u.
July 16, 2012 at 12:06u
To solve the error: “The data area passed to a system call is too small.” just change the target.Write-call to the following:
if (bytesRead < (int)optimalTransferSizeBytes)
targetStream.Write(buffer, bytesRead, pcbWritten);
else
targetStream.Write(buffer, (int)optimalTransferSizeBytes, pcbWritten);
I had the same problem and this solution worked for me!
Btw great work on WPD Christophe, helped me a lot.
January 17, 2013 at 15:57u
I think that it is enough to do like this:
if (bytesRead > 0)
targetStream.Write(buffer, bytesRead, pcbWritten);
September 10, 2012 at 14:06u
[...] WPD Apis for transferring image files to a connected WPD supported device. I have been following THIS link. My problem is that everytime i try and transfer a file i keep getting the error: Value does [...]
December 30, 2012 at 17:08u
Hi Christophe,
where do you get the fmtid GUID and pid values for the properties from? I have been searching endlessly and have a hard time finding an overview of these values.
Any pointers?
Thanks
May 23, 2013 at 15:45u
Christophe, I can’t tell you how many hours you’ve saved me. Thank you!
And, to give back for others, I’ve modified this code to work for a Galaxy Tab 2 10.1. There were two “aha” moments involved:
1. The parentObjectId parameter in the TransferContentToDevice() method is very device-specific. For the Galaxy Tab 2, the root folder is named “Tablet” and it’s ID is “s10001″, so the call to TransferContentToDevice() looks like:
kindle.TransferContentToDevice(
@”c:\temp\test.txt”,
@”s10001″);
NOTES:
- I left the object name as “kindle” even though I’m attaching a different device. Clearly, “kindle” is just the variable name and doesn’t matter in terms of function.
- I am copying the file c:\temp\test.txt to the device. You will want to substitute a file name appropriate to your environment–any file that exists on your PC, basically.
2. I also saw the error described by Rotem, above, and I adopted the_Drood’s solution:
// credit to the_Drood for the following conditional write
if (bytesRead < (int)optimalTransferSizeBytes) {
targetStream.Write(buffer, bytesRead, pcbWritten);
} else {
targetStream.Write(buffer, (int)optimalTransferSizeBytes, pcbWritten);
}