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 になりますね。
後ほど、記事を修正せねば。