[Webアプリ] [Django] Webアプリ開発に挑戦 (1) 環境構築

前々から興味はあったのだがとにかく知識が全くないのでなかなか手を出せずにいたWebアプリ。ついに気合を入れてやってみようと思う。
最終的には会社の部品DBを作りたい。


さて、そうは言ってもとにかく何も判らない。まずは言語選択かな、と思い適当にGoogle先生に聞いてみてPythonを使ってみることにした。
言語が決まったらWebアプリのフレームワークだよなー、ってことでまたGoogle先生を頼る。いくつか候補が出てきたがDjangoってのが良さそうに見えたのでこれにする。
この辺りは悩んでいてもしょうがないからとにかく決めて始めることにする。
そして開発環境はPyCharmっていうのがいいらしい。PyCharmには、有料のProfessional Editionと無料のCommunity Editionがある。Professional EditionだとDjangoのプロジェクトを作れるらしい。
Professional Editionの価格は、US$19.9/月 または US$199/年で年ライセンスだと2年目US$159、3年目以降US$119になる。(30日の試用期間あり)
Professional Editionを使ってみていい感じだったら月ライセンスかな。ていうか最近はこういうのみんなサブスクリプション型のライセンスなのねー。


という訳で環境は決まった。

インストールしていく。

Pythonのインストール

まずはインストーラのダウンロード。
https://www.python.org/downloads/windows/
から最新版(2017/11/22現在)のPython 3.6.3-2017-10-03をゲット。Windows x86-64 executable installerにした。64bitの方がいいよね?
ダウンロードしたファイルをダブルクリックしてポチポチやってればPythonのインストールは完了。
オプションは全部デフォルトのままで、Add Python 3.6 to PATHのチェックだけ付けた。

virtualenvのインストール

さて、上で書いた環境一覧には入ってないが、ここでvirtualenvをインストールする。virtualenvってのは名前の通り、Pythonの仮想環境を作ってくれるらしい。違うバージョンのPythonを使いたいとかプロジェクト毎の環境を作ったり。おそらく開発中とか実験中は色んなモジュールを入れてみたりすると思うので、これはやっておいた方が良さそう。実験用環境と開発用環境とリリースを想定した環境とかかな。まあ、やってく内にきっと何となく判ってくるだろう。とりあえずあって困るものじゃなさそうだし。
インストールは簡単。Pythonにはpipというパッケージ管理システムが付いていてpipコマンドでインストールができる。
コマンドプロンプトを開いて

C:\>pip install virtualenv
Collecting virtualenv
Downloading virtualenv-15.1.0-py2.py3-none-any.whl (1.8MB)
100% |████████████████████████████████| 1.8MB 883kB/s
Installing collected packages: virtualenv
Successfully installed virtualenv-15.1.0

これだけ。
どこにインストールされたんだろう?と思って探してみるとPythonインストールディレクトリのScriptsの下にvirtualenv.exeってのがあった。(pipもここにある)

仮想環境の作成

virtualenvをインストールしたので仮想環境を作ってみる。名前はenv00にしておく。
コマンドプロンプトで仮想環境を作成したいディレクトリへ移動して

D:\XXXXX>virtualenv env00
Using base prefix 'd:\\program files\\python\\python36'
New python executable in D:\XXXXX\env00\Scripts\python.exe
Installing setuptools, pip, wheel...done.

15〜20秒ぐらいで終わった。ここでdirを見ると

D:\XXXXX>dir
ドライブ D のボリューム ラベルは DATA です
ボリューム シリアル番号は 3E82-5136 です

D:\XXXXX のディレクト

2017/11/22 13:30

. 2017/11/22 13:30 .. 2017/11/22 13:30 env00 0 個のファイル 0 バイト 3 個のディレクトリ 654,557,671,424 バイトの空き領域

と、作った仮想環境名のディレクトリが出来てる。中に入ってみると

D:\XXXXX>cd env00

D:\XXXXX\env00>dir
ドライブ D のボリューム ラベルは DATA です
ボリューム シリアル番号は 3E82-5136 です

D:\XXXXX\env00 のディレクト

2017/11/22 13:30

. 2017/11/22 13:30 .. 2017/11/22 12:40 Include 2017/11/22 13:30 Lib 2017/11/22 13:30 60 pip-selfcheck.json 2017/11/22 13:30 Scripts 2017/11/22 13:30 tcl 1 個のファイル 60 バイト 6 個のディレクトリ 654,557,671,424 バイトの空き領域

こんな感じ。今のところ中身が何なのかはさっぱり判らない。
この環境を使うには

D:\XXXXX\env00>Scripts\activate
(env00) D:\XXXXX\env00>

とすればいいみたい。これで仮想環境の中にいる状態になる。
仮想環境から抜けるには

(env00) D:\XXXXX\env00>Scripts\deactivate
D:\XXXXX\env00>

Djangoのインストール

ようやくDjangoのインストール。上で作った仮想環境(env00)の中にインストールする。
まずは仮想環境に入る。仮想環境のディレクトリに移動して

D:\XXXXX\env00>Scripts\activate
(env00) D:\XXXXX\env00>

インストールにはpipコマンドが使える。

(env00) D:\XXXXX\env00>pip install django
Collecting django
Downloading Django-1.11.7-py2.py3-none-any.whl (6.9MB)
100% |████████████████████████████████| 7.0MB 252kB/s
Collecting pytz (from django)
Downloading pytz-2017.3-py2.py3-none-any.whl (511kB)
100% |████████████████████████████████| 512kB 1.5MB/s
Installing collected packages: pytz, django
Successfully installed django-1.11.7 pytz-2017.3

ちゃんと最新版がインストールされたみたい。

PyCharmのインストール

https://www.jetbrains.com/pycharm/
からProfessionalの方をダウンロードする。インストーラを実行してポチポチやってれば終わる。
Create Desktop shortcutは64-bit launcherをチェックした。
Download and install JRE x86 by JetBrainsはチェックしなかった。(何の為なのかよくわからんし)


早速起動してみると、PyCharmの設定をインポートするか?と聞いてくる。初めて使うんだからもちろんインポートする設定なんて持ってない。Do not import settingsを選んでOKをクリック。
次にプライバシーポリシーとかが書かれてるので、Accept。そうするとライセンスの画面になる。お試し使用をしたいのでEvaluate for freeを選択してEvaluateボタンをクリック。
最後にライセンス契約を承諾しないと使い始められないよ、と言われるのでAccept。これでPyCharmが起動する。


以上で環境構築完了!


参考にしたサイト
https://qiita.com/kaki_k/items/1fff7fefcf26dc4b69bc

[RaspberryPi] RTCによる日時の保持

RaspberryPi3にWindows10 IoT Coreをインストールして使っているが、RaspberryPiにはRTCが実装されていないので、電源をOFFすると時刻が停止する。起動すると前回シャットダウンした時の時刻になっている。
インターネットに接続されている環境ではデフォルトで起動時に時刻合わせをするんだけど、インターネットに接続されていないとそれが出来ないから当然日時がずれる。
これを何とかしたくて調べてみたところ、とても参考になるサイトがあった。

Adding the Missing Real Time Clock to Windows IoT on Raspberry Pi
https://www.codeproject.com/Articles/1113626/Adding-the-Missing-Real-Time-Clock-to-Windows-IoT

これによると、DS3231というRTCのICを使っている。
まずはこれを買わなくては、と探すとちょうどいい基板に実装されているのがあった。しかも安い。

Rasbee オリジナル DS3231 クロックモジュール 時計モジュール 最高の精度 Arduinoのための Raspberry Piのための 1個 [並行輸入品]
https://www.amazon.co.jp/Rasbee-%E3%82%AF%E3%83%AD%E3%83%83%E3%82%AF%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB-%E6%99%82%E8%A8%88%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB-Arduino%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE-Raspberry/dp/B01HH5VP2W/ref=sr_1_6?ie=UTF8&qid=1502876622&sr=8-6&keywords=DS3231

早速入手。
これはRaspberryPiのIOピンに直接接続できる様になっていて、しかも大抵のRaspberryPi用ケースに収まる。(多分)
実際、俺が使っているケースにもうまく収まった。

さて、ハード的にはOKなのであとはソフト。今回は、上のCODE PROJECTの記事で公開されているソフトをそのまま活用させてもらうことにした。何をやっているのかは、記事を参照。
ただし、記事の通りにやっても起動時にうまくRTC時刻がシステムに設定されないのでちょっと変更。

まずは、公開されているBinaries(x86 and ARMS)をダウンロードして解凍、「ARM」フォルダを開くと必要なファイルがそろっている。
使うのは、

  • SetTimeTool.exe
  • UpdateTime.ps1

の2つ。
C:\shares\boot
というフォルダを作ってそこにコピー。
更に

  • UpdateTime.bat

というファイルを作る。
中身は

Powershell.exe -Command "C:\shares\boot\UpdateTime.ps1"

としておく。

ここで、一度RTCの設定を確認。PowerShell

cd C:\shares\boot
./SetTimeTool

とすると、RTCの現在時刻が表示される。まだ設定をしてないから正しい時刻は表示されない。
SetTimeToolは、システム時刻をRTCに設定する様に作られてるから、まずはシステム時刻を合わせる。

set-date "2017/08/16 18:00:00"

とかやればOK。
確認は、

get-date

システム時刻が正しく設定されたら

./SetTimeTool set-time

とすると、システム時刻をRTCに設定してくれる。
もう一度

./SetTimeTool

とすると、システム時刻と同じになったRTC時刻が表示されるはず。

ここまで出来たら、後はOS起動時にRTC時刻をシステム時刻に設定する様にスケジューラに登録すればいい。
PowerShell

SCHTASKS /CREATE /TN "Update Time Script" /TR C:\shares\boot\UpdateTime.bat /SC ONSTART /RU SYSTEM

とやれば登録完了。

シャットダウン => しばらく待つ => 起動
としてシステム時刻がずれないことを確認できるはず。
(インターネット接続はしないでおく)

実際には起動してから少し時間がかかるっぽい。
デフォルトでスタートアップになっている IoTCoreDefaultAppの画面が表示されてから15〜20秒程度で、システム時刻が更新される。

[UWP] 配置エラー

以前作成したUWPアプリを久しぶりにVisual Studio 2017で開いて実行(デバッグ)しようとしたところ、今まで見たことなかった配置エラーというのが出た。

出力ウィンドウには以下のメッセージ。

DEP0700: アプリケーションの登録に失敗しました。[0x80073CF9] エラー 0x80070003: パッケージ e5a0f0ac-3d92-49b7-9a71-298087080b2b_1.0.0.0_x86__wv8jtq60vztky の展開が AppLocker によってブロックされました。

少し調べたけど良く分からず、Visual Studioの修復とかやっても直らない。
もう少し調べたら、こんなのがあった。

https://developercommunity.visualstudio.com/solutions/73776/view.html

同じ症状で単にC:\Windows\System32\AppLockerというフォルダを作ったらエラーが解消されたらしい。
試しにやってみたら本当に直った。何だこれ。。


ちなみに、上のエラー発生とほぼ同時期にWindows10のスタートボタンをクリックしてもメニューが表示されなくなっていた。
これもGoogle先生に聞いて色々と試したが全然直らず。
Microsoftのフォーラムに書いてあったコマンドとかも試したけど、コマンドの実行結果がほとんどエラー。
(はっきり覚えていないけど、これもAppLockerが何とかって出ていた気がする)


何と、上の配置エラーが直ると同時にスタートメニューも直った。
訳が判らないけど、とりあえず直ったからOKとしよう。。。


試しにC:\Windows\System32\AppLockerフォルダを削除してみたらところ再度配置エラー発生。
やっぱりこれなのか。フォルダ内には何も保存されていないんだけど。。

[UWP] シャットダウン

Windows10 IoT Coreは組込機器で動作させることを前提としているのでOS操作用のUIは無い。(スタートボタンとか)
というか、起動したらアプリが実行されてそのアプリしか操作できない状態になる。
だから、[スタートボタン] => 電源 => シャットダウン みたいな操作は出来ない。
でも、組込機器だって電源を切ることはあるからその時にはシャットダウンが必要になる。つまりUWPアプリからWindowsのシャットダウンを行う必要があるってことになる。
そのやり方。

プロジェクトに参照を追加する

まず始めにプロジェクトに参照を追加する。Visual Studio2015のメニューで
プロジェクト => 参照の追加 を選択して参照マネージャーを表示させ、Universal Windowsの拡張で「Windows IoT Extensions for the UWP」にチェックを入れる。
バージョンがいくつかあるが、最新(2017/3/9現在)の10.0.14393.0を選択。
これで、Windows.System.ShutdownManagerクラスが使える様になる。

アプリマニフェストの編集

マニフェストにCapabilityを追加する。
追加項目は、GUIのチェック項目に存在しないので、XMLをテキストで開いて以下の内容を追加する。

  • Packageタグ

(5行目の追加と6行目の"iot"追加)

<Package
	xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
	xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
	xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
	xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"
	IgnorableNamespaces="uap mp iot">
  • Capability
<Capabilities>
	<iot:Capability Name="systemManagement" />
</Capabilities>

コード

上に書いた様にWindows.System.ShutdownManagerクラスを使う。
このクラスは、2つのstaticメソッドを持ってる。

public static void BeginShutdown(ShutdownKind shutdownKind, TimeSpan timeout)
システムをシャットダウンする。ShutdownKindは、Shutdown/Restart。
timeoutは、ShutdownKindがRestartの場合に再起動するまでの待ち時間を指定する。

public static void CancelShutdown()
実行中のシャットダウンをキャンセルする。

ということなので、システムをシャットダウンするには

ShutdownManager.BeginShutdown(ShutdownKind.Shutdown, TimeSpan.Zero);

とすればOK。

参考にしたサイト
https://marcominerva.wordpress.com/2016/12/13/how-to-shutdown-a-windows-10-iot-core-device-from-a-uwp-app/

[UWP] テキストファイルの読み書き

RaspberryPi3に入れたWindows10 IoT Core上で動作するアプリを作成中なんだけど色々と判らないことが多い。
そもそもIoT Core以前にUWPアプリ(Universal Windows Platform アプリ)についての知識が全然ない。
しかもIoT Core上だと使えないUWPのAPIがたくさんある。

  • Universal APIs not functional in Windows10 IoT Core at this time

https://developer.microsoft.com/en-us/windows/iot/docs/unavailableapis

今回USBメモリをRaspberryPi3に挿してログファイルを保存したり、設定ファイルを読み書きしたりしてみたのでメモ。
開発環境は、Visual Studio 2015 Update3

PCの場合 (Windows10)

IoT Coreの前にまずは普通のWindows10の場合。保存先のフォルダを選択するにはFolderPicker(Windows.Storage.Pickers.FolderPicker)が使える。
これは、.NET FrameworkのFolderBrowserDialog(System.Windows.Forms.FolderBrowserDialog)みたいなものかな。
これを使えば簡単にフォルダ選択が出来る。

private async void btnFolderPicker_Click(object sender, RoutedEventArgs e)
{
	FolderPicker fp = new FolderPicker();
	fp.SuggestedStartLocation = PickerLocationId.ComputerFolder;
	fp.FileTypeFilter.Add("*");
	StorageFolder sf = await fp.PickSingleFolderAsync();
	if (sf != null)
	{
		//----- 書込み時に使う為にStorageFolderを保持
		m_storageFolder = sf;
		//----- これをやっとかないとアクセスできない
		Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("PikcedFolder", m_storageFolder);
	}
}

要はアクセスしたいフォルダのStorageFolderを取得すればいい。
で、ファイルを書き込むには

private async Task WriteFile()
{
	if (m_storageFolder == null)
	{
		return;
	}
	//----- ファイル名
	DateTime dt = DateTime.Now;
	string strFilename = string.Format("{0}.txt", dt.ToString("yyyyMMdd-HHmmssfff"));
	//----- ファイルを作成
	StorageFile sf = await m_storageFolder.CreateFileAsync(strFilename, CreationCollisionOption.ReplaceExisting);
	//----- ストリームを取得
	var stream = await sf.OpenAsync(FileAccessMode.ReadWrite);
	//----- 出力ストリームを取得
	using (var outputStream = stream.GetOutputStreamAt(0))
	{
		//----- データライタを取得
		using (var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream))
		{
			dataWriter.WriteString("ABCDEFG\r\n");
			await dataWriter.StoreAsync();
			await outputStream.FlushAsync();
		}
	}
	stream.Dispose();
}

とやればOK。これはほぼMicrosoftのサンプルのまま。
(https://docs.microsoft.com/ja-jp/windows/uwp/files/quickstart-reading-and-writing-files)

RaspberryPi3の場合 (Windows10 IoT Core)

IoT Coreの場合もPCと同じ様に、保存したいフォルダのStorageFolderを取得出来れば、書込み自体は全く同じ。
ただ残念ながらFolderPickerが使えない。
しょうがないから自分で何とかしなきゃいけないんだけど、まず始めにアプリマニフェストを編集する必要がある。
今回はUSBメモリを使うからPackage.appxmanifestを開いてCapabilitiesタブを選び、Removable Storageにチェックをする。
これをやるとXMLにはPackageタグ内に下の内容が追加される。

<Capabilities>
  <uap:Capability Name="removableStorage" />
</Capabilities>

それから保存するファイルのファイルタイプを登録しないといけない。
マニフェストのDeclarationsタブを選んで、Available Declarations:からFile Type Associationsを選択してAdd。
右側に色々と項目が出てくるけど、必須なのは赤い×印のとこ。(NameとFile Type)
テキストファイルを保存したいから、
Name: text
File type: .txt
とする。
これをやるとXMLにはApplicationタグ内に下の内容が追加される。

<Extensions>
  <uap:Extension Category="windows.fileTypeAssociation">
    <uap:FileTypeAssociation Name="text">
      <uap:SupportedFileTypes>
        <uap:FileType>.txt</uap:FileType>
      </uap:SupportedFileTypes>
    </uap:FileTypeAssociation>
  </uap:Extension>
</Extensions>

これでとりあえず、準備は完了。後はコード。
USBメモリを使いたいからリムーバブルメディアの一覧を取得する。

	//----- 接続されてるリムーバブルメディアの一覧を配下に持つStorageFolderを取得
	// デスクトップの「PC」を開いて並んでるドライブがリムーバブルディスクだけになってる感じ
	StorageFolder sf = Windows.Storage.KnownFolders.RemovableDevices;
	//----- リムーバブルメディアの一覧を取得
	IReadOnlyList<StorageFolder> listSf = await sf.GetFoldersAsync();

上のコードで対象のUSBメモリを含む一覧が取得できたから、後はStorageFolderのNameとかDisplayNameプロパティを見て使いたいドライブを選べばOK。
更にドライブ内のフォルダを選択するには

	//----- 例えばリストの2番目が使いたいUSBメモリの場合
	StorageFolder sfUsb = listSf[1];
	//----- USBメモリルートのフォルダ一覧を取得
	IReadOnlyList<StorageFolder> listUsb = await sfUsb.GetFoldersAsync();

とやればいい。同様に更にフォルダ階層をもぐっていくなら

	//----- リストの3番目のフォルダを選択する場合
	StorageFolder sfNext = listUsb[2];
	//----- その下のフォルダ一覧
	listUsb = await sfNext.GetFoldersAsync();

と繰り返していく。で、ファイル書込みをしたいフォルダにたどり着いたら

	//----- リストの2番目のフォルダを保存フォルダにしたい場合
	//----- 対象のStorageFolderを保持
	m_storageFolder = listUsb[1];
	//----- FolderPickerの時と同様、これをやっとかないとアクセスできない
	Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("PikcedFolder", m_storageFolder);

これで選択完了。ファイル書込みは上のPCの場合で書いたのと同じでいける。

選択方法が判ったので、適当なUIを作ってみる。画面(ページ)上には

  • ListBox lbFolder
  • Button btnSet
  • Button btnWrite

があるだけ。

namespace XXXXXXXX
{
	public sealed partial class TestPage : Page
	{
		private StorageFolder m_storageFolder;
		private Stack<StorageFolder> m_stackStorageFolder;
		private IReadOnlyList<StorageFolder> m_listStorageFolder;
		
		public TestPage()
		{
			this.InitializeComponent();
			m_storageFolder = null;
			m_stackStorageFolder = new Stack<StorageFolder>();
			m_listStorageFolder = null;
		}
		//----- ページロードイベントハンドラ
		// リムーバブルメディアの一覧を表示する
		private async void Page_Loaded(object sender, RoutedEventArgs e)
		{
			m_storageFolder = Windows.Storage.KnownFolders.RemovableDevices;
			m_stackStorageFolder.Clear();
			m_listStorageFolder = await m_storageFolder.GetFoldersAsync();
			DispFolders();
		}

		//----- リストボックスダブルタップイベントハンドラ
		private async void lbData_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
		{
			int iSelectedIndex = lbData.SelectedIndex;
			if (iSelectedIndex == -1)
			{
				return;
			}
			//----- リストの先頭がダブルタップされた場合、1つ上のフォルダへ移動
			// (リストの先頭には現在のカレントフォルダ名が表示されている。".."とか"↑"の方がいいかも)
			if (iSelectedIndex == 0)
			{
				await UpFolder();
			}
			//----- リストの2番目以降がダブルタップされた場合、選択されたフォルダへ入る
			else
			{
				await DownFolder(iSelectedIndex - 1);
			}
			//----- リスト表示更新
			DispFolders();
		}
		
		//----- 1つ上のフォルダへ移動
		private async Task UpFolder()
		{
			if (m_stackStorageFolder.Count == 0)
			{
				//----- トップ(PC)にいるから上に行けない
				return;
			}
			m_storageFolder = m_stackStorageFolder.Pop();
			m_listStorageFolder = await m_storageFolder.GetFoldersAsync();
		}
		
		//----- 選択されたフォルダへ移動
		private async Task DownFolder(int iFolderIndex)
		{
			//----- 今いるとこをスタックにPush
			m_stackStorageFolder.Push(m_storageFolder);
			//----- 選択されたStorageFolderを取得
			m_storageFolder = m_listStorageFolder[iFolderIndex];
			//----- 選択されたStorageFolder下のリストを取得
			m_listStorageFolder = await m_storageFolder.GetFoldersAsync();
		}
		
		//----- リスト表示更新
		private void DispFolders()
		{
			lbData.Items.Clear();
			if (m_stackStorageFolder.Count == 0)
			{
				//----- トップにいる場合、リスト先頭は[PC]にする
				lbData.Items.Add("[PC]");
			}
			else
			{
				//----- トップ以外の場合、カレントフォルダ名を表示
				lbData.Items.Add(string.Format("[{0}]", m_storageFolder.Name));
			}
			//----- 配下のフォルダを表示
			for (int iIndex = 0; iIndex < m_listStorageFolder.Count; iIndex++)
			{
				StorageFolder sf = m_listStorageFolder[iIndex];
				if (m_stackStorageFolder.Count == 0)
				{
					//----- カレントがトップの場合、DisplayNameも表示
					// DisplayNameはボリュームラベルになってるから判りやすい
					// Nameはドライブレター
					lbData.Items.Add(string.Format(" - [{0} / {1}]", sf.DisplayName, sf.Name));
				}
				else
				{
					//----- フォルダ名を表示
					lbData.Items.Add(string.Format(" - [{0}]", sf.Name));
				}
			}
		}

		//----- Setボタンクリックイベントハンドラ
		private void btnSetFolder_Click(object sender, RoutedEventArgs e)
		{
			//----- 現在選択中のフォルダを書込み対象として登録する
			Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("PikcedFolder", m_storageFolder);
		}

		//----- Writeボタンクリックイベントハンドラ
		private async void btnWrite_Click(object sender, RoutedEventArgs e)
		{
			await WriteFile();
		}

		//----- テキストファイル書込み
		private async Task WriteFile()
		{
			if (m_storageFolder == null)
			{
				//----- フォルダが選択されてない
				return;
			}
			//----- ファイル名
			DateTime dt = DateTime.Now;
			string strFilename = string.Format("{0}.txt", dt.ToString("yyyyMMdd-HHmmssfff"));
			//----- ファイルを作成
			StorageFile sf = await m_storageFolder.CreateFileAsync(strFilename, CreationCollisionOption.ReplaceExisting);
			//----- ストリームを取得
			var stream = await sf.OpenAsync(FileAccessMode.ReadWrite);
			//----- 出力ストリームを取得
			using (var outputStream = stream.GetOutputStreamAt(0))
			{
				//----- データライタを取得
				using (var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream))
				{
					dataWriter.WriteString("ABCDEFG\r\n");
					await dataWriter.StoreAsync();
					await outputStream.FlushAsync();
				}
			}
			stream.Dispose();
		}
	}
}

ま、細かいところは色々あるが(主にエラー処理)、とりあえずこんな感じで何とかなりそう。

[Raspberry Pi] Windows10 IoT Coreで動作するアプリの開発環境

RaspberryPi3にWindows10 IoT Coreをインストールして、そこで動作するアプリ(ユニバーサルWindowsアプリ)を開発する為の方法を忘れない様にメモ。

※ PCのOSは、Windows10じゃないとダメ。

(1) RaspberryPi3にWindows10 IoT Coreをインストール

 これは、色んなとこに書いてあるから割愛。
 または、後で書く。

(2) Visual Studioの設定

  1. Visual Studio 2015をインストールする。
  2. インストール済みの場合はコンパネのプログラムと機能からVisual Studio 2015を選んで「変更」。
  3. インストールする機能を選択する画面で「ユニバーサルWindowsアプリ開発ツール」を選択。

(3) Windows10 IoT Core Dashboardのインストール

https://developer.microsoft.com/en-us/windows/iot/docs/iotdashboard
からインストーラをダウンロードしてインストール。

(4) Windows IoT Remote Clientのインストール

https://www.microsoft.com/en-us/store/p/windows-iot-remote-client/9nblggh5mnxz
ストアからインストール。

こんなとこかな。

都道府県 Free V3.0 公開

ちょうど、世界地図と中国地図を申請したくらいのタイミングで、申請済みだった都道府県が「In Review」になりました。
早い!
そして今日の午前中、「Ready for Sale」となりました。

レビューめちゃくちゃに早いなぁ。
最近はこんな感じなんだろうか。

とにかく無事公開されて一安心。
一通り、公開中アプリのiOS10対応が済んだらアップデートしてこうと思います。