かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

WinRTのStreamとDataReader, DataWriterって使いにくいよね!

.NETのStream系に慣れてるせいか、とても癖があるように感じてしまうWinRTのStream。ちょっと使ってみたいと思います。

まず、ファイルにデータを書き込むところから。 ファイルに書き込む処理には便利な機能があって、書き込みをトランザクションとして扱ってくれる機能があります。StorageFileオブジェクトのOpenTransactedWriteAsyncメソッドでトランザクション開始して、Streamプロパティで取得できるStreamにデータを書き込めばOKです。

Stream生のままだと低レベルすぎるのでDataWriterでラップして各型ごとのデータ書き込みメソッドでデータを書き込むのが幾分か楽です。

以下の例では、文字列長と、文字列を書き込んでいます。全てIOのある処理は非同期なのがWinRTの特徴ですよね。

var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
    "sample.txt", 
    CreationCollisionOption.ReplaceExisting);
using (var tx = await file.OpenTransactedWriteAsync())
using (var w = new DataWriter(tx.Stream))
{
    var data = "Hello world " + DateTime.Now;
    var buff = Encoding.UTF8.GetBytes(data);
    w.WriteInt32(buff.Length);
    w.WriteString(data);
    await w.StoreAsync();

    await tx.CommitAsync();
}

とまぁ、書き込みはいいとして読み込みが凄い癖があります。

読み込みはDataReaderでStreamをラップして使えばいいんですが、こいつがLoadAsyncでバッファに指定したサイズだけデータを読み込んで、Read型名って名前のメソッドでバッファからデータを読み込みます。LoadAsyncメソッドが非同期メソッドです。Read型名は同期です。つまりRead型名ではIOが発生しないと。あらかじめ必要なデータをLoadAsyncでバッファに読み込んでおかないといけない。難しい。

ということで、先ほど書き込んだデータを読み込むコードは以下のようになります。LoadAsyncでintのサイズだけデータを読み込んで、ReadInt32で読み込んだデータをint型として取りだしています。そして、同じ要領で文字列も読み込んでいます。

var data = default(string);

var file = await ApplicationData.Current.LocalFolder.TryGetItemAsync("sample.txt");
if (file == null) { return; }

using (var s = await ((StorageFile)file).OpenReadAsync())
using (var r = new DataReader(s))
{
    await r.LoadAsync(sizeof(int));
    var length = r.ReadInt32();
    await r.LoadAsync((uint)length);
    data = r.ReadString((uint)length);
}

var dialog = new MessageDialog(data);
await dialog.ShowAsync();

個人的にはAsStreamとかでSystem.IO.Streamにしてしまって操作するけどね!