xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

C# でインストールされている Windows Store App の一覧を取得する

とある先輩に「Windows Store のアプリ一覧を取得したいんだけど、やり方知らない?」と聞かれたのでやってみました。今回は、その方法と簡単な実装のメモの回。

最初から答えを授かりました

2 分ほど iPhone で調べて PowerShell コマンドで最低限できそうというところまでは当たりをつけていたのですが、C# から PowerShell コマンドを呼び出すのではない方法がいいなーと思っていました。と思っていたら C# MVP の大先輩である @matarillo さんがパッと答えを教えてくださいました!本当に感謝!

どうやら WMI (= Windows Management Instrumentation) という OS 管理の機能/技術を使えばできるみたいです。

実装

LINQPad で書いてみたのですが、ザッと以下のような感じで取得できました。WQL なるクエリ言語で書いて投げるだけの簡単なお仕事でした。ちなみに実行には管理者権限が必要です。

void Main()
{
    WindowsStoreApp.GetInstalled().Dump();
}

public sealed class WindowsStoreApp
{
    public string Architecture { get; }
    public string Language { get; }
    public string Name { get; }
    public string ProgramId { get; }
    public string Vendor { get; }
    public Version Version { get; }

    private WindowsStoreApp(PropertyDataCollection props)
    {
        this.Architecture = (string)props[nameof(Architecture)].Value;
        this.Language = (string)props[nameof(Language)].Value;
        this.Name = (string)props[nameof(Name)].Value;
        this.ProgramId = (string)props[nameof(ProgramId)].Value;
        this.Vendor = (string)props[nameof(Vendor)].Value;
        this.Version = Version.Parse((string)props[nameof(Version)].Value);
    }

    public static WindowsStoreApp[] GetInstalled()
    {
        // これで取り出せちゃう!簡単!
        const string wql = "select * from Win32_InstalledStoreProgram";
        using (var searcher = new ManagementObjectSearcher(wql))
        {
            return searcher
                .Get()
                .Cast<ManagementBaseObject>()
                .Select(x => x.Properties)
                .Select(x => new WindowsStoreApp(x))
                .ToArray();
        }
    }
}

手元の環境では結果は以下のようになっていて、この NameProgramId から所望のアプリがインストール済みかを判定できそうです。

f:id:xin9le:20190828023330p:plain

Microsoft Office の Application ID

例えば Microsoft Office の Windows Store 版が入っているかは、以下のサイトにある Application ID を参考にすると判定できるような気がしています。「気がしている」というのは、僕が Office 365 を契約していないので Windows Store 版をインストールできていないから、なだけなのです...(悪しからず

LINQPad.Controls でお手軽 GUI 操作

今頃気が付いたのですが、超カジュアル IDE の LINQPadLINQPad.Controls なる GUI 部品の名前空間が追加されていたので遊んでみました。以下のようなインタラクティブなことがお手軽にできるようになります。

f:id:xin9le:20190825152552g:plain

使い方

使い方はかなり簡単で、C# で WinForms / WPF / UWP などのデスクトップアプリを作ったことがある方にとってはお馴染みの方法です。TextBoxButton などのインスタンスを new して、イベント処理などをコードで表現するだけです。唯一違うのは GUI 部品を .Dump() すること。これで結果画面に GUI 部品が表示されるようになります。

var textBox = new TextBox("This is initial text.").Dump();  // TextBox 表示
var button = new Button("Execute").Dump();  // Button 表示
button.Click += (sender, e) =>
{
    $"Hello, my name is {textBox.Text}.".Dump();
};

サンプル

LINQPad をインストールすると、LINQPad.Controls のサンプルが結構含まれているので参考になります。その中に正規表現評価のサンプルがありました。以下のようなこともチョイチョイっとコードを書くとできちゃうみたいです。便利!

f:id:xin9le:20190825160839g:plain

LinkGenerator : ASP.NET Core の DI で利用可能な URL 生成機構

ASP.NET Core でリダイレクト機能を持つ Action Filter を作っていたときのこと。Controller や View で利用できる IUrlHelper を Action Filter で利用できないことに気が付きました。Controller だと以下のように Url プロパティで IUrlHelper にアクセスできるので簡単ですが、Action Filter には IUrlHelper にアクセスするためのプロパティなどがどこにもありません。これでは特定の Action に対するルーティングを考慮した URL の生成ができない!うーん、困った。

// Controller だとこう書けるから簡単だけど
public class ApplicationController
{
    [HttpGet("{market}/app/{id}")]
    public IActionResult Index([FromQuery]string id)
    {
        var action = "Index";
        var controller = "Application";
        var routeValues = new { market = "jp", id = "abc123" };
        var url = this.Url.Action(action, controller, routeValues);

        // url : "/jp/app/abc123"
    }
}

ドキュメントを読んでみる

IUrlHelper がない環境下でルーティングを考慮した URL の生成ができないかと調べていたら、公式ドキュメントにヒントがありました。

  • URL generation is based on addresses, which support arbitrary extensibility:

この英語の雰囲気的には以下のような感じかと思います。なるほど LinkGenerator を DI すれば良い、と。

  • LinkGenerator なる機能があり、それを DI を介して取得できる
  • LinkGenerator を使えないところには IUrlHelper が提供されている

使ってみる

簡単な認証フィルターを例に LinkGenerator 型を使ってみましょう。アカウントのサインインが必要なページに来たら認証画面にリダイレクトし、認証が終わったらページに戻す実装です。

public class AccountController
{
    [AllowAnonymous]
    public IActionResult SignIn(string returnUrl = "/")
    {
        // 認証済みなら即リダイレクト
        if (this.User.Identity.IsAuthenticated)
            return this.LocalRedirect(returnUrl);

        // リダイレクト先を決定して認証チャレンジ
        var props = new AuthenticationProperties { RedirectUri = returnUrl };
        return this.Challenge(props);
    }
}
public class AuthorizationFilter : IAuthorizationFilter
{
    private LinkGenerator LinkGenerator { get; }

    public AuthorizationFilter(LinkGenerator linkGenerator)
        => this.LinkGenerator = linkGenerator;

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // AllowAnonymous 属性が付いている場合はスキップ
        if (context.Filters.Any(x => x is IAllowAnonymousFilter))
            return;

        // 認証済みなら何もしない
        if (context.HttpContext.User.Identity.IsAuthenticated)
            return;

        // 認証ページにリダイレクト
        var returnUrl = context.HttpContext.Request.GetEncodedPathAndQuery();
        var routeValues = new { returnUrl };
        var url = this.LinkGenerator.GetPathByAction(context.HttpContext, "SignIn", "Account", routeValues);
        context.Result = new LocalRedirectResult(url);
    }
}

IUrlHelper と使い方が若干違いますが、概ね同じような感覚で使えますね。LinkGenerator のこと、ときどきでいいから...思い出してください。

Minecraft に C# からコマンド叩き込んで操作してみた

f:id:xin9le:20190708020622p:plain

最近日本マイクロソフトさんが #くらでべ という YouTube チャンネルをやっていて、その中で Minecraft の自動化を取り上げていました。誕生日もクリスマスも Minecraft の LEGO やグッズをせがむくらい Minecraft が大好き過ぎる娘がいるのですが、ここは自動化して娘に「お父さん、すごいじゃん!」と言わせてみたい!ということで、勉強も兼ねて真似してみました。

Minecraft 自動化の動画

以下が #くらでべ さんがやっていた動画です。@rioriost さんの説明は非常に分かりやすいですし、@chomado ちゃんの合いの手も面白くって、入門としては大変素晴らしいです。

今回僕が試したのは第 3 回までの範囲で、ここまでが重要です。それができればあとはプログラミングとしての応用なので、まずは第 3 回の内容までを押さえましょう。

この動画の中では Azure Notebooks というお手軽なプログラム実行環境から Python を使って Minecraft にコマンドを投げているのですが、僕は生粋の C#er なのでやっぱり C# でコマンドを投げたい!ということで C# で Minecraft のコマンドを投げて操作するところまでを簡単に紹介してみたいと思います。

Step.1 : オレオレ Minecraft サーバーを立てる

まず、好き勝手するためのオレオレサーバーを立てます。今回は先の動画で紹介されている方法に従って Azure 上にサーバーを立てる方向で進めてみますが、慣れている方 (?) はローカル環境にサーバーを立ててみても良いでしょう。今は Dockerfile とかの設定は大変に面倒なので、@rioriost さんが Docker Hub に公開しているコンテナイメージをありがたく利用させていただきます。

これを Azure Container Instances (ACI) にホストします。これまた @rioriost さんがご丁寧に Bash の Deploy Script を準備してくださっているので、ちょこっと書き換えて実行します。僕は冒頭を以下のように書き換えました。

#!/bin/bash

# Here you need to specify these parameters
readonly AZURE_ACCT="xin9le" 
readonly ACI_RES_LOC="japaneast"
readonly ACI_RES_GRP="${AZURE_ACCT}democraft"
readonly ACI_STR_SH_NAME="acishare"
readonly RCON_PASSWORD="すごく文字列です"
readonly ACI_CNT_NAME="${ACI_RES_GRP}-container"
readonly ACI_STR_AN="democraftstorage"

これを Azure CLI がインストールされた環境で実行すると Azure 上にサーバーができあがります。僕は Azure CLI をインストールするのが面倒だったので、Azure Portal に付属している Cloud Shell から叩きました。

f:id:xin9le:20190708033007p:plain

Step.2 : 接続確認

サーバーが出来上がったら接続確認をしてみましょう。執筆時点で構築したサーバーは v1.14.3 なので、v1.14.3 でプレイするのが良いのではないかと思います。

  1. Minecraft Java Edition (v1.14.3) を起動
  2. [マルチプレイ] - [ダイレクト接続] を開く
  3. [サーバーアドレス] に Azure Container Instances の IP アドレス or FQDN を入力
  4. [サーバーに接続] をクリック

内容に問題がなければこれで接続できます。僕の場合、サーバーアドレスは xin9ledemocraft-container.japaneast.azurecontainer.io となりました。

f:id:xin9le:20190708035640p:plain

Step.3 : C# からコマンドを投げてみる

最後に C# で Minecraft のコマンドを送信してみましょう。Minecraft のコマンドは RCON なるプロトコルでやりとりされるようです。単純な REST API じゃないので HttpClient ではダメで、古き良き (?) Socket プログラミングが求められます。とはいえ (当然ながら) 先駆者はいるので、ありがたく先人の作ったライブラリを使わせていただきましょう。今回は .NET Standard に対応している CoreRCON *1 というライブラリを使ってみます。

PM> Install-Package CoreRCON

以下のようなコードを書いて実行してみます。するとあら不思議!昼が夜になる!

async Task Main()
{
    var fqdn = "xin9ledemocraft-container.japaneast.azurecontainer.io";
    var ipAddresses = await Dns.GetHostAddressesAsync(fqdn);
    var ipAddress = ipAddresses.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork);
    ushort port = 25575;
    var password = "すごく文字列です";
    var command = "/time set night";  // テクマクマヤコン!夜にな〜れ!

    var connection = new CoreRCON.RCON(ipAddress, port, password);
    var result = await connection.SendCommandAsync(command);
    Console.WriteLine(result);
}

f:id:xin9le:20190708041011p:plain

調子に乗って昼と夜を 3 秒ごとにパッパッと切り替わるようにしてみましょう。特に意味のないイタズラですw

async Task Main()
{
    var fqdn = "xin9ledemocraft-container.japaneast.azurecontainer.io";
    var ipAddresses = await Dns.GetHostAddressesAsync(fqdn);
    var ipAddress = ipAddresses.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork);
    ushort port = 25575;
    var password = "すごく文字列です";
    var commands = new []{ "/time set day", "/time set night" };
    var connection = new CoreRCON.RCON(ipAddress, port, password);

    var isDay = true;
    while (true)
    {
        await Task.Delay(3000);
        var index = Convert.ToInt32(isDay);
        var command = commands[index];
        var result = await connection.SendCommandAsync(command);
        Console.WriteLine($"{DateTimeOffset.Now} - {result}");
        isDay = !isDay;
    }
}

まとめ

C# を使った Minecraft 自動化の第一歩として、Azure 上に Minecraft サーバーを立てて C# からコマンド発行するところまでやってみました。「#くらでべ の写経してみた」という薄っぺらい記事ですが、ここまでできれば後は C# で自由自在!子供たちから「すごい!」と言ってもらえること間違いナシですよ!

あ、途中の Deploy Script で立ち上げた Azure Container Instances はすごくお高いヤツなので、使い終わったら必ず停止しましょう!くれぐれもクラウド死しないようご注意ください。

*1:すでにメンテナンスはされていないらしい

超絶大盛況!de:code 2019 で「ドキドキ・ライブコーディング対決」をやってきました

去る 2019/5/29 (水) - 2019/5/30 (木) に日本マイクロソフトが主催する年次開発者イベント de:code 2019 が開催されました。大変ありがたいことに @chack411 さんこと井上章さんから登壇の依頼を受け、北陸のコミュニティではもはや毎年恒例になっている「ドキドキ・ライブコーディング対決」をやってきました@chack411 さんも過去に何度か僕たちのセッションを北陸の勉強会で見たことがあり、それが「面白くて忘れられない」とのことでのご依頼でした。本当に感謝!セッションは最終日の最終コマという大トリ!超緊張!

「ドキドキ・ライブコーディング対決ってなんやねん」という感じですが、C# 大好き Microsoft MVP による、C#er のための、ライブ C# プログラミング対決です。よくある登壇中のライブコーディングとは全く違います。一般的なライブコーディングは予めどういうコードを書くか決まっていて、実際にその場で試して見せるというものですが、僕たちのライブコーディングは事前にコードが準備されていません...!まじモンのガチライブコーディングですw

メンバーは以下の 4 人。写真右から紹介します。

f:id:xin9le:20190612003459j:plain

氏名 ハンドル名 役割
小島 富治雄 @Fujiwo 解答者
石野 光仁 @AILight 問題作成
司会進行
室星 亮太 @RyotaMurohoshi 解答者
鈴木 孝明 @xin9le 解答者

「役割」というところから気配を感じるかもしれませんが、「石野さんが事前に準備した問題」を「他の 3 人が制限時間内にその場で解く」というものです。この事前に準備された問題が、セッションで出題される瞬間まで「本当に」知らされません。だからその場でガチのライブコーディングになります。そんな破茶滅茶なセッションです。de:code でもチョークトーク枠として設定されている通りあくまでもエンタメ枠。単純な聴講型のセッションではなく、みんなで盛り上げて作る聴講者参加型のセッションです。自分たちは「プログラミング・エンターテインメント」と呼んでいるのですが、それくらいプログラミングをエンタメとして昇華させた唯一無二のコンテンツです。たぶん...!

ものすごい反響

会場でもみなさん盛り上がってくださいまして、外まで漏れ聞こえるほどの笑い声だったと後に伝え聞きました。参加者のみなさんのツッコミのおかげで本当に爆笑の渦でしたw イベント直後も Twitter や blog 記事などでかなりの反響をいただきました。以下にまとめてリンクを掲載しておきます。本当に感謝!(見落としがあったらごめんなさい...)

アンケート結果

アンケート結果もとてつもなく良く、Microsoft がイベントに際して取得/集計している NSAT (= ユーザー満足度) が以下のような結果でした。200 点満点の評価方法なので実は超すごい!

評価ポイント NSAT
講師 198
コンテンツ 193
全体 195

コメントもポジティブなものばかりいただきました。以下にいくつか抜粋します。コメントくださった方々、本当に励みになります。お忙しい中ご回答ありがとうございました!

  • 期待していなかったけれど、良い意味で裏切られました。とても盛り上がり、これを最終セッションに選んで良かったです!
  • de:codeで一番楽しい講演でした。 私は、まだまだプログラミングスキルが足らず、理解できないことも多かったですが、帰ってプログラミングしよう!と強く思えたので、とても良かったです。今週末は僕も令和つくります!!!
  • みなさん非常にスキルの高い方の熱いバトルが見られて面白かったです。Blazorでここまでできるのかと大変興味深かったです
  • エンターテインメントとしても、Blazor の PR としても最高でした。登壇者皆さんのスキルが高くないといずれも成り立たなかったと思います、また来年もやってください。

セッションお品書き

簡単にセッション中にやったことリストを挙げておきます。どうなったかは、上述の参加者レポートでお楽しみください。

  • 「ドキドキ・ライブコーディング」がどんなセッションなのか説明
  • 早速ライブコーディングのお題が出題される
    1. 西暦で入力した文字を和暦に変換せよ
    2. 和暦を「令和”元年”」で表示せよ
  • 「七並べ」アルゴリズム対決
    1. 事前に考えてきた各人のアルゴリズムを解説
    2. 各人が思う Blazor の良いところアピール
    3. 全員のアルゴリズムを同一プロジェクトに入れて実行 (= 勝敗を決める)

Blazor の可能性

僕は Blazor の良いところとして、Web でも C#/.NET を書けることで受けられる恩恵を実際に作ったサンプルを通して紹介しました。今回紹介したのは MVVM で作ったテトリス (のようなもの) です*1MVVM アーキテクチャに従って設計することで、WPF / Blazor を View にあたるレイヤーを変えるだけでそのまま動かせるといった構成になっています。GitHub でソースコードを公開していますので、参考にしてみてください*2

このサンプルでは ReactiveProperty を利用して変更通知をしているのですが、.NET Standard で作ると Windows デスクトップアプリも Web アプリも完全に同一のコードベースで動作させることができます。Web フロントエンドに JavaScript を利用していたらこんなことは簡単には実現できませんが、Blazor があれば C#/.NET で Web フロントエンドまで完全にカバーすることができます。

みなさんが作ってきた既存のコード資産、日々お世話になっているライブラリが Web フロントエンドでも動きます。そんな夢のような時代が本当に来た、というのを実際にお見せしたくてこのサンプルを作りました。

補足

Blazor は .NETer にとってすごい技術ではありますが、僕は JavaScript を覚えなくてもいいとは思っていません。.NET だけで Web フロントエンドを書くというのは世の中的には極めてニッチな領域です。「潰しが効かない技術力」になってしまうことは容易に想像できます。なので現実的な落とし所として Blazor でしかできないという状況は避け、JavaScript / TypeScript でもできるという前提で「敢えて得意な .NET を選んでいる」というのが良いチョイスでしょう。まだまだ超アーリーアダプターな範囲なので、僕自身もそういった開発に理解があり、利用を許される環境下でのみトライして行きたいと思っています。

まとめ

初めての de:code 登壇。GW などの準備期間中はとても不安でしたが、参加者のみなさんからのツッコミや笑い声に支えられ、結果として自分たちもとっても楽しむことができたセッションでした。また、いただいたアンケート結果やコメントがパワフルなので、もしかしたら (?) 来年も枠をいただけるかもしれません。もしそんなラッキーが起こったら、そのときはまたぜひご参加ください :)

登壇者からの記事/資料

*1:わかりやすさのためにテトリスとしています

*2:あくまで見栄えのするアプリを MVVM アーキテクチャとして作りたかっただけで、テトリスでなくても良かった