metro アプリケーションの作り方は、いくつかパターンが考えられて、
- VB/C# 単体で作る。
- C++/CX 単体で作る。
- VB/C# でライブラリを作って、VB/C# で使う。
- C++/CX でライブラリを作って、C++/CX で使う。
このように同じ言語で揃えるパターンの他にも、
- C++/CX でライブラリを作って、VB/C# で利用する。
- VB/C# でライブラリを作って、C++/CX で利用する。
というのがあります。さきの同じ言語の場合はスムースにいくのですが、他言語と mix する場合はどうなるかというと…実は、「VB/C# でライブラリを作って、C++/CX で利用する」という方法は取れません。あまり明記はされていないのですが、C#/VB の場合は、.NET Framework を使っていて、C++/CX では WinRT のみ使っている且つ .NET Framework が使えない、ということを考えると、c++/cx から c# は使えんだろう、という想像ができます。
という訳で、これを実験して確認しておきます。
■c++/cx で作ったライブラリを、c++/cx で使う
まずは、普通に使えるであろう、c++/cx だけの組み合わせを試してみます。
c++/cx のライブラリのほうは、こんな感じ。
「WinRT コンポーネントDLL」を選択して作ります。
#pragma once namespace CppCxClassLib { public ref class Calc sealed { public: Calc(); int Add( int x, int y ); Platform::String^ Add( Platform::String^ x, Platform::String^ y ); }; }
// WinRTComponent.cpp #include "pch.h" #include "WinRTComponent.h" using namespace CppCxClassLib; using namespace Platform; Calc::Calc() { } int Calc::Add( int x, int y ) { return x+y; } Platform::String^ Calc::Add( Platform::String^ x, Platform::String^ y ) { return x + y ; }
公開するクラスは「sealed」を付けておきます。
これを利用する c++/cx の画面のほうは、普通の metro アプリケーションで作ります。
参照設定で「CppCxClassLib」のライブラリを参照しておきます。
void CppCxUseCSharpLib::BlankPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { CppCxClassLib::Calc^ calc = ref new CppCxClassLib::Calc(); int num = calc->Add( 10 , 20 ); textBox1->Text = num.ToString(); } void CppCxUseCSharpLib::BlankPage::Button_Click_2(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { CppCxClassLib::Calc^ calc = ref new CppCxClassLib::Calc(); String^ s = calc->Add( L"masuda", L"tomoaki" ); textBox1->Text = s; }
これで普通の動くことを確認します。まあ、最初の動作確認ですね。
■c++/cx で作ったライブラリを、C# で使う
今度は、CppCxClassLib ライブラリを C# で利用します。
private void Button_Click_1(object sender, RoutedEventArgs e) { var calc = new CppCxClassLib.Calc(); int num = calc.Add(10, 20); textBox1.Text = num.ToString(); } private void Button_Click_2(object sender, RoutedEventArgs e) { var calc = new CppCxClassLib.Calc(); string s = calc.Add("masuda", "tomoaki"); textBox1.Text = s; }
書き方は、c++/cx と同じに使えます。CppCxClassLib ライブラリを参照設定すれば普通のライブラリと同様に使えます。
■C# で作ったライブラリを、C++/CX で使う?
さて、本題の C++/CX -> C# という向きはできるのでしょうか?という確認です。
まずは C# のライブラリを作成しておきます。
namespace CSharpClassLib { public sealed class Calc { /// <summary> /// int 型を返す /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public int Add(int x, int y) { return x + y; } /// <summary> /// System.String 型を返す /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public string Add(string x, string y) { return x + y; } } }
中身は、C++/CX と同じものですね。
このライブラリを metro c++/cx から参照せっていすると…実は参照設定ができます。
参照設定をしただけでは別にエラーはでません。
では、コーディングを C# のライブラリを使うように書き直してみると…
void CppCxUseCSharpLib::BlankPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { CSharpClassLib::Calc^ calc = ref new CSharpClassLib::Calc(); int num = calc->Add( 10 , 20 ); textBox1->Text = num.ToString(); } void CppCxUseCSharpLib::BlankPage::Button_Click_2(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { CSharpClassLib::Calc^ calc = ref new CSharpClassLib::Calc(); String^ s = calc->Add( L"masuda", L"tomoaki" ); textBox1->Text = s; }
普通にコーディングができてしまいます。c++/cx のライブラリと同様にインテリセンスも効くので、コーディングは楽チンです。
なので、ひょっとするとこのまま c++/cx -> c# の向きは ok なのか、と思いきや実際コンパイルしてみるとエラーになります。
インテリセンスのエラーはないんですけどね。
という訳で、めでたく c/c++ -> c# への向きは「できません」ということです。
■まとめ
ライブラリの使い方としては、以下の通り。
- OK: c# -> c#
- OK: c++/cx -> c++/cx
- OK: c# -> c++/cx
- NG: c++/cx -> c#
ってことでチェック完了…と思いきや、実は C++/CX -> C# が使えます。
2012/05/24 追記
C# のライブラリの出力先を「WinMDファイル」にしておくと、C++/CX でも使えるようになりますね。これは便利。
なので、この話は別途書きます。
C#のライブラリのプロジェクトのプロパティで「出力の種類」を「WinMDファイル」にすると、C++/CXから使用できるようになりますよ。私も、最初このことに気付かずc/c++ -> c#はできないと思っていました。
おお、確かにできます、できます。ありがとうございます。
「WinMDファイル」にすると、C++/CX -> C# が ok になりますね。
後ほど、記事を修正せねば。