Windows 8 では、プラットフォームを完全に刷新することによって、開発者の皆さんがそれぞれ知識を培ってきたプログラミング言語とテクノロジを使って、デバイスやフォーム ファクターに最適化されたアプリを開発できるようになりました。1 つのアプリ内でさらに簡単に複数の言語を使うための方法が Windows ランタイムです。たとえば、独自の Windows ランタイム コンポーネントを C++ で作成することで、Xbox 360 コントローラーと対話できる優れた Metro スタイル アプリを HTML と JavaScript で開発することができます。また、再利用可能な XAML コントロールを作成して Windows ランタイム コンポーネント経由で公開し、C++ と C# の両方で記述された Metro スタイル アプリですぐに利用することができます。妥協することなく自分の専門言語を使えるというのが、Windows 8 プラットフォームのアプリ開発が備える基本的な特長です。

このブログ記事では、独自の Windows ランタイム コンポーネントを作成するために必要な知識について説明します。

基本的事項

多様な言語の選択を可能にする中心的な存在が Windows ランタイムです。Windows ランタイム経由で公開することによって、JavaScript、C++、C#、Visual Basic から通常どおりの自然な方法で呼び出すことができます。この基盤を、独自の API を開発する場合にも利用できます。

皆さんが作成してアプリ内にパッケージ化する Windows ランタイム コンポーネントは、Windows 8 プラットフォームに既に組み込まれているファースト パーティ コンポーネントと区別して、一般にサード パーティ Windows ランタイム コンポーネントと呼ばれます。これらのサード パーティ Windows ランタイム コンポーネントを C++、C#、または Visual Basic で記述できます。これらが公開する API を、アプリにパッケージ化された別の Windows ランタイム コンポーネントを含むあらゆる場所から呼び出すことができます。さらに、Windows ランタイム コンポーネント経由で公開される API はあらゆる言語から呼び出すことができます。

アプリ用に作成する Windows ランタイム コンポーネントでは、Windows ランタイム API、Win32、COM、.NET API、サード パーティのライブラリなど、Metro スタイル アプリ開発でサポートされているものであれば何でも使用することができます。作成する Windows ランタイム コンポーネントは、API を公開する従来のいわゆる C++ DLL や .NET アセンブリとは違うという点に注意してください。.NET でのクラス ライブラリ作成や C++ でのスタンドアロン DLL の作成は Windows ランタイム コンポーネントを作成することとは異なります。Windows ランタイム コンポーネントは .wnmd ファイル内で宣言され、このファイルが Windows ランタイム メタデータを公開し、JavaScript などの言語が Windows ランタイム API を自然に使用できるようにしています (たとえば、JavaScript に公開される API における pascalCasedNames のサポート)。さらに、Windows ランタイム メタデータによって、IntelliSense のサポートを始めとする強力なツール機能を Visual Studio から提供できるようになります。

Windows ランタイム コンポーネントを独自に作成する理由

Windows ランタイム コンポーネントを作成することによって、再利用性と言語の相互運用性を活かした開発が実現します。。ここで紹介するいくつかのアプリのシナリオをとおして、サード パーティ Windows ランタイム コンポーネントを使ったよりよいエクスペリエンスの構築をご確認いただければと思います。

Metro スタイル アプリと Xbox 360 Controller for Windows の連携が可能
図 1 Xbox 360 のコントローラー:

Metro スタイル アプリで Win32 および COM API を使用する

Internet Explorer 10 のプラットフォームを活用することで、HTML、CSS、JavaScript を使った強力な Metro スタイル アプリを開発できます。では、HTML5 の Canvas を使ったゲームを開発した場合に、このゲームを Xbox 360 Controller for Windows と連携させたいときはどうすればよいでしょうか?コントローラーからの入力をアプリが受信できるようにする XInput API はありますが、この API が公開する Win32 API は JavaScript で直接利用できません。

この状況は、Windows ランタイム コンポーネントの作成によって問題を解決できることを示す最適な例であり、この解決策で HTML ベースの Metro スタイル アプリで XInput API を使えるようになります。「XInput と JavaScript のコントローラー スケッチのサンプル」(英語) では、まさにこの内容を説明しています。このサンプル アプリでは、C++ で記述されたゲーム コントローラー Windows ランタイム コンポーネントが、XInput API が公開する機能をラップしています。この HTML ベースのコントローラー スケッチ アプリは、C++ によるゲーム コントローラー Windows ランタイム コンポーネントを使うことで Xbox 360 コントローラーとの対話式操作を実現しています。

HTML と JavaScript だけでは成功しないこのシナリオは、サード パーティ Windows ランタイム コンポーネントの作成でしか解決されない複雑な処理を、サード パーティ Windows ランタイム コンポーネントで解決する絶好の例です。

コンピューティング負荷の高いオペレーション

科学、エンジニアリング、地図/地理情報などの分野で開発されるアプリでは、多くの場合コンピューティング負荷の高いオペレーションが発生します。こうした高負荷なオペレーションの多くで強力な並行処理が必要になりますが、必要なパフォーマンスを達成するにあたって C++ は非常に適しています。「JavaScript と C++ による Metro スタイル アプリ、Bing マップ トリップ オプティマイザーの開発」(英語) では、C++ による Windows ランタイム コンポーネントの作成がもたらす最適なアプリ エクスペリエンス実現のもう 1 つのシナリオを確認することができます。

同じ計算を Bing サーバーのクラウドで実行できるにもかかわらず、旅行の経路をローカル データで計算するというコンピューティング負荷の高いタスクを、なぜローカル データを使って実行するのか疑問に感じると思います。Bing マップはこの計算のために JavaScript API を公開しますが、アプリのオフライン実行が必要になる場合があります。さらに、ユーザーがタッチ ジェスチャを使ってリアルタイムで経路を変更できるようにする必要があります。こうしたコンピューティング負荷の高いオペレーションをローカルで実行できれば、エクスペリエンスはさらに優れたものになります。

コンピューティング負荷の高いオペレーションを並列タスク ライブラリを使って C++ で記述することで、クライアントの性能を活用して、優れたユーザー エクスペリエンスを構築できます。Windows ランタイムはこのシナリオに非常に適しています。Bing マップ AJAX コントロールを備えたリッチ クライアント ユーザー インターフェイス (UI) を HTML と JavaScript で開発しながら、経路計算の高負荷なオペレーションは、並列化を使った瞬時の計算を提供する C++ のコードを使って実行できます。

ライブラリ

私たちのコミュニティにはさまざまな優れたライブラリが数多くの開発者から寄せられ、だれでも使えるように公開されています。かつては、アプリを実装したプログラミング言語とライブラリの言語が同じでなければライブラリの再利用が困難になる場合がありました。たとえば、強力な .NET アプリを開発しても、C++ で記述されたライブラリを使用するには、PInvoke などの相互運用手段を使った複雑な手間が必要でした。

Windows ランタイムがあれば、Windows 8 におけるこうした言語のギャップを解消し、1 つのコード ベースによる 1 つの Windows ランタイム コンポーネント ライブラリを、コンポーネントの言語やアプリの中心となるプログラミング言語に関係なく幅広い開発者に提供できるようになります

現在は、Windows ランタイムによって公開されるカスタム コントロールの XAML ライブラリを 1 つ作成するだけで、C++ と C# 両方の開発者がこれを利用できるようになりました。さまざまな開発者に共有されている各種データ ストレージ Windows ランタイム ライブラリを、自分の XAML または HTML ベースの Metro スタイル アプリで使用することも可能です。これらすべてのシナリオは、相互運用のためのコードを記述する手間をかけずに実現されます。

私たちは、ライブラリを開発する開発者と、それを幅広く共有する Metro スタイル アプリ開発者のコミュニティにとって Windows ランタイムが大きなメリットだと考えています。では次に、C++/CX (英語) と C# を使ってサード パーティ Windows ランタイム コンポーネントを作成する基本的な手順をすべて再現した 2 つの例を見ていきましょう。

シナリオ 1: ネイティブ オーディオでアプリを強化する

基盤となるアプリ ロジックが C# で記述されたソフトウェア シンセサイザー アプリを XAML を使って作成しているとします。この音楽用アプリでフィルターをサポートするために、XAudio を使ってオーディオ バッファーを直接制御できるようにしたいと考えています。

Windows ランタイム コンポーネントをソリューションに追加する

Visual Studio を使って新しい C++ Windows ランタイム コンポーネント プロジェクトを既存のソリューションに追加します。この Windows ランタイム コンポーネントに音楽処理機能がラップされます。

Visual Studio を使って新しい C++ Windows ランタイム コンポーネントを音楽アプリに追加する
図 2: 新しい C++ Windows ランタイム コンポーネントの追加

API を公開するための C++ プロジェクトが Visual Studio によって作成されます。この API 用の実装は DLL にパッケージ化され、Windows ランタイム メタデータは .winmd ファイルにパッケージ化され、これらの両方を C# プロジェクトで利用することができます。

XAML C# プロジェクトに公開されるクラスを定義する

ここでは、C# プロジェクトに公開される API を C++/CX (英語) を使って作成しますが、Windows ランタイム C++ テンプレート ライブラリ (WRL) を使うことも可能です。最初は、XAudio の機能をカプセル化する非常に基本的なクラスを定義します。

XAudioWrapper.h

#pragma once

#include "mmreg.h"
#include <vector>
#include <memory>

namespace XAudioWrapper
{
public ref class XAudio2SoundPlayer sealed
{
public:
XAudio2SoundPlayer(uint32 sampleRate);
virtual ~XAudio2SoundPlayer();

void Initialize();

bool PlaySound(size_t index);
bool StopSound(size_t index);
bool IsSoundPlaying(size_t index);
size_t GetSoundCount();

void Suspend();
void Resume();

private:
interface IXAudio2* m_audioEngine;
interface IXAudio2MasteringVoice* m_masteringVoice;
std::vector<std::shared_ptr<ImplData>> m_soundList;
};
}

最初に気付くのは、クラス定義で使われている publicrefsealed の各キーワードです。Metro スタイルの JavaScript や C# などの別の言語からクラスをインスタンス化するには、クラスを public ref class の sealed として宣言する必要があります。

クラスの共通の機能 (メソッド、プロパティなど) は、C++ の組み込み型または Windows ランタイム型に制限されます。これらの型のみが、Windows ランタイム コンポーネントにおいて言語の境界を越えることができます。ただし、このコード スニペットのように、クラスのプライベート データ メンバーには通常の C++ ライブラリ (標準テンプレート ライブラリ コレクション) を使うことができます。これらのプライベート データ メンバーは、言語の境界を越える際に伴う規則に従う必要はありません。サポートされていない構成要素が使用された場合、Visual Studio のコンパイラはエラー メッセージを生成してガイダンスを表示します。

公開されるクラスを Windows ランタイム コンポーネントに実装する

クラスの基本的なインターフェイスを定義した後は、実装されるメソッドをいくつか確認します。

XAudioWrapper.cpp

XAudio2SoundPlayer::XAudio2SoundPlayer(uint32 sampleRate) :
m_soundList()
{
// Create the XAudio2 engine
UINT32 flags = 0;

XAudio2Create(&m_audioEngine, flags);

// Create the mastering voice
m_audioEngine->CreateMasteringVoice(
&m_masteringVoice,
XAUDIO2_DEFAULT_CHANNELS,
sampleRate
);
}

void XAudio2SoundPlayer::Resume()
{
m_audioEngine->StartEngine();
}

bool XAudio2SoundPlayer::PlaySound(size_t index)
{
//
// Setup buffer
//
XAUDIO2_BUFFER playBuffer = { 0 };
std::shared_ptr<ImplData> soundData = m_soundList[index];
playBuffer.AudioBytes = soundData->playData->Length;
playBuffer.pAudioData = soundData->playData->Data;
playBuffer.Flags = XAUDIO2_END_OF_STREAM;

HRESULT hr = soundData->sourceVoice->Stop();
if (SUCCEEDED(hr))
{
hr = soundData->sourceVoice->FlushSourceBuffers();
}

//
// Submit the sound buffer and (re)start (ignore any 'stop' failures)
//
hr = soundData->sourceVoice->SubmitSourceBuffer(&playBuffer);
if (SUCCEEDED(hr))
{
hr = soundData->sourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
}

return SUCCEEDED(hr);
}

このコード スニペットでは、Metro スタイル アプリ開発向けに提供されている XAudio2 COM API を使って、オーディオ エンジンの接続、サウンドの再生、エンジンの再開を実行しているだけです。これらに加えて、Windows ランタイム型だけではなく C++ の構成要素と型を使って必要な機能を実装できます。

Windows ランタイム コンポーネントを追加して使用する

基本的なクラスを定義、実装した後は、Visual Studio を使って、XAudioWrapper Windows ランタイム コンポーネントを C# プロジェクトから C++ プロジェクトに追加します。

Visual Studio を使って XAudioWrapper Windows ランタイム コンポーネントへの参照を音楽アプリに追加する

図 3: XAudioWrapper Windows ランタイム コンポーネントの音楽アプリへの追加

これによって、C++ プロジェクトから公開したクラスを C# プロジェクトで利用できるようになります。

MainPage.cs

using XAudioWrapper;

namespace BasicSoundApp
{
public sealed partial class MainPage : Page
{
XAudio2SoundPlayer _audioPlayer = new XAudio2SoundPlayer(48000);
public MainPage()
{
this.InitializeComponent();
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
_audioPlayer.Initialize();
}

private void Button_Click_1(object sender, RoutedEventArgs e)
{
_audioPlayer.PlaySound(0);
}
}
}

このコード スニペットでご覧いただけるように、C# から XAudio ラッパーへの対話は、通常の .NET コンポーネントの場合と同じように実行されます。名前空間を参照し、コンポーネントをインスタンス化して、公開される各種のメソッドを起動しています。これらすべてについて、ネイティブ コードを呼び出すための DllImport はまったく必要ありません。

シナリオ 2: 組み込みの API を使ってアプリから zip ファイルを開く

もう 1 つの例として、HTML を使ってファイル ビューアー アプリを開発していて、ユーザーが zip ファイルを選択できるような機能をこのアプリに追加する場合を考えてみます。ここでは、既に Windows に組み込まれている API と、.NET プラットフォームに公開されている zip ファイル処理用の API を使用します。

Windows ランタイム コンポーネントをソリューションに追加する

手順は音楽アプリで説明した内容と同じですが、zip 処理機能をラップする今回の目的には、C# Windows ランタイム コンポーネントを選択します。

Visual Studio を使って新しい C# Windows ランタイム コンポーネントをファイル ビューアー アプリに追加する
図 4: 新しい C# Windows ランタイム コンポーネントの追加 

API を公開するための C# プロジェクトが Visual Studio によって作成されます。この API 用の実装と Windows ランタイム メタデータは .winmd ファイルにパッケージ化され、Web プロジェクトで利用することができます。

公開されるクラスを Windows ランタイム コンポーネントに実装する

ここでは、Web プロジェクトに公開される API を C# を使って作成しますが、Visual Basic を使うことも可能です。最初は、zip の機能をカプセル化するシンプルな C# クラスを定義します。

ZipWrapper.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage;

public sealed class ZipWrapper
{
public static IAsyncOperationWithProgress<IList<string>, double> EnumerateZipFileAsync(StorageFile file)
{
return AsyncInfo.Run(async delegate(
System.Threading.CancellationToken cancellationToken, IProgress<double> progress)
{
IList<string> fileList = new List<string>();
progress.Report(0);

using (var stream = await file.OpenStreamForReadAsync())
{
using (var archive = new ZipArchive(stream))
{
for (int i = 0; i < archive.Entries.Count; i++)
{
// add code for processing/analysis on the file
// content here

// add to our list and report progress
fileList.Add(archive.Entries[i].FullName);
double progressUpdate = ((i + 1) / ((double)archive.Entries.Count)) * 100; // percentage
progress.Report(progressUpdate);
}
}
}

progress.Report(100.0);
return fileList;
});
}
}

このクラスは、public であり sealed です。C++ Windows ランタイム コンポーネント作成の場合と同じように、別の言語がクラスをインスタンス化するにはこれが必要です。クラスで公開される静的メソッドは、メソッド シグネチャで Windows ランタイム型 (StorageFile など) と .NET 型 (IList など) を混在させて使用しています。通常は、別の言語に公開されるパブリック フィールド、パラメーター、戻り値の型の定義には Windows ランタイム型を使うのが適切なようです。ただし、.NET の特定の基本型 (DateTimeOffset と Uri) とプリミティブ (IList) はそのまま使うことができます。

また、上記のメソッドでは、非同期と進捗状況をサポートする Windows ランタイム インフラストラクチャを活用していることがおわかりいただけると思います。このインフラストラクチャは Windows ランタイム コンポーネントを定義する場合に使うことができ、また使うことが必要です。Windows ランタイム コンポーネントまたはクラス内のプライベートな機能の実装の範囲では、Windows ランタイム型および API しか使用できないという制限はありません。ZipArchive API のコード スニペットのように、Metro アプリ開発用に公開される .NET API サーフェイス (英語) を自由に使うことができます。

Windows ランタイム コンポーネントを追加して使用する

zip ツールのラッパーを実装した後は、Visual Studio を使って JavaScript プロジェクトから C# プロジェクトへの参照を追加します。

Visual Studio を使って ZipUtil Windows ランタイム コンポーネントへの参照をファイル ビューアー アプリに追加する
図 5: ZipUtil Windows ランタイム コンポーネントのファイル ビューアー アプリへの追加

これによって、C# プロジェクトから公開したクラスを Web プロジェクトで利用できるようになります。

program.js

function pickSinglePhoto() {

// Create the picker object for picking zip files
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
openPicker.fileTypeFilter.replaceAll([".zip"]);

// Open the picker for the user to pick a file
openPicker.pickSingleFileAsync().then(function (file) {
if (file) {
ZipUtil.ZipWrapper.enumerateZipFileAsync(file).then(
function (fileList) {
for (var i = 0; i < fileList.length; i++)
document.getElementById('output').innerHTML += " " + fileList[i];
},
function (prog) {
document.getElementById('zipProgress').value = prog;
}
);
} else {
document.getElementById('output').innerHTML = "an error occurred";
}
});
};

ご覧いただけるように、JavaScript から zip ツール ラッパーへの対話は、通常の JavaScript オブジェクトの場合と同じように実行されます。Windows ランタイム コンポーネントで公開される静的メソッドを呼び出すことができ、この呼び出しに .then() のような JavaScript の非同期言語の構成要素を使うことができます。

基本のガイダンス

Metro スタイル アプリ用に記述するすべての API をサード パーティ Windows ランタイム コンポーネントとして公開しなければならないわけではありません。一般的には、異なるプログラミング言語間の対話が必要な場合に Windows ランタイム型を使用し、Windows ランタイム コンポーネントで公開されていない機能のために使用している言語がある場合に、その言語に組み込まれた型と構成要素を使用します。さらに、独自の Windows ランタイム コンポーネントを作成する場合には、言語の境界を越える際に伴う、各種言語に固有のさまざまな機能や規則を考慮する必要があります。これには、デリゲートとイベント、非同期処理、メソッド過負荷、コレクションなど特定のデータ型の処理、例外処理、独自のデバッグ処理などがあります。このトピックの詳細については、使用する開発言語での Windows ランタイム コンポーネントの開発 (英語) に関するセクションを参照してください。

終わりに

Windows ランタイム コンポーネントによって、複数のプログラミング言語と API テクノロジを組み合わせて思いどおりのアプリを実現することができます。Windows 8 は妥協を求めることがありません。そして、これを開発作業にも適用し、開発者固有のシナリオに最適なプログラミング言語を自由に組み合わせられるようにしています。私たちは、これにより皆さんが、今まで接したことのないプログラミング言語について学習する労力をイノベーションに向けていただけるようになると考えています。

これからも、皆さんのすばらしいアプリ構築をサポートしていければと思います。

-- Windows プログラム マネージャー、Ines Khelifi

参照情報

この記事で紹介した内容は、Windows ランタイム コンポーネントの可能性のほんの一部です。さらに以下のリソースをお読みいただくことで、アプリ開発を進めるにあたってさらに詳しい情報を入手できます。