Mastodon

酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

デスクトップアプリからWinRT APIを使用する

本当は、ネイティブコードをWinRTへ移植して使えるようにするまでをやろうかと思っていたのですが、どう考えても許可がでそうになかったので止めました。

Windows 8 Store apps Advent Calendar」の第4日目です。他の方とネタが被るとダメだと思いまして考えた結果、きっと誰とも被らない少し異色なネタをしてみたいと思います。

デスクトップアプリからWinRT APIを使ってみます。実際のコードはそんなにパッとするものではないのですが、実際にWinRT APIを使えるようになるまでの準備が少しややこしいのでまとめてみました。

まずは、プロジェクトを作成する

デスクトップアプリと聞いてパッと思いついたのは、WinFormsでしたので適当にプロジェクトを作成します。ここでは言語にC#を選択しています。

プロジェクトが作成されましたら、プロジェクト一式を保存します。直接、csprojファイルを変更したいので、念のためVisual Studioを終了させておきます。

.csprojファイルを更新する

デフォルトのプロジェクト設定では、「動作する.NET Frameworkのバージョン」については指定されていますが、Windows 7などの過去のバージョンのWindowsでも動くようにと、「動作するプラットフォームのバージョン」まで指定はされていません。

このため、これから参照するdllアセンブリは参照される候補としてピックアップされません。WinRT APIは現在のところWindows 8でしか使えませんので、まずは「動作するプラットフォームのバージョン」を8に限定する必要があります。

具体的にどうするのか?……以下のTargetPlatformVersionタグを追加します。

<TargetPlatformVersion>8.0</TargetPlatformVersion>

.csprojファイルをエディタ等で開いて、下記のように直接タグを追加します。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{81A6921D-E3DA-4954-A4F5-60260307D0F2}</ProjectGuid>
    <OutputType>WinExe</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>WindowsFormsApplication1</RootNamespace>
    <AssemblyName>WindowsFormsApplication1</AssemblyName>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <TargetPlatformVersion>8.0</TargetPlatformVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>

DLLアセンブリを参照する

Visual Studioを起動して、WindowsFormsApplication1.sln(冒頭で作成したソリューション)を開きます。

ソリューションエクスプローラーからWindowsFormsApplication1を選択して、[参照設定]を右クリックします。コンテキストメニューが表示されますので、[参照の追加]を選択します。

「参照マネージャー」ダイアログの左側のメニューに[Windows]が追加されていると思いますので、選択して「Windows.winmd」を追加してください。

続いて、「参照マネージャー」ダイアログの左にある[アセンブリ]を選択し、[参照]ボタンをクリックします。以下のアドレスから直接アセンブリを参照してください。

  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\System.Runtime.WindowsRuntime.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.Tasks.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.IO.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.InteropServices.WindowsRuntime.dll

WinRT APIを使ってみる

WinRT APIを使ってみましょう。実行する際のトリガーはボタンのClickイベントにしましょう。Windowsフォームに適当にボタンを追加してください。ダブルクリックでClickイベントハンドラを追加しておいても構いません。

Form1.csの頭のusingディレクティブに以下の名前空間を追加します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Runtime.InteropServices.WindowsRuntime; // 追加

ドキュメントライブラリからファイルを取得する

WinRT特有の処理が思いつかなかったので、以前にも紹介したことのある特定のディレクトリに含まれているファイル一覧を取得するサンプルコードです。

private async void button1_Click(object sender, EventArgs e)
{
    var folder = Windows.Storage.KnownFolders.DocumentsLibrary;
    var files = await folder.GetFilesAsync();
    foreach (var file in files)
    {
        System.Diagnostics.Debug.WriteLine(file.Name);
    }
}

出力ウィンドウを見ると、WinFormsアプリからも問題なく一覧が取得できていることが確認できます。

デスクトップアプリからトースト通知をおこなう

あと、頑張ればデスクトップアプリからトースト通知をおこなうこともできちゃいます*1

private void button2_Click(object sender, EventArgs e)
{
    // テンプレートのタイプを取得
    var template = ToastTemplateType.ToastText01;
    // テンプレートを取得(XMLDocument"
    var toastXml = ToastNotificationManager.GetTemplateContent(template);
    // textタグを取得(ここに文字列が入る)
    var textTag = toastXml.GetElementsByTagName("text").First();
    // 子要素に文字列を追加
    textTag.AppendChild(toastXml.CreateTextNode("こんにちは通知"));

    // Notifierを作成してShowメソッドで通知
    var notifier = ToastNotificationManager.CreateToastNotifier(ApplicationID);
    notifier.Show(new ToastNotification(toastXml));
}

上記のコードを実行すると、下図のようにトースト通知されることが分かります。

蛇足ですが、何故「頑張れば…」とお茶を濁したかというと、トースト通知はコードを実行するだけでは表示されません(エラーも発生しません)。まずはスタート画面へのショートカットの追加と、追加したショートカットのプロパティに対してAppUserModeIdを設定しておく必要があります。

この処理がWindows APIを使用しているため、.NETなWinFormsアプリで実行するには、「Windows API Code Pack for Microsoft .NET Framework」が必要になります。この辺りの説明が面倒くさい複雑になってしまうと思いましたので、省略させて頂きました。

もし、ご興味があれば以下のサンプルコードが一番シンプルにまとまっていると思いますのでご参考ください。

*1:トースト通知をおこなう処理はこちらを参考にさせて頂きました