WPDデバイスからファイルをダウンロードする
WPDデバイスからファイルをダウンロードするコードを紹介します。
概要
WindowsマシンとUSBケーブルで接続したデバイスとの間でMTPを利用してファイルのダウンロードをします。
事前準備
Portable Device APIの参照追加
Windows Formプロジェクトを作成します。
こちらの記事を参照してPortable Device APIのCOMオブジェクトを参照します。
WPDデバイスのファイル、フォルダを参照する機能の実装
ダウンロードするファイルを選択するため、WPDデバイスにあるファイル、フォルダを参照する機能を実装します。実装手順やコードは
こちらの記事を参照してください。
プログラム
UI
下図のUIを作成します。
こちらの記事で作成したUIにボタンを2つ追加します。また、FileOpenDialog, FileSaveDialogを追加します。ファイルのダウンロードではFileSaveDialogのみを利用します。
コード
プログラムコードは長いため、末尾に記載します。
解説
Downloadボタンのクリック
Downloadボタンがクリックされると下記のコードを実行します。ダウンロードするファイルはListViewで選択されれているファイルになります。ListViewの選択項目のファイル名とオブジェクトIDを取得します。SaveFileDialogを開きファイルの保存先を選択します。SaveFileDialogがOKで完了した場合、Downloadメソッドを呼び出しファイルのダウンロードを開始します。
private void button1_Click(object sender, EventArgs e)
{
string Filename = listView1.Items[listView1.SelectedItems[0].Index].Text;
string FileID = listView1.Items[listView1.SelectedItems[0].Index].SubItems[2].Text;
saveFileDialog1.FileName = Filename;
if (saveFileDialog1.ShowDialog() == DialogResult.OK) {
Download(FileID, saveFileDialog1.FileName);
}
}
Download処理
ファイルの情報の取得と同様に、IPortableDeviceContent を取得し IPortableDeviceProperties を取得します。
IPortableDeviceContent オブジェクトのTransfer メソッドを呼び出し、IPortableDeviceResources オブジェクトを取得します。また、_tagpropertykey オブジェクトを作成します。GUIDに
0xE81E79BE, 0x34F0, 0x41BF, 0xB5, 0x3F, 0xF1, 0xA0, 0x6A, 0xE8, 0x78, 0x42
を設定し、PIDに WPD_RESOURCE_DEFAULT を設定します。先に取得したIPortableDeviceResources オブジェクトのGetStream メソッドを呼び出しファイルをダウンロードするためのストリームを取得します。
ストリームはGetStream の第四引数に与えたPortableDeviceApiLib.IStream オブジェクトでアクセスします。
public void Download(string FileID, string FilePath)
{
WPDDevice device = (WPDDevice)comboBox_device.SelectedItem;
IPortableDeviceContent content;
device.DeviceClass.Content(out content);
IPortableDeviceProperties properties;
content.Properties(out properties);
PortableDeviceObject downloadFileObj = WrapObject(properties, FileID);
IPortableDeviceResources resources;
content.Transfer(out resources);
PortableDeviceApiLib.IStream wpdStream;
uint optimalTransferSize = 0;
var property = new _tagpropertykey();
property.fmtid = new Guid(0xE81E79BE, 0x34F0, 0x41BF, 0xB5, 0x3F, 0xF1, 0xA0, 0x6A, 0xE8, 0x78, 0x42);
property.pid = WPD_RESOURCE_DEFAULT;
resources.GetStream(FileID, ref property, 0, ref optimalTransferSize, out wpdStream);
取得した PortableDeviceApiLib.IStream はそのまま利用できないため、ComTypes.IStream にキャストして利用します。PC側のディスクに書き込むためのFileStreamを準備します。ComTypes.IStream からデータを読み出し、FileStreamでPCのディスクに書き込みます。ComTypes.IStream のReadメソッドの第三引数にIntPtrを渡しますが、COMに渡すポインタのため、あらかじめ Marshal.AllocCoTaskMem でメモリ確保をする必要があります。Readメソッド終了後に pbytesRead に格納されている実態の値にアクセスする場合は、Marshal.ReadInt32 メソッドを利用します。ComTypes.IStream のデータがすべて読み終わるまでループで繰り返しストリームからデータを読み出します。
System.Runtime.InteropServices.ComTypes.IStream sourceStream = (System.Runtime.InteropServices.ComTypes.IStream)wpdStream;
System.IO.FileStream targetStream = new System.IO.FileStream(FilePath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
int BUFFER_SIZE = 32767;
byte[] buffer = new byte[BUFFER_SIZE];
IntPtr pbytesRead = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)));
while (true) {
sourceStream.Read(buffer, BUFFER_SIZE, pbytesRead);
int bytesRead = Marshal.ReadInt32(pbytesRead);
if (bytesRead <= 0) {
break;
}
targetStream.Write(buffer, 0, bytesRead);
}
targetStream.Close();
Marshal.ReleaseComObject(sourceStream);
Marshal.ReleaseComObject(wpdStream);
}
実行結果
プロジェクトを実行します。下図のウィンドウが表示されます。
コンボボックスで接続するデバイスを選択します。項目をダブルクリックしフォルダ内にアクセスします。
今回はこちらのスクリーンショットの画像ファイルをダウンロードします。ダウンロードするファイルを選択します。選択された状態で上部の[Download]ボタンをクリックします。
[Download]ボタンをクリックするとファイル保存ダイアログが表示されます。[保存]ボタンをクリックします。
ファイルがダウンロードされます。ファイル保存ダイアログで保存したディレクトリを確認するとファイルが作成されていることが確認できます。
ファイルを開きスクリーンショットの画像ファイルがダウンロードできていることが確認できました。
参考: プログラムコード全体
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using PortableDeviceApiLib;
namespace WpdDemo
{
public partial class FormMain : Form
{
public const uint WPD_OBJECT_ID = 2;
public const uint WPD_OBJECT_PARENT_ID = 3;
public const uint WPD_OBJECT_NAME = 4;
public const uint WPD_OBJECT_CONTENT_TYPE = 7;
public const uint WPD_OBJECT_SIZE = 11;
public const uint WPD_OBJECT_ORIGINAL_FILE_NAME = 12;
public const uint WPD_OBJECT_DATE_CREATED = 18;
public const uint WPD_OBJECT_DATE_MODIFIED = 19;
public const uint WPD_FUNCTIONAL_OBJECT_CATEGORY = 2;
public const uint WPD_CLIENT_NAME = 2;
public const uint WPD_CLIENT_MAJOR_VERSION = 3;
public const uint WPD_CLIENT_MINOR_VERSION = 4;
public const uint WPD_CLIENT_REVISION = 5;
public const uint WPD_RESOURCE_DEFAULT = 0;
public const uint WPD_STORAGE_FREE_SPACE_IN_BYTES = 5;
private PortableDeviceManager deviceManager;
class WPDDevice
{
public string DeviceID;
public string DeviceName;
public PortableDeviceClass DeviceClass;
public override string ToString()
{
return string.Format("WPDデバイス名:{0}\r\n", DeviceName);
}
}
public enum ObjectKind { FOLDER, FILE };
public class PortableDeviceObject
{
public string Id { get; set; }
public string Name { get; set; }
public ObjectKind kind { get; set; }
}
public FormMain()
{
InitializeComponent();
}
private void FormMain_Load(object sender, EventArgs e)
{
init();
}
private void init()
{
deviceManager = new PortableDeviceManager();
deviceManager.RefreshDeviceList();
uint count = 0;
deviceManager.GetDevices(null, ref count);
string[] devicesIDs = new string[count];
WPDDevice[] wpdDevice = new WPDDevice[count];
//PortableDeviceClass[] devices = new PortableDeviceClass[count];
deviceManager.GetDevices(devicesIDs, ref count);
for (int i = 0; i < count; i++) {
wpdDevice[i] = new WPDDevice();
wpdDevice[i].DeviceID = devicesIDs[i];
}
IPortableDeviceValues clientInfo = (IPortableDeviceValues)new PortableDeviceTypesLib.PortableDeviceValuesClass();
for (int i = 0; i < count; i++) {
wpdDevice[i].DeviceClass = new PortableDeviceClass();
wpdDevice[i].DeviceClass.Open(wpdDevice[i].DeviceID, clientInfo);
IPortableDeviceContent content;
wpdDevice[i].DeviceClass.Content(out content);
IPortableDeviceProperties properties;
content.Properties(out properties);
IPortableDeviceValues propertyValues;
properties.GetValues("DEVICE", null, out propertyValues);
//wpdDevice[i].DeviceClass.Close();
string name;
_tagpropertykey property = new _tagpropertykey();
property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
property.pid = 4;
try {
propertyValues.GetStringValue(property, out name);
wpdDevice[i].DeviceName = name;
}
catch (System.Runtime.InteropServices.COMException) {
wpdDevice[i].DeviceName = "なし";
}
comboBox_device.Items.Add(wpdDevice[i]);
}
}
private void comboBox_device_SelectedIndexChanged(object sender, EventArgs e)
{
DeviceChanged();
}
private void DeviceChanged()
{
WPDDevice device = (WPDDevice)comboBox_device.SelectedItem;
IPortableDeviceContent content;
device.DeviceClass.Content(out content);
IPortableDeviceProperties properties;
content.Properties(out properties);
IEnumPortableDeviceObjectIDs objectIDs;
string FolderID = "DEVICE";
content.EnumObjects(0, FolderID, null, out objectIDs);
listView1.Items.Clear();
string objectID;
uint fetched = 0;
while (true) {
objectIDs.Next(1, out objectID, ref fetched);
if (fetched <= 0) break;
PortableDeviceObject currentObject = WrapObject(properties, objectID);
ListViewItem li = listView1.Items.Add(currentObject.Name);
switch (currentObject.kind) {
case ObjectKind.FILE:
li.SubItems.Add("ファイル");
break;
case ObjectKind.FOLDER:
li.SubItems.Add("フォルダ");
break;
}
li.SubItems.Add(currentObject.Id);
}
}
private void listView1_DoubleClick(object sender, EventArgs e)
{
string FolderID = listView1.Items[listView1.SelectedItems[0].Index].SubItems[2].Text;
WPDDevice device = (WPDDevice)comboBox_device.SelectedItem;
IPortableDeviceContent content;
device.DeviceClass.Content(out content);
IPortableDeviceProperties properties;
content.Properties(out properties);
IEnumPortableDeviceObjectIDs objectIDs;
content.EnumObjects(0, FolderID, null, out objectIDs);
listView1.Items.Clear();
string objectID;
uint fetched = 0;
while (true) {
objectIDs.Next(1, out objectID, ref fetched);
if (fetched <= 0) break;
PortableDeviceObject currentObject = WrapObject(properties, objectID);
ListViewItem li = listView1.Items.Add(currentObject.Name);
switch (currentObject.kind) {
case ObjectKind.FILE:
li.SubItems.Add("ファイル");
break;
case ObjectKind.FOLDER:
li.SubItems.Add("フォルダ");
break;
}
li.SubItems.Add(currentObject.Id);
}
}
public static PortableDeviceObject WrapObject(IPortableDeviceProperties properties, string objectID)
{
IPortableDeviceKeyCollection keys;
properties.GetSupportedProperties(objectID, out keys);
IPortableDeviceValues values;
properties.GetValues(objectID, keys, out values);
// Get the name of the object
string name;
_tagpropertykey property = new _tagpropertykey();
property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
property.pid = WPD_OBJECT_NAME;
try {
values.GetStringValue(property, out name);
}
catch (System.Runtime.InteropServices.COMException exc) {
name = "(non name)";
}
// Get the original name of the object
string OriginalName;
property = new _tagpropertykey();
property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
property.pid = WPD_OBJECT_ORIGINAL_FILE_NAME;
try {
values.GetStringValue(property, out OriginalName);
}
catch (Exception e) {
OriginalName = "";
}
// Get the type of the object
Guid contentType;
property = new _tagpropertykey();
property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
property.pid = WPD_OBJECT_CONTENT_TYPE;
try {
values.GetGuidValue(property, out contentType);
}
catch (System.Runtime.InteropServices.COMException exc) {
PortableDeviceObject obj = new PortableDeviceObject();
obj.Id = null;
obj.Name = name;
obj.kind = ObjectKind.FOLDER;
return obj;
}
Guid folderType = new Guid(0x27E2E392, 0xA111, 0x48E0, 0xAB, 0x0C, 0xE1, 0x77, 0x05, 0xA0, 0x5F, 0x85);
Guid functionalType = new Guid(0x99ED0160, 0x17FF, 0x4C44, 0x9D, 0x98, 0x1D, 0x7A, 0x6F, 0x94, 0x19, 0x21);
if (contentType == folderType || contentType == functionalType) {
PortableDeviceObject fobj = new PortableDeviceObject();
fobj.Id = objectID;
fobj.Name = name;
fobj.kind = ObjectKind.FOLDER;
return fobj;
}
if (OriginalName.CompareTo("") != 0) {
name = OriginalName;
}
PortableDeviceObject robj = new PortableDeviceObject();
robj.Id = objectID;
robj.Name = name;
robj.kind = ObjectKind.FILE;
return robj;
}
private void button1_Click(object sender, EventArgs e)
{
string Filename = listView1.Items[listView1.SelectedItems[0].Index].Text;
string FileID = listView1.Items[listView1.SelectedItems[0].Index].SubItems[2].Text;
saveFileDialog1.FileName = Filename;
if (saveFileDialog1.ShowDialog() == DialogResult.OK) {
Download(FileID, saveFileDialog1.FileName);
}
}
public void Download(string FileID, string FilePath)
{
WPDDevice device = (WPDDevice)comboBox_device.SelectedItem;
IPortableDeviceContent content;
device.DeviceClass.Content(out content);
IPortableDeviceProperties properties;
content.Properties(out properties);
PortableDeviceObject downloadFileObj = WrapObject(properties, FileID);
IPortableDeviceResources resources;
content.Transfer(out resources);
PortableDeviceApiLib.IStream wpdStream;
uint optimalTransferSize = 0;
var property = new _tagpropertykey();
property.fmtid = new Guid(0xE81E79BE, 0x34F0, 0x41BF, 0xB5, 0x3F, 0xF1, 0xA0, 0x6A, 0xE8, 0x78, 0x42);
property.pid = WPD_RESOURCE_DEFAULT;
resources.GetStream(FileID, ref property, 0, ref optimalTransferSize, out wpdStream);
System.Runtime.InteropServices.ComTypes.IStream sourceStream = (System.Runtime.InteropServices.ComTypes.IStream)wpdStream;
System.IO.FileStream targetStream = new System.IO.FileStream(FilePath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
int BUFFER_SIZE = 32767;
byte[] buffer = new byte[BUFFER_SIZE];
IntPtr pbytesRead = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)));
while (true) {
sourceStream.Read(buffer, BUFFER_SIZE, pbytesRead);
int bytesRead = Marshal.ReadInt32(pbytesRead);
if (bytesRead <= 0) {
break;
}
targetStream.Write(buffer, 0, bytesRead);
}
targetStream.Close();
Marshal.ReleaseComObject(sourceStream);
Marshal.ReleaseComObject(wpdStream);
}
private void button2_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK) {
string FolderID = listView1.Items[listView1.SelectedItems[0].Index].SubItems[2].Text;
Upload(FolderID, openFileDialog1.FileName);
}
}
public void Upload(string FolderID, string FilePath)
{
WPDDevice device = (WPDDevice)comboBox_device.SelectedItem;
IPortableDeviceContent content;
device.DeviceClass.Content(out content);
IPortableDeviceProperties properties;
content.Properties(out properties);
IPortableDeviceValues values = GetRequiredPropertiesForContentType(FilePath, FolderID);
PortableDeviceApiLib.IStream tempStream;
uint optimalTransferSizeBytes = 0;
content.CreateObjectWithPropertiesAndData(values, out tempStream, ref optimalTransferSizeBytes, null);
System.Runtime.InteropServices.ComTypes.IStream targetStream = (System.Runtime.InteropServices.ComTypes.IStream)tempStream;
try {
System.IO.FileStream sourceStream = new System.IO.FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] buffer = new byte[optimalTransferSizeBytes];
int bytesRead;
while (true) {
bytesRead = sourceStream.Read(buffer, 0, (int)optimalTransferSizeBytes);
if (bytesRead <= 0) {
break;
}
IntPtr pcbWritten = IntPtr.Zero;
targetStream.Write(buffer, (int)bytesRead, pcbWritten);
}
targetStream.Commit(0);
}
finally {
Marshal.ReleaseComObject(tempStream);
}
}
private IPortableDeviceValues GetRequiredPropertiesForContentType(string FileName, string parentObjectId)
{
IPortableDeviceValues values = new PortableDeviceTypesLib.PortableDeviceValues() as IPortableDeviceValues;
_tagpropertykey WPD_OBJECT_PARENT_ID_PK = new _tagpropertykey();
WPD_OBJECT_PARENT_ID_PK.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
WPD_OBJECT_PARENT_ID_PK.pid = WPD_OBJECT_PARENT_ID;
values.SetStringValue(ref WPD_OBJECT_PARENT_ID_PK, parentObjectId);
System.IO.FileInfo fileInfo = new System.IO.FileInfo(FileName);
var WPD_OBJECT_SIZE_PK = new _tagpropertykey();
WPD_OBJECT_SIZE_PK.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
WPD_OBJECT_SIZE_PK.pid = WPD_OBJECT_SIZE;
values.SetUnsignedLargeIntegerValue(WPD_OBJECT_SIZE_PK, (ulong)fileInfo.Length);
var WPD_OBJECT_ORIGINAL_FILE_NAME_PK = new _tagpropertykey();
WPD_OBJECT_ORIGINAL_FILE_NAME_PK.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
WPD_OBJECT_ORIGINAL_FILE_NAME_PK.pid = WPD_OBJECT_ORIGINAL_FILE_NAME;
values.SetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME_PK, System.IO.Path.GetFileName(FileName));
var WPD_OBJECT_NAME_PK = new _tagpropertykey();
WPD_OBJECT_NAME_PK.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC, 0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
WPD_OBJECT_NAME_PK.pid = WPD_OBJECT_NAME;
values.SetStringValue(WPD_OBJECT_NAME_PK, System.IO.Path.GetFileName(FileName));
return values;
}
}
}
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用