ファイルを移動/コピー/削除するには?[ユニバーサルWindowsアプリ開発]WinRT/Metro TIPS

Windows Runtime APIはファイルの扱いが独特だ。そこで、本稿では、このAPIを使ってファイルをコピー/移動/削除する方法を解説する。

» 2015年06月03日 05時00分 公開
[山本康彦BluewaterSoft/Microsoft MVP for Windows Platform Development]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 ファイルを他のフォルダーへ移動/コピーする、あるいは削除する。基本的なことではあるが、デスクトップアプリ開発やASP.NETのWeb開発からWindowsランタイムアプリ開発に移ってきた人にとっては、どうすればよいのか分かりにくいかもしれない。そこで本稿では、Windows 8.1とWindows Phone 8.1用のユニバーサルWindowsアプリ、およびWindows 10用のユニバーサルWindowsアプリで共通に使える方法を解説する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #106」からダウンロードできる。

事前準備

 Windows 8.1とWindows Phone 8.1用のユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Community 2013 with Update 4を使っている。

  • SLAT対応のPC*1
  • 2014年4月のアップデート*2適用済みの64bit版Windows 8.1 Pro版以上*3
  • Visual Studio 2013 Update 2(またはそれ以降)*4を適用済みのVisual Studio 2013(以降、VS 2013)*5

*1 SLAT対応ハードウエアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウエアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。

*2 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。

*3 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。

*4 マイクロソフトのダウンロードページから誰でも入手できる(このURLはUpdate 4のもの)。

*5 本稿に掲載したコードを試すだけなら、無償のExpressエディションやCommunityエディションで構わない。Visual Studio Express 2013 with Update 4 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsランタイムアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。また、2014年11月12日(米国時間)に新しくリリースされたVisual Studio Community 2013 with Update 4(製品版)もマイクロソフトのページから無償で入手できる。Communityエディションは本稿執筆時点では英語版だけなので、同じ場所にあるVisual Studio 2013 Language Packの日本語版を追加インストールし、[オプション]ダイアログで言語を切り替える必要がある。


Windows.Storage名前空間

 .NET Frameworkで開発してきた人は、ファイルの移動やコピーというとSystem.IO名前空間のFileクラスなどを思い浮かべるだろう。しかし、WindowsランタイムではWindows.Storage名前空間のクラスを主に利用する。System.IO名前空間は使えなくもないが、かえって面倒になる。

 ファイルの移動/コピー/削除には、以下に挙げるクラスをよく利用する。

  • ファイルの取り扱い:StorageFileクラス、NameCollisionOption列挙型、StorageDeleteOption列挙型(いずれもWindows.Storage名前空間)
  • フォルダーの取り扱い:StorageFolderクラス、KnownFoldersクラス、ApplicationDataクラス(いずれもWindows.Storage名前空間)

 なお、本稿で紹介するコードはWindows Phone 8.1でも動作するものだが、StorageFileクラス/StorageFolderクラスの中にはWindows Phone 8.1でサポートされていないメソッドなどもあるので注意してほしい。

処理対象のファイルやフォルダーのオブジェクトを得るには?

 移動/コピー/削除の対象とするファイルは、上述したStorageFileオブジェクトとして扱う。ところが、Windowsランタイムアプリでは、ファイルのフルパスを使ってファイルのオブジェクトを得ることはできないのだ。本稿では、StorageFileオブジェクトは取得できているものとして話を進めるが、簡単に紹介しておこう。StorageFileオブジェクトを取得するには、次のような方法を用いる。

  • URIスキーム:アプリのパッケージやローカルデータ記憶域にあるファイルは、URIスキームで指定が可能である。例:StorageFile.GetFileFromApplicationUriAsync("ms-appdata:///local/foo.txt");
  • ApplicationDataクラス/KnownFoldersクラス:アプリのローカルデータ記憶域にあるファイルは、ApplicationDataクラスを利用して取得できる。マニフェストで「機能」を追加した場合は、KnownFoldersクラスも使える。例:ApplicationData.Current.LocalFolder.GetFileAsync("foo.txt")
  • ファイルピッカー:任意の場所のファイルを扱うには、ファイルピッカー(Windows.Storage.Pickers名前空間のFileOpenPicker/FileSavePickerクラス)を出してエンドユーザーに選択してもらう必要がある

 また、処理対象のフォルダーはStorageFolderオブジェクトとして扱うのだが、これも同様にして取得できる(ピッカーにはFolderPickerクラスも用意されている)。

ファイルをコピーするには?

 StorageFileクラスのCopyAsyncメソッドを使えばよい。

 例えば、アプリのローカルフォルダーへコピーするコードは次のようになる。

// コピー元のファイル(何らかの手段で取得したStorageFileオブジェクト)
StorageFile file = ……省略……

// コピー先:アプリのローカルフォルダー
StorageFolder targetFolder = ApplicationData.Current.LocalFolder;

// コピーを実行する
var destFile = await file.CopyAsync(targetFolder);

' コピー元のファイル(何らかの手段で取得したStorageFileオブジェクト)
Dim file As StorageFile = ……省略……

' コピー先:アプリのローカルフォルダー
Dim targetFolder As StorageFolder = ApplicationData.Current.LocalFolder

' コピーを実行する
Dim destFile = Await file.CopyAsync(targetFolder)

ファイルをコピーするコード例(上:C#/下:VB)
StorageFileクラスのCopyAsyncメソッドの引数には、コピー先のフォルダーをStorageFolderオブジェクトで指定する。CopyAsyncメソッドはコピー先に作成したファイルのStorageFileオブジェクトを返してくるので、引き続きコピー先のファイルを使った処理を行うのに便利である。
なお、awaitキーワードを使っているので、このコードを含むメソッドのシグネチャにはasyncキーワードが必要だ。

 ただし、上のコードでは、コピー先に同じ名前のファイルがすでに存在していると、例外が発生する。そのようなときにどうするかを、NameCollisionOption列挙型を引数に与えて指定できる。例えば、コピー先に同じ名前のファイルがあったときに上書きするには、次のコードのようにする。

// コピー元のファイル(何らかの手段で取得したStorageFileオブジェクト)
StorageFile file = ……省略……

// コピー先:アプリのローカルフォルダー
StorageFolder targetFolder = ApplicationData.Current.LocalFolder;

// コピーを実行する
var destFile = await file.CopyAsync(targetFolder, file.Name,
                                    NameCollisionOption.ReplaceExisting);

' コピー元のファイル(何らかの手段で取得したStorageFileオブジェクト)
Dim file As StorageFile = ……省略……

' コピー先:アプリのローカルフォルダー
Dim targetFolder As StorageFolder = ApplicationData.Current.LocalFolder

' コピーを実行する
Dim destFile = Await file.CopyAsync(targetFolder, file.Name,
                                    NameCollisionOption.ReplaceExisting)

ファイルを上書きコピーするコード例(上:C#/下:VB)
このコードは、コピー先のフォルダーに同じ名前のファイルがすでに存在しているときには、上書きコピーする(存在しなかったときは普通にコピーする)。CopyAsyncメソッドの第2引数はコピー先のファイル名で、NameCollisionOption列挙体で名前が衝突した時の動作を指定する場合には、この指定も必須である。CopyAsyncメソッドの第3引数に与えるNameCollisionOption列挙型には、この他にGenerateUniqueName(自動的に新しいファイル名を付ける)とFailIfExists(例外を発生させる)がある。
なお、awaitキーワードを使っているので、このコードを含むメソッドのシグネチャにはasyncキーワードが必要だ。

ファイルを移動するには?

 StorageFileクラスのMoveAsyncメソッドを使えばよい。

 使い方は、上のCopyAsyncメソッドとほぼ同じだが、返値を持たないところが異なる。ファイルのコピーでは新しいファイルが作られるので、それにアクセスするには新しいStorageFileオブジェクトが必要だった。ファイルの移動では、「元のファイルがそのまま場所を変えただけ」だと考えるので、元のStorageFileオブジェクトで引き続きアクセスできるのである。

 例えば、アプリのローカルフォルダーへ移動するコードは次のようになる。

// 移動元のファイル(何らかの手段で取得したStorageFileオブジェクト)
StorageFile file = ……省略……

// 移動先:アプリのローカルフォルダー
StorageFolder targetFolder = ApplicationData.Current.LocalFolder;

// ファイルを移動する
await file.MoveAsync(targetFolder);

' 移動元のファイル(何らかの手段で取得したStorageFileオブジェクト)
Dim file As StorageFile = ……省略……

' 移動先:アプリのローカルフォルダー
Dim targetFolder As StorageFolder = ApplicationData.Current.LocalFolder

' ファイルを移動する
Await file.MoveAsync(targetFolder)

ファイルを移動するコード例(上:C#/下:VB)
StorageFileクラスのMoveAsyncメソッドの引数には、移動先のフォルダーをStorageFolderオブジェクトで指定する。移動後のファイルには、引き続きローカル変数「file」でアクセス可能である。
なお、awaitキーワードを使っているので、このコードを含むメソッドのシグネチャにはasyncキーワードが必要だ。

 上のコードでは、移動先に同じ名前のファイルがすでに存在していると、コピーと同様に例外が発生する。その場合に上書きするのであれば、やはりコピーと同様にNameCollisionOption.ReplaceExistingオプションを指定すればよい。ファイル名が衝突したときに自動的に別の名前にして移動するには、次のコードのようにする。

// 移動元のファイル(何らかの手段で取得したStorageFileオブジェクト)
StorageFile file = ……省略……
// 例として、このStorageFileオブジェクト(=file変数)のパス(=Pathプロパティ)が
// 「……省略……\TempState\sample.txt」だったとする

// 移動先:アプリのローカルフォルダー
StorageFolder targetFolder = ApplicationData.Current.LocalFolder;

// ファイルを移動する
await file.MoveAsync(targetFolder, file.Name, NameCollisionOption.GenerateUniqueName);
// 移動先のフォルダーにすでに同名のファイルが存在していた場合には、
// StorageFileオブジェクト(=file変数)のパスが、移動後に例えば次のように変わっている
// Windows 8.1:……省略……\LocalState\sample - コピー.txt
// Windows Phone 8.1:……省略……\LocalState\sample (1).txt

' 移動元のファイル(何らかの手段で取得したStorageFileオブジェクト)
Dim file As StorageFile = ……省略……
' 例として、このStorageFileオブジェクト(=file変数)のパス(=Pathプロパティ)が
' 「……省略……\TempState\sample.txt」だったとする

' 移動先:アプリのローカルフォルダー
Dim targetFolder As StorageFolder = ApplicationData.Current.LocalFolder

' ファイルを移動する
Await file.MoveAsync(targetFolder, file.Name, NameCollisionOption.GenerateUniqueName)
' 移動先のフォルダーにすでに同名のファイルが存在していた場合には、
' StorageFileオブジェクト(=file変数)のパスが、移動後に例えば次のように変わっている
' Windows 8.1:……省略……\LocalState\sample - コピー.txt
' Windows Phone 8.1:……省略……\LocalState\sample (1).txt

ファイル名が衝突したときに違う名前にしてファイルを移動するコード例(上:C#/下:VB)
このコードは、移動先のフォルダーに同じ名前のファイルがすでに存在しているときは、自動的に違うファイル名に変えてファイルを移動する(存在しなかったときは普通に移動する)。MoveAsyncメソッドの第2引数はコピー先のファイル名で、NameCollisionOption列挙体で名前が衝突した時の動作を指定する場合には、この指定も必須である。MoveAsyncメソッドの第3引数に与えるNameCollisionOption列挙型には、この他にReplaceExisting(上書きする)とFailIfExists(例外を発生させる)がある。
コメントに、移動前と後のStorageFileオブジェクトのPathプロパティの変化を書いておいた。移動前のStorageFileオブジェクトは、移動したことによって無効になったりnull/Nothingになったりはしない。そのPathプロパティがファイルの移動によって変化するのである。
なお、awaitキーワードを使っているので、このコードを含むメソッドのシグネチャにはasyncキーワードが必要だ。

ファイルを削除するには?

 StorageFileクラスのDeleteAsyncメソッドを使えばよい(次のコード)。

// 削除したいファイル(何らかの手段で取得したStorageFileオブジェクト)
StorageFile file = ……省略……

// ファイルを削除する
await file.DeleteAsync();

' 削除したいファイル(何らかの手段で取得したStorageFileオブジェクト)
Dim file As StorageFile = ……省略……

' ファイルを削除する
Await file.DeleteAsync()

ファイルを削除するコード例(上:C#/下:VB)
引数なしのDeleteAsyncメソッドでは、ファイルが「ごみ箱」に送られることもある(後述)。また、ファイルを削除してもローカル変数「file」はnull/Nothingにならないので注意してほしい(さらにアクセスすると例外が出る)。
なお、awaitキーワードを使っているので、このコードを含むメソッドのシグネチャにはasyncキーワードが必要だ。

 上のコードでは、ファイルが「ごみ箱」に送られるだけで完全には削除されないことがある。アプリのローカルデータ記憶域にあるファイル(=ApplicationDataクラスを利用してアクセスするファイル)とネットワークドライブにあるファイルは、完全に削除される。ローカルディスクでそれ以外の場所にあるファイルは「ごみ箱」に送られるのだ。

 常に完全に削除したい場合は、次のコードのようにしてStorageDeleteOption列挙型のPermanentDeleteメンバーを引数に指定すればよい。

// 削除したいファイル(何らかの手段で取得したStorageFileオブジェクト)
StorageFile file = ……省略……

// ファイルを常に完全に削除する
await file.DeleteAsync(StorageDeleteOption.PermanentDelete);

' 削除したいファイル(何らかの手段で取得したStorageFileオブジェクト)
Dim file As StorageFile = ……省略……

' ファイルを常に完全に削除する
Await file.DeleteAsync(StorageDeleteOption.PermanentDelete)

ファイルを常に完全に削除するコード例(上:C#/下:VB)
DeleteAsyncメソッドの引数にStorageDeleteOption.PermanentDeleteを指定すると、ファイルは「ごみ箱」に送られることなく完全に削除される。
なお、awaitキーワードを使っているので、このコードを含むメソッドのシグネチャにはasyncキーワードが必要だ。

まとめ

 ファイルの移動/コピー/削除は、Windows.Storage名前空間のStorageFileクラスを使うと簡単だ。ファイルを別のフォルダーへ移動したときにStorageFileオブジェクトが(消滅/生成せずに)変化するという挙動は、慣れないと戸惑うかもしれない。

【コラム】ユニバーサルWindowsアプリを開発するなら必見、厳選MVAコース!

Microsoft Virtual Academy(MVA)では、マイクロソフトのトレーニングコースが無償で受講できる。多数あるコースの中から、これからユニバーサルWindowsアプリを開発してみたいという人にぴったりのものを紹介しよう。


「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。