2014/11/30

TokyoSubwayView

TokyoSubwayViewというWindowsストアアプリをリリースしています。「東京メトロオープンデータ活用コンテスト用公共交通データAPI」(長い……)を利用するアプリで、東京メトロオープンデータ活用コンテストに応募しています。

1. 機能


ごくシンプルに、東京メトロ地下鉄路線内の列車の運行状況をグラフィカルに表示します。

各駅の位置関係は実際の地理的な位置に沿っているため、都心部では密集して見にくくなっていますが、表示する路線と優先度(優先度の高いものが上に来る)をオプションで設定できます。

駅と線路の色は列車の運行状況を示します(各線のラインカラーとは別)。
  • 青 : 遅れなく運行中
  • オレンジ : 5分以上遅れて運行中
  • マゼンタ : 15分以上遅れて運行中
  • 黄緑: 列車予測線(線路のみに、隣接した駅にその方向に走る列車が停車中のときに出る)
駅か線路をクリック/タップするとそこにいる列車の情報が表示されます。

ここで謝っておきますと、現在Windowsストアに出ているのはVer 1.1で、一部バグがあります(初期状態で表示される路線の優先度がオプションと逆になっているなど)。Ver 1.2で大体解消したと思いますが、コンテストのルールによりコンテスト終了(来年1月以降)まで修正ができないため、そのままになっています。上の画像はVer 1.2のものです。

2. 開発


このアプリの開発は@ch3coohさんのブログを見て意外と難しくなさそうと思ったところから始まりました。
そこから何を作るか考えて、要素技術を確認して、行けそうだと思ったのが10/19のOSC Tokyo/Fallの直前。11/17の締め切りまでWindowsストアの審査に1週間を見込むとして、最大3週間あれば形になるだろうと取り掛かったものの、WinRTではWPFで使ったテクニックが使えない場所が多々あって時間を取られるうちに最後の1週間を切り、既に諦めが入りつつも見切りでまとめたのが11/15のめとべや東京#6の朝(それでもこの期間でできたのは@biacさんの多数の記事のおかげ)。懇親会で「もう間に合わないんですけどね……」と言いつつ見せたら@openlibsysさんに後押しいただいて、残る問題を一応潰して全部動くようになったのが11/17の早朝。プライバシーポリシーは@snow_caitさん情報でGistで行くことにして、開発者登録からストア審査提出までどたばたと済ませて、自分ではこれで一区切り付けて終わり、のつもりだったんですが……

なあにーーーーーーーーーーーーーーーーーーーーっ

認定まで25分、公開まで1時間11分しかかかりませんでした。ストアの説明では審査には何段階かあって合わせて1週間ほどかかる感じでしたが、現在では短縮されたようです……。

ということで奇跡的にコンテストには間に合いましたが、後から見るとちらほら怪しい点が……。ただ、応募アプリをとにかく出さないとアクセストークンが停止されるので、APIを利用すること自体できなくなるんですよね……。

ソースコードはGitHubに置きました。Ver 1.2でかなり修正しています。
ただし、アクセストークンは各開発者が管理することなっているので、入れていません。で、現在有効なアクセストークンは自分でも応募アプリを出した人のものだけなので、実際に動かす人はまずいないと思いますが、一応。

コンテスト自体については、審査に2箇月かけてその間のアプリ修正を禁止するとか、今時のアプリ開発のスピード感からして正直どうかという感もあり、とりあえず忘れます。

2014/10/05

ThinkPad X230とmSATA SSD

ThinkPad X230にmSATAのCrucial M550 256GBを入れた。
ThinkPad X230 and Crucial M550
ThinkPad X230 and Crucial M550

開発環境を色々入れて手狭になってきていたところに、Windows 10 TPをどうやって動かそうか考えて、元からmSATAに増設する予定だったので丁度よく。

Windows 10 TPのインストールはISOファイルからインストール用USBメモリを作って行ったが、X230をUEFIブートにしているので、USBメモリもUEFIブータブルにするためにFAT32でフォーマットする必要があった以外は従来と変わりない。

とりあえずインストールしたままの状態ではM550の温度は常に50度台前半で、かつてないペースで雫ちゃんの声を聞くことになった。

さすがに気になったので、IntelのChipset SupportとRapid Storageのドライバーを古いインボックスのものからLenovoの新しいものに代えたら、30度台後半で落ち着くようになった。

速度はmSATAの制限があるので、このぐらい。実用上は十分以上。

これでまたX230のスロットが一つ埋まった。

2014/10/04

Windows 10 TPのバージョン判定

Windows 10 TP(Technical Preview)のバージョン判定について一応確認してみた。

1. 判定方法


内部バージョンが6.4というのは分かっているので、これをC#のコードから判定する方法として以下を試してみた。
.NET FrameworkのSystem.Environment.OSVersionは内部的にGetVersion/GetVersionExを呼び出しているようなので、これに準じる。

なお、GetVersion/GetVersionExについては、Windows 8.1と同様にマニフェストファイルでcompatibilityの記述が必要となる。

2. コード


それぞれのコードは以下のとおり。

GetVersionEx
public static Version GetOsVersionByGetVersionEx()
{
  var info = new OSVERSIONINFOEX();
  info.dwOSVersionInfoSize = (uint)Marshal.SizeOf(info);

  var result = GetVersionEx(ref info);

  return result
    ? new Version((int)info.dwMajorVersion, (int)info.dwMinorVersion)
    : null;
}
マニフェストファイルのcompatibilityにWindows 10のIDを含めている。
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <application>
    <!-- The ID below indicates application support for Windows 7 -->
    <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>

    <!-- The ID below indicates application support for Windows 8 -->
    <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>

    <!-- The ID below indicates application support for Windows 8.1 -->
    <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>

    <!-- The ID below indicates application support for Windows 10 -->
    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
  </application>
</compatibility>
RtlGetVersion
public static Version GetOsVersionByRtlGetVersion()
{
  var info = new OSVERSIONINFOEX();
  info.dwOSVersionInfoSize = (uint)Marshal.SizeOf(info);

  var result = RtlGetVersion(ref info);

  return (result == 0) // STATUS_SUCCESS
    ? new Version((int)info.dwMajorVersion, (int)info.dwMinorVersion)
    : null;
}
NetWkstaGetInfo
public static Version GetOsVersionByNetWkstaGetInfo()
{
  IntPtr buff = IntPtr.Zero;

  try
  {
    var result = NetWkstaGetInfo(null, 100, out buff);

    if (result == 0) // NERR_Success
    {
      var info = (WKSTA_INFO_100)Marshal.PtrToStructure(buff, typeof(WKSTA_INFO_100));

      if (info.platform_id == 500) // PLATFORM_ID_NT
        return new Version((int)info.ver_major, (int)info.ver_minor);
    }
  }
  finally
  {
    if (buff != IntPtr.Zero)
      NetApiBufferFree(buff);
  }

  return null;
}
[修正]

IntPtrをNetApiBufferFreeで解放するよう修正した。

WMI
public static Version GetOsVersionByWmi()
{
  var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");

  var os = searcher.Get().Cast<ManagementObject>().FirstOrDefault();

  if ((os != null) && (os["OsType"] != null) && (os["Version"] != null))
  {
    if (os["OsType"].ToString() == "18") // WINNT
      return new Version(os["Version"].ToString());
  }

  return null;
}
VerifyVersionInfo

引数majorとminorでバージョン番号を指定し、それとの比較をする形。
public static bool? IsOsEqualOrNewerByVerifyVersionInfo(int major, int minor)
{
  var info = new OSVERSIONINFOEX();
  info.dwMajorVersion = (uint)major;
  info.dwMinorVersion = (uint)minor;
  info.dwOSVersionInfoSize = (uint)Marshal.SizeOf(info);

  ulong cm = 0;
  cm = VerSetConditionMask(cm, VER_MAJORVERSION, VER_GREATER_EQUAL);
  cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_GREATER_EQUAL);

  var result = VerifyVersionInfoW(ref info, VER_MAJORVERSION | VER_MINORVERSION, cm);

  if (result)
    return true;

  return (Marshal.GetLastWin32Error() == 1150) // ERROR_OLD_WIN_VERSION
    ? false
    : (bool?)null;
}
Win32の宣言を含むコード全体はレポジトリに置いた。

3. テスト結果


実機にインストールしたWindows 10 TP上でのテスト結果は予想通りで、どの方法でもバージョン判定はできている。

マニフェストファイル中のOSのIDをコメントアウトして変えてみると、

GetVersionExはサポートしている上限のOSのバージョン番号を返しているのが分かる。以上で確認終了。

4. 備考


MSDNのGetVersion/GetVersionExの説明にはWindows 8.1後のOSでは変更されるか利用できなくなる可能性があるのでVersion Helper APIを使うように書かれている。一方、C#の観点から言えば、Version Helper APIは要はC++のマクロだから使えないし、GetVersion/GetVersionExを使っているSystem.Environment.OSVersionはどうなるのかということになって、先行き不透明な状態にある。まあ様子を見て使える手の中から対応を考えるしかないと思う。

参考までに、Version Helper APIと同じ機能を.NET Framework用に書いた例。