かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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にしてしまって操作するけどね!