チュートリアル : C++ での基本 Windows ランタイム コンポーネントの作成および JavaScript による呼び出し
このチュートリアルでは、JavaScript、C#、または Visual Basic から呼び出すことのできるの基本的な Windows ランタイム コンポーネント DLL を作成する方法を示します。このチュートリアルを開始する前に、抽象バイナリ インターフェイス (ABI)、ref クラス、Visual C++ コンポーネント拡張などの概念を理解しておくなら、ref クラスの扱いが簡単になります。詳細については、「C++ での Windows ランタイム コンポーネントの作成」および「Visual C++ の言語リファレンス (C++/CX)」を参照してください。
C++ コンポーネント プロジェクトの作成
この例では、コンポーネント プロジェクトを最初に作成しますが、JavaScript のプロジェクトを最初に作成することもできます。順序は重要ではありません。
コンポーネントのメイン クラスには、プロパティとメソッド定義の例、およびイベント宣言が含まれます。これらは扱いの方法を示すために用意されます。必須ではなく、この例では、生成されたコードはすべて独自のコードで置き換えられます。
C++ コンポーネント プロジェクトを作成するには
Visual Studio メニュー バーで [ファイル] をクリックし、[新規] を選択し、[プロジェクト] をクリックします。
[新しいプロジェクト] ダイアログ ボックスで、左ペインの [Visual C++] を展開し、Windows ストア アプリのノードを選択します。
中央のペインで、[Windows ランタイム コンポーネント] をクリックし、プロジェクト名を CppLib にします。
[OK] をクリックします。
アクティブ化可能なクラスをコンポーネントに追加
アクティブ化可能なクラスとは、JavaScript が new 式を使用して作成できるクラスです。コンポーネントでは、public ref class sealed として宣言します。実際、Class1.h と .cpp ファイルには既に ref クラスがあります。名前は変更できますが、この例では既定の名前 Class1 を使用します。必要に応じてコンポーネントに追加の ref クラスを定義できます。ref クラスの詳細については、「型システム (C++/CX)」を参照してください。
必須の #include の追加とステートメントの使用
必須の #include を追加してステートメントを使用するには
これらの #include ディレクティブを Class1.h に追加します。
#include <collection.h> #include <amp.h> #include <amp_math.h>
これらの #include ディレクティブを Class1.cpp に追加します。
#include <ppltasks.h> #include <concurrent_vector.h>
ステートメントを使用してこれらを Class1.cpp に追加します。
using namespace concurrency; using namespace Platform::Collections; using namespace Windows::Foundation::Collections; using namespace Windows::Foundation; using namespace Windows::UI::Core;
同期パブリック メソッドをクラスに追加
すばやく実行するメソッドは同期メソッドとして実装できます。これらは、JavaScript の呼び出し元と同じスレッドで実行します。パブリック メソッドのすべてのパラメーターの型と戻り値は Windows ランタイム 互換性の型である必要があります。
同期パブリック メソッドをクラスに追加するには
次の宣言を Class1.h クラスに追加します。
public: Windows::Foundation::Collections::IVector<double>^ ComputeResult(double input); Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last); Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last); event PrimeFoundHandler^ primeFoundEvent; private: bool is_prime(int n); Windows::UI::Core::CoreDispatcher^ m_dispatcher; private: void ComputeResultImpl(concurrency::array_view<float, 1>&);
これらの実装を Class1.cpp に追加します。
void Class1::ComputeResultImpl(array_view<float, 1>& logs) { parallel_for_each( logs.extent, [=] (index<1> idx) restrict(amp) { logs[idx] = concurrency::fast_math::log10(logs[idx]); } ); } //Public API IVector<double>^ Class1::ComputeResult(double input) { // Implement your function in ISO C++ or // call into your C++ lib or DLL here. This example uses AMP. float numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 10000.0 }; array_view<float, 1> logs(6, numbers); ComputeResultImpl(logs); // Return a Windows Runtime-compatible type across the ABI auto res = ref new Vector<double>(); int len = safe_cast<int>(logs.extent.size()); for(int i = 0; i < len; i++) { res->Append(logs[i]); } return res; }
非同期パブリック メソッドをクラスに追加
実行に時間がかかる可能性のあるメソッドは非同期メソッドとして実装します。非同期メソッドはバックグラウンド スレッドで実行され、JavaScript のスレッドをブロックしません。create_async メソッドを内部的に使用することで、async パブリック メソッドを作成します。JavaScript は、他の約束のようにメソッドを呼び出します。同期メソッドと同様に、すべてのパラメーターの型は Windows ランタイム 互換性の型である必要があります。非同期メソッドでは、戻り値の型は次のいずれかである必要があります。
値を返さず、進行状況に関する情報を提供しない非同期のメソッドでは IAsyncAction。
値を返さないものの、進行状況に関する情報を提供する非同期のメソッドでは IAsyncActionWithProgress。
値を返すものの、進行状況に関する情報を提供しない非同期のメソッドでは IAsyncOperation<T>。
値 T を返し、進行状況に関する情報も提供する非同期のメソッドでは IAsyncOperationWithProgress<T>。
詳細については、「C++ における Windows ストア アプリ用の非同期操作の作成」を参照してください。
結果を返し、進行状況に関する情報を提供する非同期メソッドを追加するには
次の宣言を Class1.h クラスに追加します。
Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last); Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last); event PrimeFoundHandler^ primeFoundEvent; private: bool is_prime(int n);
これらの実装を Class1.cpp に追加します。
// Determines whether the input value is prime. bool Class1::is_prime(int n) { if (n < 2) return false; for (int i = 2; i < n; ++i) { if ((n % i) == 0) return false; } return true; } // This method computes all primes, orders them, then returns the ordered results. IAsyncOperationWithProgress<IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last) { return create_async([this, first, last] (progress_reporter<double> reporter) -> IVector<int>^ { // Ensure that the input values are in range. if (first < 0 || last < 0) { throw ref new InvalidArgumentException(); } // Perform the computation in parallel. concurrent_vector<int> primes; long operation = 0; long range = last - first + 1; double lastPercent = 0.0; parallel_for(first, last + 1, [this, &primes, &operation, range, &lastPercent, reporter](int n) { // Report progress message. double progress = 100.0 * InterlockedIncrement(&operation) / range; if (progress >= lastPercent) { reporter.report(progress); lastPercent += 1.0; } // If the value is prime, add it to the local vector. if (is_prime(n)) { primes.push_back(n); } }); // Sort the results. std::sort(begin(primes), end(primes), std::less<int>()); // Copy the results to an IVector object. The IVector // interface makes collections of data available to other // Windows Runtime components. IVector<int>^ results = ref new Vector<int>(); std::for_each(std::begin(primes), std::end(primes), [&results](int prime) { results->Append(prime); }); reporter.report(100.0); return results; }); }
イベントを発生し、進行状況に関する情報を提供する非同期メソッドを追加するには
次のパブリック宣言を Class1.h クラスに追加します。
Windows::Foundation::IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last); event PrimeFoundHandler^ primeFoundEvent;
このプライベート宣言を追加します。
Windows::UI::Core::CoreDispatcher^ m_dispatcher;
Class1.h 名前空間スコープで次のデリゲートを追加します。
public delegate void PrimeFoundHandler(int i);
このオブジェクトを使用して、並列関数から UI スレッドにイベントをマーシャリグします。
これらの実装を Class1.cpp に追加します。
// This method returns no value. Instead, it fires an event each time a prime is found, and transfers the prime through the event. IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last) { auto window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); m_dispatcher = window->Dispatcher; return create_async([this, first, last](progress_reporter<double> reporter) { // Ensure that the input values are in range. if (first < 0 || last < 0) { throw ref new InvalidArgumentException(); } // Perform the computation in parallel. concurrent_vector<int> primes; long operation = 0; long range = last - first + 1; double lastPercent = 0.0; parallel_for(first, last + 1, [this, &primes, &operation, range, &lastPercent, reporter](int n) { // Report progress message. double progress = 100.0 * InterlockedIncrement(&operation) / range; if (progress >= lastPercent) { reporter.report(progress); lastPercent += 1.0; } // If the value is prime, add it to the local vector. if (is_prime(n)) { primes.push_back(n); m_dispatcher->RunAsync( CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, n]() { this->primeFoundEvent(n); }, Platform::CallbackContext::Any)); } }); reporter.report(100.0); }); }
JavaScript プロジェクトの作成
JavaScript プロジェクトを作成するには
([ソリューション] ノードのショートカット メニューにある) [ソリューション エクスプローラー] で、[追加] をクリックして、[新しいプロジェクト] をクリックします。
[JavaScript] を展開し、[空のアプリケーション] をクリックします。
[OK] をクリックして、App1 の既定の名前を使います。
App1 プロジェクト ノードを右クリックし、[スタートアップ プロジェクトに設定] をクリックします。
CppLib にプロジェクト参照を追加
[参照] ノードのショートカット メニューで、[参照の追加] をクリックします。
[参照マネージャー] ダイアログ ボックスの左ペインで、[ソリューション] を選択して、[プロジェクト] をクリックします。
中央のペインで、CppLib を選択して、[OK] をクリックします。
JavaScript イベント ハンドラーを呼び出す HTML の追加
default.html ページの <body> ノードにこの HTML を貼り付けます。
<div id="LogButtonDiv">
<button id="logButton" onclick="LogButton_Click()">Logarithms using AMP</button>
</div>
<div id="LogResultDiv">
<p id="logResult"></p>
</div>
<div id="OrderedPrimeButtonDiv">
<button id="orderedPrimeButton" onclick="ButtonOrdered_Click()">Primes using parallel_for with sort</button>
</div>
<div id="OrderedPrimeProgress">
<progress id="OrderedPrimesProgressBar" value="0" max="100"></progress>
</div>
<div id="OrderedPrimeResultDiv">
<p id="orderedPrimes">
Primes found (ordered):
</p>
</div>
<div id="UnorderedPrimeButtonDiv">
<button id="ButtonUnordered" onclick="ButtonUnordered_Click()">Primes returned as they are produced.</button>
</div>
<div id="UnorderedPrimeDiv">
<progress id="UnorderedPrimesProgressBar" value="0" max="100"></progress>
</div>
<div id="UnorderedPrime">
<p id="unorderedPrimes">
Primes found (unordered):
</p>
</div>
スタイルの追加
body スタイルを削除し、default.css に次のスタイルを追加します。
#LogButtonDiv { background: maroon; border: orange solid 1px; -ms-grid-row: 1; /* default is 1 */; -ms-grid-column: 1; /* default is 1 */ } #LogResultDiv { background: black; border: red solid 1px; -ms-grid-row: 1; -ms-grid-column: 2; } #UnorderedPrimeButtonDiv, #OrderedPrimeButtonDiv { background: green; border: orange solid 1px; -ms-grid-row: 2; -ms-grid-column:1; } #UnorderedPrimeDiv, #OrderedPrimeDiv { background: maroon; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column:1; } #UnorderedPrimeProgress, #OrderedPrimeProgress { background: lightgray; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column: 2; -ms-grid-column-span: 2; } #UnorderedPrimeResult, #OrderedPrimeResult { background: black; border: red solid 1px; -ms-grid-row: 2; -ms-grid-column: 3; }
コンポーネント DLL に呼び出す JavaScript イベント ハンドラーの追加
次の関数を default.js ファイルの末尾に追加します。これらの関数は、メイン ページのボタンが選択されたときに呼び出されます。JavaScript が C++ クラスをアクティブにして、そのメソッドを呼び出し、戻り値を使用して HTML ラベルを設定することに注目します。
var nativeObject = new cpplib.Class1(); function LogButton_Click() { var val = nativeObject.computeResult(0); var result = ""; for (i = 0; i < val.length; i++) { result += val[i] + "<br/>"; } document.getElementById('logResult').innerHTML = result; } function ButtonOrdered_Click() { document.getElementById('orderedPrimes').innerHTML = "Primes found (ordered): "; var asyncResult = nativeObject.getPrimesOrdered(2, 1000).then( function (v) { for (var i = 0; i < v.length; i++) document.getElementById('orderedPrimes').innerHTML += v[i] + " "; }, function (error) { document.getElementById('orderedPrimes').innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById( "OrderedPrimesProgressBar"); progressBar.value = p; }); } function ButtonUnordered_Click() { document.getElementById('unorderedPrimes').innerHTML = "Primes found (unordered): "; nativeObject.onprimefoundevent = handler_unordered; var asyncResult = nativeObject.getPrimesUnordered(2, 1000).then( function () { }, function (error) { document.getElementById("unorderedPrimes").innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById("UnorderedPrimesProgressBar"); progressBar.value = p; }); } var handler_unordered = function (n) { document.getElementById('unorderedPrimes').innerHTML += n.target.toString() + " "; };
アプリケーションの実行
F5 キーを押します。
オブジェクト ブラウザーでコンポーネントを検査 (省略可能)
オブジェクト ブラウザーでは、.winmd ファイルで定義されている Windows ランタイム のすべての型を検査できます。これには Platform 名前空間と既定の名前空間の型が含まれます。ただし、Platform::Collections 名前空間の型は、winmd ファイルではなく、ヘッダー ファイル collections.h で定義されます。そのため、これらの型はオブジェクト ブラウザーには表示されません。
オブジェクト ブラウザーでコンポーネントを検査するには
Visual Studio のメニュー バーで、[表示]、[その他のウィンドウ]、[オブジェクト ブラウザー] をクリックします。
オブジェクト ブラウザーの左ペインで [CppLib] ノードを展開し、コンポーネントで定義されるメソッドや型を表示します。
デバッグのヒント
より詳細なデバッグ機能については、パブリックの Microsoft シンボル サーバーからデバッグ シンボルをダウンロードしてください。メイン メニューの [ツール] をクリックし、[オプション] をクリックします。[オプション] ウィンドウで [デバッグ] を展開し、[シンボル] をクリックします。[Microsoft シンボル サーバー] の横にあるチェック ボックスをオンにして、[OK] をクリックします。初回のダウンロードには時間がかかる場合があります。次回に F5 キーを押したときのパフォーマンスを向上させるために、提供されたスペースを使用して、シンボルをキャッシュするローカル ディレクトリを指定します
コンポーネント DLL を含む JavaScript ソリューションをデバッグする場合、デバッガーを設定して、スクリプトのステップ実行か、ネイティブ コードのステップ実行を有効にできますが、その両方を同時に有効にすることはできません。設定を変更するには、ソリューション エクスプローラーにある JavaScript プロジェクト ノードのショートカット メニューで、[プロパティ]、[デバッグ]、[デバッガーの種類] をクリックします。
パッケージ デザイナーで適切な機能を選択してください。たとえば、Windows ランタイム API を使用してファイルを開く場合、パッケージ デザイナーの [機能] ペインの [ドキュメント ライブラリ アクセス] チェック ボックスをオンにしてください。
JavaScript コードがコンポーネントのパブリック プロパティまたはメソッドを認識していないようであるなら、JavaScript で Camel 形式の大文字小文字の区別を使用していることを確認します。たとえば、ComputeResult C++ メソッドは、JavaScript では computeResult として参照する必要があります。
ソリューションから C++ Windows ランタイム コンポーネント プロジェクトを削除する場合、JavaScript のプロジェクトから手動でプロジェクト参照も削除する必要があります。これを行わなかった場合、後続のデバッグまたはビルド操作が妨げられます。その後、必要に応じて DLL にアセンブリ参照を追加できます。
参照
関連項目
Roadmap for Windows Store apps using C++
その他の技術情報
JavaScript および C++ での Windows ストア アプリ (Bing Maps Trip Optimizer) の開発