C++/CX で扱う文字列の話

[win8] C++/CLI と C++/CX の違い | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3401

の続きとして「文字列の話」を少し書き下しておきます。

.NET Framework では文字列を一律 System::String で扱っています。これは、C#/VB 共通で、C++/CLI でも文字列は System::String。なので、やり取りは相互にやり取りは簡単です。まぁ、ファイルから SJIS を読み込む時はややこしいことをしないと駄目なんですが、ひとまず UTF-8 のファイルを作っておけば、統一的に扱えます。

が、御存じのように歴史的な点から C/C++ の文字列の扱いはやっかいです。ざっと書き出してみると、

  • 御馴染みの char[] 。
  • 御馴染みの std::string
  • MFC で使う CString
  • C++/CLI で使う System::String
  • ワイドキャラクタで使う wchar_t []
  • string のワイドキャラクタ版 wstring

なところです。後、winapi を使う場合には「LPCSTR」とか「LPSTR」とかが頻発します。COM を扱う場合には BSTR もありますね。

この中で、char[] と std::string を頻繁に使います。どちらも ASCII 文字しか扱えませんが「お手軽」なのと、欧米産のサンプルコードは大体これになっているので、これを使ってしまうのです。

そこで、日本語を扱う時にはどうすればよいのか?と悩むのですが、MFC の CString を使います。STL的には wchar_t が推奨されているのですが、当時 wstirng 関係が貧弱で、更に xml 絡み(xercesなど)が入ってきて i18l はえらいことになって来てしまって、ってな具合で wchar_t は避けられてきたのです(最近5年ぐらいは分からないのですが、10年前はそうでした)。

なので、Windows アプリを使う場合は、CString で統一して内部コードを Unicode にします。こうするとコンバートが簡単なのです。ただし、正確に SJIS からコンバートできるわけではないので、外字とか波線/チルダを正確に扱いたい時は SJIS を使います(あと、全角の空白とか半角カタカナとか)。データベースのカラム名やプログラム内部で扱うファイル名ぐらいならば CString で十分なのです。

また、C#/VB との相互運用をする場合には、System::String を使います。マーシャリング関数も 2008 あたりから追加されているので、char[] との変換も楽なんですねぇ。

■metro の C++/CX でどう扱うのか?

で、metro C++ としての C++/CX がどうなっているのかというと。

  • Platform::String で扱う

ことになっています。この Platform::String は微妙なクラスで、非常に簡単な代入メソッドしか用意されていません。Platform::String Class 比較と代入と長さぐらいしかありません。まあ、これはこれでいいんですよ。C#/VB へ公開する場合は、自動的に System::String に変換される(System::String が面倒をみている?)ので、相互に使えます。

が、Platform::String を編集しようとする時、挿入とかフォーマットとか部分文字列を扱う場合にはどうしたらいいのか?

ここで使われるのが wchar_t[] と wstring なのです。Platform::String は内部的に unicode と同じなので、wchar_t[] で扱うのが良いのですね。と言いますか、char[] や string では扱えないのですよ。いちいちコンバートしないといけません。なので、どうせならば、wchar_t や wstring で扱うのが良いわけですね。

さて、ワイドキャラクタを扱う関数はどうなのかというと、従来はCStringにコンバートすると便利だったわけです。CStringクラスには、文字列絡みの便利メソッドがたくさんあったのでこれを使っていた…のですが、metro C++ では、CString クラスを含む MFC が使えません。なんだかなぁ、という訳です。

仕方がないので、kernel32 や user32 に含まれている wprintf などを使おうと思ったのですが、windows.h がインクルードできません。と言いますか、metro c++ から直接 row winapi を触っちゃ駄目ですよね、多分。

なので wchar.h という C95 ぐらい?から用意されているワイドキャラクタ用の関数を使います。ここには wprintf 関数などがあります。

C言語関数辞典 – wchar.h
http://www.c-tipsref.com/reference/wchar.html

ここの wprintf や swprintf なんですが、windows.h を使っていた私にとって微妙に似て非なる関数名というか…まぁ、いいんですよ。これくらいなら覚え直せばよいので。

さて、肝心の std::wstring なのですが、果たして string 並み安定しているのでしょうか。ってのが問題ですね。wstring

という訳で、C++/CX で文字列を扱う場合は、

  • Platform::String クラスを使う
  • wchar_t を使う
  • str::wstring を使う

というチープというか、整理されちゃったというか、非常に貧弱な環境に戻ります。ただし、日本人のプログラマにラッキーなのは、Platform::String の扱いが wchar_t をデフォルトとしているので、以前の char や string を前提としたプログラムコードが少なくなることが期待できます。desktop 版のほうは相変わらずなんでしょうが、少なくとも metro c++ のほうは wchar_t のほうが主流になるようです。

■Platform::String と wchar_t, std::wstring の相互変換

ちなみに、この3つの相互変換は簡単で、以下のようにやります。

	// 文字列の初期化
	Platform::String^ str = L"masuda";
	// wchar_t に変換
	const wchar_t *wstr = str->Data();
	// String に変換
	Platform::String ^str2 = ref new Platform::String(wstr);

	// std::wstring に変換
	std::wstring ws( str->Data() );
	// String に変換
	Platform::String ^str3 = ref new Platform::String(ws.c_str());

一度、wchar_t か wstring に変換した後で編集して書き戻すという流れになりますね。書式は wstring では面倒なので、swpritnf 関数を使うと便利です。

カテゴリー: C++/CX, windows 8 | 4件のコメント

iPhone/iPad アプリ開発 逆引き大全 500の極意ができました

iPhone/iPadアプリ開発 逆引き大全500の極意|書籍情報|秀和システム
http://www.shuwasystem.co.jp/products/7980html/3352.html
iPhone/iPadアプリ開発逆引き大全500の極意―Apple Mac OS10Lion対応 iOS5SDK/Xcode4対応: 増田 智明, 池谷 京子: 本
http://www.amazon.co.jp/exec/obidos/ASIN/4798033529

image

そんな訳で苦節半年(ぐらいかな)掛かりましたが、iPhone/iPad プログラミングの本ができあがりました。総ページ数 600頁強、総Tips 数 500 、私の担当分は 300 tips という具合です。今回の本では、サンプルプログラムを CD-ROM ではなくて、サイトからダウンロードすることになっています。現時点で準備中(整理が間に合わなかった)なので、今週中にはアップします。

ひと通り iPhone プログラミングの手順が書いてあります。Xcode 4 をベースにして、iOS 5 に対応というところですね。執筆中に、XCode 4 のバージョンが上がるものだから、ちょっとばかり最新のバージョンと違うかもしれない(コンポーネントにメソッドを追加するところとか、最新版のほうがちょっと便利)のですが、ほぼOKです。

で、基本的に XCode のコンポーネントをぺたぺたとストーリーボードに貼りつけて作る、というやり方なので、UIKitをがりがりコーディングということはしていません。なので、ハードなゲームアプリとか、実にすばらしいUIのあるアプリ、を使う向きの方には物足りない(と言いますか、役に立たない)かもしれませんが、ひとまず、XCode をダウンロードしてシミュレータから初めてみたい、という方にお勧めです。いわば、Visual Studio 感覚で、iPhone アプリを作りたいよという方向きですね。私自身が、非常に Windows 寄りな人なので、そっちのほうから攻めています。

Objective-C の文法自体は、本書では網羅していません。iPhone アプリを作成するために必要な部分だけを解説しています。なので、Objective-C なりのオブジェクト指向プログラミングをしたい場合にも、本書はちょっと向きません。逆に言えば、C、C++ は大まかにわかる、Objective-C はわからない、C# はちょっとわかる程度でも使える感じになっています。他書とちょっと違うのは、Objective-C から C言語の配列や文字列を扱うところを少しいれてあります。これは、C言語で作ったライブラリをそのまま iPhone に流用するところを想定しています。逆に、ポインタやリファレンスの話はざっくりと省いてあります。

Tips の中で、XCode からプロパティを操作するところ、コードからプロパティを操作するところがかなりダブっているところがありますが執筆上の都合でそのままにしてあります。本当はそれぞれ分けてページ数を減らせばよかった(単価が安くなる?)のですが、諸々の事情がありそのままに、という具合です。あちこちページを捲らなくてもプロパティの操作については一か所で閲覧できる、という「前向きに」捉えてくださるとありがたいです。m(_”_)m

と言う訳で、実に Visual Studio な人が、Xcode を使ったら「こうなっちまった」感が強い極意シリーズとなってしまいましたが、本屋さんで一度手に取ってみてくださると分かります。そうそう、どう作っても UI が iPhone になるように出来ていて UIKit は素晴らしいかなと。Objective-C の文法と、UIKit があいまって実に…な感じだったります。

カテゴリー: 書籍, Objective-C | iPhone/iPad アプリ開発 逆引き大全 500の極意ができました はコメントを受け付けていません

アメブロ自動ペタツール ThxPeta ver.0.7

みなさん、元気にコンプガチャやっていますか~、私はやっていません。

さて、先日のアメブロのメンテナンスでペタツールのペタ自動取得部分が動かなくなっていたので、修正しました。ご連絡ありがとうございます > peco さん m(_”_)m

ダウンロードは、こちらから。

image

単純なタグ取得修正でよかったよかった、と。

カテゴリー: ツール | 3件のコメント

metro-desktop 間のループバックには CheckNetIsolation コマンドで設定する

metro-desktop のプロセス間通信の続き

[win8] metro アプリケーションからデスクトップアプリにプロセス間通信する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3379
[win8] MetroアプリからDesktopアプリへWCFで接続する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3387
[win8] metro-desktopのプロセス間通信をWeb API風にする | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3395

上記なところで、metro アプリから desktop アプリへ WCF/WebAPI 通信を行う実例を示しましたが、実は metro アプリを visual studio 11 上でデバッグ実行した時しか動かなくて、パッケージを作って metro アプリをインストールした時には「ローカルコンピュータに繋がらない」という現象になりました…つーか、もともと「ループバックはつながらない」と @biac さんから聞いていたものの、「なんか動くから、ええやん」って気分で突き進んでいたのが問題。

windows 8 の metro アプリの方針として「インストール/アンインストールは安全にできる」、「実行時に他の環境を壊さない」というものがあるので、他アプリとの直接的なプロセス間通信の手段が用意されていません。当然「ループバック」(自分自身への送受信)もガードされています。
実は、ループバック自体はガードしなくてもよいのですが、今回私がやっているように、desktop アプリ側に WCF サーバーを立てて、metro アプリでクライアントを立てると安々とプロセス間通信ができてしまうんですよね。プロセス間通信自体は悪いことではないのでしょうが、desktop アプリとの複雑な連携をしてしまうと「安全な metro アプリ」の保証ができない、といったとこrでしょうか。そもそも、通常の metro アプリ自体は単体でインストールされることになるので、desktop アプリと連携を取ることはありませ。なので、WCF/WebAPI 通信をするにしても、インターネットやイントラネット上の WEB サーバーがなどが相手先になるのです。通信手段が、Http プロトコルしか用意されていないのも、そのあたりの制限だと思います(C++/CX の場合はその限りではないのですが)。

なので、

  • metro アプリ → 他コンピュータの WCF サーバーは接続可能
  • metro アプリ → 自コンピュータの WCF サーバーは接続不可

の状態が初期値になっています。

ですが、Windows Store を通さない業務アプリだったり、開発段階で1台のマシンでデバッグしたい場合にはこの制限は不便です。特にデバッグ時は、サーバーアプリとクライアントアプリ(metroアプリ)を連携させながらデバッグしたいハズです。
なので、Visual Studio 11 では、デバッグ実行時のみ、ループバックが有効になるフラグが付けられています。

*.appxmanifest ファイルをダブルクリックして、「デバッグ」のタブを開くと「ローカルネットワークのループバックを許可」にチェックが入っています。これにより、デバッグ実行をした時にループバックが有効になります。

さて、Visual Studio 11 によるデバッグ時にはこれでもいいのですが、業務アプリの場合はどうしましょうか?つーか、これじゃ使えねーよ、ってな具合なので、探しましたよ~

How to enable loopback and troubleshoot network isolation
http://msdn.microsoft.com/en-us/library/windows/apps/Hh780593.aspx

そのものズバリの対処方法が公式に載っていました。「CheckNetIsolation.exe LoopbackExempt」というコマンドを使います。試してみると、管理者権限でない通常の DOS プロンプトでも使えるコマンドなので、desktop アプリのインストール時に一緒に実行すると良いかと。

ざっと、要点だけを抜き出すと。


CheckNetIsolation.exe LoopbackExempt -s 			リスト出力
CheckNetIsolation.exe LoopbackExempt -a -p=<SID>	SID 指定でループバックを許可
CheckNetIsolation.exe LoopbackExempt -d -p=<SID>	SID 指定でループバックを不許可
CheckNetIsolation.exe LoopbackExempt -a -n=<name>	パッケージファミリ名指定でループバックを許可
CheckNetIsolation.exe LoopbackExempt -d -n=<name>	パッケージファミリ名指定でループバックを不許可

という具合です。パッケージの作り方は、

[Metro Style] #Win8 #Metro のアプリケーション・パッケージ: biac の それさえもおそらくは幸せな日々@nifty
http://bluewatersoft.cocolog-nifty.com/blog/2012/02/metro-style-w-1.html

を参照してください。Visual Studio 11 の入っていない素な環境で試す時は、下記のように「開発者用ライセンス」も取得します。

windows consumer preview / 開発環境の入っていないPCにMetrostyle appsをデプロイする – code+code-
http://code.hatenablog.com/entry/2012/04/05/014341

# windows store を通さない場合は、enterprise edition を用意するかこの開発者ライセンスを入れないと駄目な模様なので、社内展開する場合は、ちょっと工夫がいりそうです。

CheckNetIsolation コマンドを使ってバッチなどで登録する場合、事前に「パッケージファミリ名」が必要になります。これは、*.appxmanifest ファイルの「パッケージ化」のところの「パッケージ ファミリ名」を使います。SID のほうは、インストールするまで分からないので、事前には取れません。

# 「パッケージ名」じゃなくて「パッケージファミリ名」というのが曲者ですね。

このコマンド、実行すると即、許可/不許可を反映させてくれます。metro アプリがバックグラウンドで立ち上がったままでも、コマンドを実行すると許可/不許可が即反映される訳です。

これで、プロセス間通信として、WCF/WebAPIが使えますね…というか、もともとは「プロセス間通信」をしたかっただけなので、HTTP プロトコル経由はちょっとオーバーヘッドが大きすぎる。もうちょっと内部的にデータのやり取りができないかと更に模索中です。

カテゴリー: 開発, windows 8 | metro-desktop 間のループバックには CheckNetIsolation コマンドで設定する はコメントを受け付けていません

[win8] C++/CLI と C++/CX の違い

Microsoft 社の公式見解は、何処かにある(?)と思うので、私のほうで Visual Studio 11 beta を1か月程触った感じで言いますと、

  • 「C++/CX って、C++/CLI の後継でも何でもないやんッ!!! まったく別物じゃ」
  • 「C++/CX って、metro アプリケーションは作れるけれど、desktop アプリは作れないじゃんッ!!!」
  • 「C++/CLI って、metro アプリが作れないどころか、WPF アプリも相変わらず作れないじゃんッ!!!」
  • 「C++/CX って、WinRT を扱えるけど、.NET Framework を扱えないじゃん、つーか、C++ の世界に戻らないとだめじゃんッ!!!」
  • 「そもそも、C++/CX と C++/CLI って混在できないじゃんッ!!!」

ってな具合で、憤懣ひとしきりなんですよ。というわけで、ちょっと以下に戯言を。

希望/期待としては、C++/CLI が更に拡張されて XAML を扱えるようになって、なもんだから metro アプリを作れるよ、という流かと思っていたのですが相当違うようです。WinRT に隠されているというか、C++/CLI は爆死状態というか。いえ、おそらく去年の build で分かっていた段階なんでしょうが、ちと去年の夏頃は別件で忙しくてあまり C++/CLI を眺めてはいなかったんですよね~。ベータ版をインストールして、metro の C++ プロジェクトをさくりと作って「ああ、インテリセンスが動く、進化したなぁ」と思っていた訳ですが、いえいえ、あれは「C++/CX」のほうで「C++/CLI」ではなかったのですね。

さて、「C++/CX」と「C++/CLI」という用語が頻発していますが、microsoft さんの講演の中は一律して「C++」として扱っています。と言いますか「C++/CLI」には一切触れずに、「C++/CX」のことを「C++」と称しています。「Visual Basic .NET」のことを「Visual Basic」と言うようになった流れと同じ感じだと思います。

C++/CLI というのは、「.NET Framework の共通言語基盤(ランタイム)を扱えるC++」という意味です。正式名称は「C++ with Common Language Infrastructure」ってところですかね。単体で Windows アプリケーションを作れますが、主な仕事としては、「旧来のC/C++ライブラリを、そのままC#で扱えるようにする」なところで、単体で扱うことは非常に稀です。Windows アプリを作るならば、C#/VB で作ったほうが手早く作れます。ですが、C++ の文法から、.NET Framework をあつかうことができるので、C#/VB と同じようにプログラミングをすることができます。
ただし、欠点としては、

  • Visual Studio 2010 上でインテリセンスが効かないこと。
  • XAML が扱えないので、WPF や Silverlight アプリケーションが作れないこと。

があります。インテリセンスのほうは、Visual Studio 2008 を使えばよいので特に問題はありません。まあ、Visual Studio 2010 では実質開発ができない状態だったので、なんとも言えないところですが。
XAML が扱えないのは今回の visual studio 11、windows 8 で致命的なところです。

一方で、「WinRT が扱える C++」として登場しているのが「C++/CX」です。正式名称は「C++ with Component Extensions」ってことで、WinRT≒DCOM で拡張されている C++ という意味合いです。今回の Visual C++ 11 の売りは、「C++ で metro アプリケーションを作れる」ことです。metro アプリケーションというのは、windows 8 の主流となるタブレット型のアプリケーションで、内部的には XAML を使っています。となると、意訳すれば「XAML を扱える C++」ということになります。

さて、C++/CLI では XAML を扱えない。逆に、C++/CX では XAML を扱える。という区別があります。このために、C++/CLI と C++/CX の開発領域は二分されて、

  • desktop アプリ(従来の windows アプリ)の場合は、C++/CLI
  • metro アプリの場合は、C++/CX

という住み分けができます。文法的はどちらも C++ をベースにして拡張されていしマネージドポインタ「^」や「%」の扱いも同じなのでほぼ互換性があります。主な違いは、「gcnew」と「ref new」の違いぐらいです…で済めばよいのですが、実際コーディングをしてみると大きく違います。

今回、metro アプリケーションを C++ で作れるようにした理由に「ゲームアプリケーション」があります。タブレット市場でもゲームの割合は大きくて、グラフィック機能とマシンパワーを存分に使ったゲームは、windows 8 になると飛躍的に上がる(と思われる)ところです。
さて、私の場合ゲーム開発業界に疎いので正確なことは分からないのですが、察するに windows 7 の頃のゲーム開発の場合は、

  • グラフィックを扱うならば DirectX を使う。
  • 余りパフォーマンスがいらないところは、Microsoft.Xna.Framework

なのだと思います。DirectX はグラフィックボードの機能を存分発揮できるようにと開発されたインターフェースでC/C++で扱います。途中で、DirectX の .NET Framework 版があったのですが、その後 Microsoft.Xna.Framework に吸収されています。
で、ゲームのパフォーマンスを出す上で、.NET Framework が十分対応しているかというと、そうではありません。結局グラフィック関係は、WinRT に残していることを考えると GC を含む virtual machine 環境ではきついものがあります。メモリへの直接アクセスが禁止されていることのデメリットは、OpenCV や Fortran を触るとよく分かります。

このあたり、C++/CLI の出番かと思っていたのですが、ゲーム業界としては、MFCやATLぐらあらばOKで、わざわざ .NET を使う理由は無かったのですね。

で、どういう要望および内部抗争(?)があったのかは知りませんが、metro アプリケーションがタブレット型を主戦場としていることと、タブレット型ではゲームが流行ることを考えると、metro アプリ + DirectX(C/C++) の組み合わせは避けられない。しかし、C++/CLI では XAML が使えない。どういう理由か分からないけども、XAML を扱うように拡張できない(マンパワーがないのか、開発者が抜けてしまったのかはわかりません)。

という訳で、C++/CLI はばっさり切り捨ててしまって、XAML + C++ という組み合わせの新しいプラットフォームを作った訳です。これが「C++/CX」です。
なので、C++/CX の主戦場が、DirectX 廻りやネットワーク廻りなので、そのあたりが WinRT≒DCOM で補ってしまえば、「ひとまずは」.NET Framework はいらないわけです、C++/CLI をゲーム業界の方が使わないのと同じ理由で、metro アプリを作るときにも .NET Framework は要らないでしょう、という想像が働きます。

こういう経緯を考えてみると、先の憤懣の部分にコメントを付けて、

  • 「C++/CX って、C++/CLI の後継でも何でもないやんッ!!! まったく別物じゃ」
  • → もとも発祥は別物。ゲーム関係の必要性からだから。

  • 「C++/CX って、metro アプリケーションは作れるけれど、desktop アプリは作れないじゃんッ!!!」
  • → ゲームなんだから、metro アプリで十分。

    → desktop アプリは従来のインストーラーが必要なので、流行らないと思われる

  • 「C++/CLI って、metro アプリが作れないどころか、WPF アプリも相変わらず作れないじゃんッ!!!」
  • → C++/CLI は、元のままで良いので、WPF アプリを使えなくてもよい。

    → カラフルなUIを必要としないので、XAML を扱えなくてもよい。

  • 「C++/CX って、WinRT を扱えるけど、.NET Framework を扱えないじゃん、つーか、C++ の世界に戻らないとだめじゃんッ!!!」
  • → 多分「間に合わなかった」んじゃないかな?

    → そのうち様子を見て公開するかも。

  • 「そもそも、C++/CX と C++/CLI って混在できないじゃんッ!!!」
  • → C++/CX が .NET Framework を取り込めるようになったら、混在できるようになるかも。

という感じになります。

C++/CX のハマり処は、.NET Framework を使えないところです。もともとゲーム業界で働いている方の場合は、.NET を使わないので、C/C++ の関数/STLの扱いに慣れているので大丈夫だと思うのですが、.NET プログラマが、C++/CX を扱う場合にはハマり処が満載です。

ひとつあげると、C#/VB で良く使われる「String」は、C++/CXの「String」とは全く別物です。.NET の場合は「System.String」なのですが、C++/CX の場合は「Platform::String」なんですね。なので、String::Format メソッドが無くて「えッ!!!」と吃驚します。
ちなみに、C++/CLI の場合は「System::String」なんですよね~。このあたりの話はまた後程。

カテゴリー: C++, windows 8 | 10件のコメント

[win8] metro-desktopのプロセス間通信をWeb API風にする

[win8] MetroアプリからDesktopアプリへWCFで接続する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3387
[win8] metro アプリケーションからデスクトップアプリにプロセス間通信する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3379

# 追記 2012/05/12
# 再度確認したところ、localhost によるループバック接続はパッケージを作った時は駄目で、Visual Studio からデバッグ実行しただけ接続できます。このあたり、hosts 書き換え、ip 指定でも駄目なので、別の方式を考えないと。以下は、参考のため残しておきます。
# 業務的には、別マシンに proxy を立てて localhost->proxy->localohst にすれば ok なんですが、もうちょっとうまい方法を考えますか。ネットワーク負荷がかかるし。

なところで、WCF を使ってプロセス間通信できることは確認できたのですが、WCF だと metro アプリのほうで web の参照設定をしないといけません。まぁ、製品的にサーバーが先に固定化されている場合はいいのですが、流動的に作っている場合は先にインターフェースを決めないといけないというのはちょっと酷です。

な訳で、Web API 風に GET コマンドのアドレスを使って metro アプリ(クライアント)から desktop アプリ(サーバー)へ送信できるようにします。

■クライアントの metro アプリ側

private async void Button_Click_1(object sender, RoutedEventArgs e)
{
    string text = textBox1.Text;
    string url = "http://localhost:8083/metro/method";

    HttpClient client = new HttpClient();
    HttpResponseMessage res = await client.GetAsync(url + "/" + textBox1.Text);
    string response = await res.Content.ReadAsStringAsync();
    textBox2.Text = response;
}

metro アプリのほうでは、アドレスを指定してサーバーに接続します。Web API の REST のように「http://localhost:8083/metro/method/masuda/1000」とか「http://localhost:8083/metro/method?name=masuda&num=1000」のように呼び出すことを想定します。ここのサンプルでは適当に呼出URLを作っているだけなので、URLを作るための適当なラッパを作ると良いでしょう。

応答は、XML 形式でもよいのですが、自由に。

■Web APIを提供する desktop アプリ

HttpListener.BeginGetContext メソッド (System.Net)
http://msdn.microsoft.com/ja-jp/library/system.net.httplistener.begingetcontext(v=vs.110).aspx

を参考にして、HttpListener クラスでサーバーを作ります。前回は、同期メソッドを使ったために backgroundWorker コンポーネントでスレッドを使いましたが、今回は非同期メソッドの BeginGetContext、EndGetContext メソッドを使います。上記のサンプルコードではコールバック関数が static になっていますが、下記のように普通の内部メソッドを使うことができます。つーか、内部メソッドを使ったほうが、ListBox などの GUI にアクセスできるので便利かと。

using System.Net;
using System.IO;

namespace SampleLocalWebApiServer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        HttpListener listener;

        private void button1_Click(object sender, EventArgs e)
        {
            string url = "http://*:8083/metro/";
            // 開始
            listener = new HttpListener();
            listener.Prefixes.Add(url);

            listener.Start();
            listBox1.Items.Add("サーバー開始");
            listener.BeginGetContext(ListenerCallback, listener);
        }

        public void ListenerCallback(IAsyncResult result)
        {
            // HttpListener listener = (HttpListener)result.AsyncState;
            HttpListenerContext context;
            try
            {
                context = listener.EndGetContext(result);
            }
            catch
            {
                // stop メソッドで例外が発生するので、対処
                return;
            }
            // var content = listener.GetContext();
            var req = context.Request;
            var url = req.RawUrl;
            var res = context.Response;

            listBox1.Items.Add("受信");

            var output = new StreamWriter( res.OutputStream ) ;
            output.WriteLine(string.Format("called {0}", url));
            output.Close();

            // 次の受信の準備
            listener.BeginGetContext(ListenerCallback, listener);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // 終了
            listener.Stop();
            listBox1.Items.Add("サーバー終了");
        }
    }
}

サーバーの停止なのですが、Stop か、Abort を呼び出します。が、ちょっと面倒なのは、Stop メソッドを呼び出した途端にコールバックが呼び出されるんですよね…で、EndGetContext メソッドの呼び出し時に例外が発生してしまうので、コードでは try-catch で、回避しています。異常終了の場合と区別がつかないので、もうちょっとなんとかしたいところですね。

呼び出した URL は、RawUrl プロパティで取得できます。URL プロパティでもいいのですが、RawURL のほうが、サーバー名とポート名を削ってくれるので、処理が楽なのです(名前が逆っぽいのは、見ないことにしよう)。

■実行してみる

サーバーを管理者権限で動かして、metro app からつなげてみます。

無事接続できていますね。サーバー側で適当に振り分けてやれば、Web API として動かすことができます。URL Encode/Decodeすれば文字列は簡単です。複雑な場合は POST で送って、適当なクラスでラップすればよいですかね。というか、シリアライズ機能を使えば ok かも。

カテゴリー: C#, windows 8 | [win8] metro-desktopのプロセス間通信をWeb API風にする はコメントを受け付けていません

nullポインターがokな、オブジェクトをC#で実装する

WCFサービスを調べているときに見つけたので、ちょっとメモ的に。下記の SafeInvoke メソッドのところです。

Method call if not null in C# – Stack Overflow
http://stackoverflow.com/questions/872323/method-call-if-not-null-in-c-sharp

objective-c には便利な機能があって、変数が null の場合はメソッドを呼び出さないのです。このために null チェックがいりません。具体的にコードを示すと、


 NullObject *obj = [NullObject new];
 [obj callMethod];

 // null を代入
 obj = NULL;
 // 次の関数は呼び出されない
 [obj CallMethod];

ってな感じで、2回目の CallMethod は呼び出されません。 if ( obj != NULL ) というチェックがいらなくなってコードがシンプルになります。まあ、厳密性を重んじるならば NULL チェックをする「意図」は残しておいたほうがいいのですが、コードの安全性を考えるとこれで ok な気がします。

■拡張メソッドを使う

実は C# の拡張メソッドを使うと似たようなことができる、というのを先日知りました。元のクラスを NullObject にして、拡張メソッドを含むクラスを NullObjectExtention にしておきます。

namespace SampleNullObjectExtention
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var obj = new NullObject();
            Debug.Print("goMessage pre");
            obj.goMessage("masuda");
            Debug.Print("goMessage after");

            obj = null;

            Debug.Print("goMessageEx pre");
            obj.goMessageEx("tomoaki");
            Debug.Print("goMessageEx pre");

        }
    }

    public static class NullObjectExtention
    {
        public static void goMessageEx( this NullObject obj, string msg)
        {
            if (obj != null)
            {
                obj.goMessage(msg);
            }
        }
    }
    public class NullObject
    {
        public void goMessage(string msg)
        {
            Debug.Print("in goMessage {0}", msg);
        }
    }
}

すると、button1 をクリックしたときに、obj を null に設定しておいても、goMessageEx メソッド呼び出しは大丈夫なんですね。なるほど。拡張メソッド側で this で参照させて null チェックをするという技です。LINQ の内部でも使っているのかもしれません。

なかなか面倒ですが、メソッドを全てラップしてしまって、ラップした方のメソッドを使うというルールにすれば ok なんですが、いやいや、そうはいきません。インテリセンスで「goMessage」が出てくるならば、それを使ってしまうかもしれない。ならば、obj が null の場合は通常通り落ちてしまう訳です。使えない技ですね~という感じになってしまいます。

■別のアセンブリに隠す(internal protected)

C++のようにfriendで制限ができればよいのですが、.NETにはありません。その代り「internal protected」というちょっと中途半端な(便利な)範囲設定ができます。
拡張メソッドは、protected なメソッドを呼び出せないので、代替案という訳です。

テスト用のプロジェクトとは別に、クラスライブラリを作成します。

namespace CheckNullObjectLib
{
    ///
    /// NullObject の拡張クラス
    ///
    public static class NullObjectExtention
    {
        ///
        /// SetMessage の Wrapper
        ///
        /// <param name=&quot;me&quot; />
        /// <param name=&quot;msg&quot; />
        public static void SetMessage(this NullObject me, string msg)
        {
            if (me != null)
            {
                me._setMessage(msg);
            }
        }
        ///
        /// GetMessage の Wrapper
        ///
        /// <param name=&quot;me&quot; />
        /// <param name=&quot;msg&quot; />
        public static string GetMessage(this NullObject me)
        {
            if (me != null)
            {
                return me._getMessage();
            }
            // あえて空文字列を返す
            return &quot;&quot;;
        }
    }
    ///
    /// 本体の NullObject クラス
    ///
    public class NullObject
    {
        protected internal string _msg = &quot;&quot;;

        ///
        /// アクセス制限を protected internal(同じアセンブリのみ)にしておく
        ///
        /// <param name=&quot;msg&quot; />
        protected internal void _setMessage(string msg)
        {
            _msg = msg;
        }
        protected internal string _getMessage()
        {
            return _msg;
        }
    }
}

本体の NullObject クラスで公開するメソッド/プロパティは、全て protected internal にしておきます。こうすると、同じアセンブリ内のクラスからのみアクセスが可能になります。これを、ラップする NullObjectExtention クラスでは、public で拡張メソッドを作る訳です。

テストコードはこんな感じになります。

using CheckNullObjectLib;

namespace CheckTestNullObject
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private NullObject obj;

        ///
        /// set button
        ///
        /// <param name=&quot;sender&quot; />
        /// <param name=&quot;e&quot; />
        private void button1_Click(object sender, EventArgs e)
        {
            obj = new NullObject();
            obj.SetMessage(textBox1.Text);
        }

        ///
        /// get button
        ///
        /// <param name=&quot;sender&quot; />
        /// <param name=&quot;e&quot; />
        private void button2_Click(object sender, EventArgs e)
        {
            // いきなり set button を押して,
            // obj が null の状態でも大丈夫
            textBox2.Text = obj.GetMessage();
        }
    }
}

内部で保持している NullObject オブジェクト obj は、set button を押したときに new する訳ですが、間違って get button を押したとしても GetMessage メソッドで例外が発生しません。なんと便利なッ!!! objective-c の技がッ!!! って思いますが、まぁ、コーディングの基礎しては「きちんと obj は初期化しておこうね」とか「get button を押した時に null チェックをしようね」というのが C# のコーディングとして正しいやり方ですね。

■どんなところで使うのか?

使い処としては、null チェックを省くってのもそうなのですが、メソッドチェーンのところで途中に null を含められるというメリットがあります。
LINQ の定番として、

var data = ...
var result = data.From( ... ).Where( ... ).Select( ... ).OrderBy( ... );

という書き方をした場合、From, Where, Select メソッド null を返すと不意に落ちてしまう訳です。ひとつの方法としては、それぞれのメソッドが null を返さないように実装すればよいのです。もうひとつの方法としては、先の拡張メソッドのテクニックを使って null で落ちないという方法が使えます。

まあ、変数を敢えて null で返したいときに使えるかなぁと。

カテゴリー: C# | 1件のコメント

[win8] MetroアプリからDesktopアプリへWCFで接続する

昨日 [win8] metro アプリケーションからデスクトップアプリにプロセス間通信する の続き。

# 追記 2012/05/12
# 再度確認したところ、localhost によるループバック接続はパッケージを作った時は駄目で、Visual Studio からデバッグ実行しただけ接続できます。このあたり、hosts 書き換え、ip 指定でも駄目なので、別の方式を考えないと。以下は、参考のため残しておきます。
# 業務的には、別マシンに proxy を立てて localhost->proxy->localohst にすれば ok なんですが、もうちょっとうまい方法を考えますか。ネットワーク負荷がかかるし。

metro アプリで HttpClient クラスを使ってローカルホスト(localhost)に接続できることが分かったのですが、「さて、データ形式はどうしようか」ということで再考しておりました。 やっぱり、データ形式は XML 形式がいいよねと、どうせならばクライアントは WCF 形式で繋げられるとよいよね、と考えた挙句…ああ、WCF で使えばいいよね、とひと巡りして来てしまいました、という話。

「System.Net.HttpListenerException: アクセスが拒否されました。」と表示されてしまう
http://social.msdn.microsoft.com/Forums/ja-JP/wcfja/thread/4b1572df-a780-45b0-9488-cb4e3b95b53f

をよく見ると、実は WCF の話だったのですね。なるほど、というわけで

ServiceContractAttribute クラス (System.ServiceModel)
http://msdn.microsoft.com/ja-jp/library/system.servicemodel.servicecontractattribute.aspx
WCF クライアントを使用したサービスへのアクセス
http://msdn.microsoft.com/ja-jp/library/ms734691.aspx

を参考にしながら、

  • desktop アプリで WCF サーバー
  • metro アプリで WCF クライアント

を作成していきます。

■desktop アプリ(windows form アプリ)で WCF サーバー

基本は、ServiceContract、OperationContract 属性を使ってサービスで公開するメソッドを作るのと、ホストを ServiceHost クラスで作るところです。

using System.ServiceModel;
using System.ServiceModel.Description;

namespace SampleWcfMetroServer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        ServiceHost serviceHost;
        // MetroService host;

        private void button1_Click(object sender, EventArgs e)
        {
            // 開始
            serviceHost = new ServiceHost(
                typeof(MetroService),
                new Uri(&quot;http://localhost:8082/metro&quot;));
            //serviceHost.AddServiceEndpoint(
            //    typeof(IMetroService),
            //    new BasicHttpBinding(), &quot;&quot;);
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            serviceHost.Description.Behaviors.Add(smb);

            // これは無駄
            // host = new MetroService();
            // host.OnNotiry += host_OnNotiry;

            // 自分のオブジェクトを static に登録
            Form1.me = this;

            serviceHost.Open();
            listBox1.Items.Add(&quot;サービス開始&quot;);
        }

        void host_OnNotiry(string name)
        {
            // イベントを受ける
            listBox1.Items.Add(name + &quot; &quot; + DateTime.Now.ToString());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            serviceHost.Close();
            listBox1.Items.Add(&quot;サービス終了&quot;);
        }

        public void OnNotiry( string message )
        {
            this.listBox1.Items.Add( message );
        }

        public static Form1 me;
    }

    [ServiceContract]
    public interface IMetroService
    {
        [OperationContract]
        string GetDate(string name);
    }

    public class MetroService : IMetroService
    {
        public string GetDate(string name)
        {
            // 実行時にオブジェクトが作成されるので、こっちは効かない。
            if ( OnNotiry != null )
                OnNotiry(&quot;called GetDate&quot;);

            // 仕方がないので、static で公開したフォームに送信させる。
            Form1.me.OnNotiry(&quot;called GetDate&quot;);

            return string.Format(&quot;{0}:{1}&quot;, name, DateTime.Now.ToString());
        }

        // public Action<string> OnNotiry(string name);
        public delegate void OnNotiryHandler( string name );
        public event OnNotiryHandler OnNotiry;
    }
}

巷のサンプルでは、公開するクラス内で処理を行っていますが、そのままだと ListBox へ表示するなどの GUI 周りが使えないので、元のフォームにイベントメッセージを飛ばします。WCF で利用するクラスは、.NET リモートは違って接続時に動的に作成される(らしい)ので、フォームへのイベント飛ばしがちょっと妙なことになっています。このあたりは、あとで考えるということで。

■テスト用の WCF クライアントを作成

最初は、Windows Form アプリで試してみます。プロジェクトから先の WCF サービスを参照設定させるために、あらかじめ WCF サーバーを起動させておいて、プロジェクトから「サービスの参照を追加」します。WCF サーバーは管理者権限で起動するか、netsh を使ってポートを URL を登録しておきます。

image

ローカルのサービスの URL を指定して「Go」を押して検索します(このあたりは、Visual Studio 2010 も同じ)。

image

namespace SampleWcfMetroClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 送信
            var client = new MetroServiceReference.MetroServiceClient();
            string name = textBox1.Text;
            string res = client.GetDate(name);
            textBox2.Text = res;
        }

        private async void button2_Click(object sender, EventArgs e)
        {
            // 非同期送信
            var client = new MetroServiceReference.MetroServiceClient();
            string name = textBox1.Text;
            string res = await client.GetDateAsync(name);
            textBox2.Text = res;
        }
    }
}

面白いのは、.NET Framework 4.5 で作っているので自動的に、同期メソッドの GetDate と、非同期メソッドの GetDateAsync が用意されるところです。どちらを使ってもいいのですが、ここでは metro のために非同期版も作っておきます。

で、うまく送信ができれば準備完了。

■metro 版で WCF クライアントを作成

Form 版がテストができたので、本番の metro 版を作ります。Form 版と同じように「サービスの参照を追加」してから、適当にボタンを配置します。

namespace SampleWcfMetro
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class BlankPage : Page
    {
        public BlankPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name=&quot;e&quot;>Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            // 非同期送信
            var client = new MetroServiceReference.MetroServiceClient();
            string name = textBox1.Text;
            string res = await client.GetDateAsync(name);
            textBox2.Text = res;
        }
    }
}

前回の HttpClient と違うのは、Desktop 版でテストをしたコードがそのまま使えるということです。これは違いが大きいですよね。metro アプリの場合、シミュレーター等と使うのでいまいちデバッグがしづらいところがあります。そうなると基本的なコードは desktop アプリで確認しておいて、metro アプリに適宜追加してくという方法をとるほうが効率的にデバッグができます。

image

実行するとこんな感じ。意外とすんなり動きます。これならばプロセス間通信の代用にしてもよいかなと。

カテゴリー: C#, windows 8 | [win8] MetroアプリからDesktopアプリへWCFで接続する はコメントを受け付けていません

[win8] metro アプリケーションからデスクトップアプリにプロセス間通信する

発端は、.NETリモート通信を metro アプリから desktop アプリ(通常のwin8アプリ)に対して通信をさせたかった、ということです。metro アプリのデバッグログなんかを win8 アプリから見れたり、リモートデバッグしている元の PC から見られるようにするのが目的だったのです。

# 追記 2012/05/12
# 再度確認したところ、localhost によるループバック接続はパッケージを作った時は駄目で、Visual Studio からデバッグ実行しただけ接続できます。このあたり、hosts 書き換え、ip 指定でも駄目なので、別の方式を考えないと。以下は、参考のため残しておきます。
# 業務的には、別マシンに proxy を立てて localhost->proxy->localohst にすれば ok なんですが、もうちょっとうまい方法を考えますか。ネットワーク負荷がかかるし。

が、実は metro アプリでは .NET リモート通信ができません。.NET リモート通信を行うための条件として、

  • TCP/IP 通信ができること(内部ではHTTPで動作している)。
  • 共通のクラスを MarshalByRefObject で継承できること。
  • 共通のクラスを、「共通」で使えること。

になるわけですが、metro って上記の3つとも駄目で、

  • 直接 TCP/IP するクラスが用意されていない。ただし、HTTP だけは HttClient クラスがある。
  • MarshalByRefObject クラスがない。
  • metro のフレームワークと、.NET Framework 4.5 でそもそも異なる。

ってな感じで共通にクラスが使えません。まぁ、Windows Phone やら、他のバージョンのフレームワークに対しては「同じクラス」をアセンブリレベルでは共通に使えないので、当たり前といえば当たり前なのですが、同じ PC 内で、metro – desktop 間でアクセスできないのは、ちょっと辛い。

Client/Server の関係としては、metro が必ず Client になります。というのも、metro はばっくぐらうんどに入ってしまうとサスペンドをしてしまうので、Server にするにはあまり意味がないんですよね。それでも、iPhone の画像転送用の簡易 Http サーバーみたいなことができると便利なのですが、metro app の場合は desktop アプリとワンセットになって windows 8 として組み込まれるので、metro app 単体でなくても役に立つのです。まぁ、インストール自体が発生してしまうので、metro app の簡便性というのは低くなるのと、Store のほうがどうなのかという問題は残りますが…業務アプリならば別に問題なし。

■desktop のサーバーアプリ

HttpListener クラスを使って簡易的な HTTP サーバーを作ります。

using System.Net;
using System.IO;

namespace SampleProcessCommServer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // リスナー
        HttpListener listener;

        private void button1_Click(object sender, EventArgs e)
        {
            // 受け付けるURL
            string url = &quot;http://*:8081/metro/&quot;;
            // 開始
            this.listener = new HttpListener();
            this.listener.Prefixes.Add(url);
            listener.Start();
            // スレッド開始
            backgroundWorker1.RunWorkerAsync();

            listBox1.Items.Add(&quot;サーバー開始&quot;);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // 停止
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                var content = listener.GetContext();
                var request = content.Request;
                var response = content.Response;

                var output = new StreamWriter(response.OutputStream);
                string text = @&quot;<response>
    <name>SampleProccessCommServer</name>
    <date>&quot; + DateTime.Now.ToString() + @&quot;</date>
</response>
&quot;;
                output.WriteLine(text);
                output.Close();
            }
        }
    }
}

画面をブロックしないように、BackgroundWorker コンポーネントを使っていますが、.NET 4.5 なので例の async/await を使っても ok です。

URL の指定の仕方は、HttpListener クラス (System.Net) を参考にしてください。URL の書き方は、.NET リモートと似ています…というか、内部的にこれを使っているかと思われます。

レスポンスは XML 形式で書いていますが、文字列であればなんでも ok です。クライアント側の解析のためは XML 形式にしておいたほうがよいでしょう。

■desktop のクライアントアプリ

テスト用に desktop 版のクライアントアプリを作って試します。

using System.Net;
using System.IO;

namespace SampleProcessCommClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 送信
            var client = new WebClient();
            string url = "http://localhost:8081/metro/sample001";
            var output = new StreamReader( client.OpenRead(url));
            string content = output.ReadToEnd();
            output.Close();
            textBox2.Text = content;
        }
    }
}

接続先は自分自身なので「localhost」にしておきます。.NET リモート風にアドレスを指定しておいて、最終的にはサーバーのほうで分岐させるという感じになります。

さて、これでテストをするときに、サーバープログラムのほうで、「System.Net.HttpListenerException: アクセスが拒否されました。」とエラーがでます。実は、windows 7 あたりから、一般ユーザーの場合はサーバーのポートを開く権限がないのですよね。「System.Net.HttpListenerException: アクセスが拒否されました。」と表示されてしまう を参考にしてポートを設定するか、アプリを「管理者権限」で立ち上げてしまいます。

ポートの設定は、「netsh http add urlacl url=http://*:8081/metro/ user=masuda」のようにします。

すると無事に送受信ができます。

■metro アプリから接続する。

先にテストしたクライアントアプリを metro に書き写してしまえば ok …と思いきや、かなりコードが違います。

using System.Net.Http;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace SampleProcessComm
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class BlankPage : Page
    {
        public BlankPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name=&quot;e&quot;>Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            var client = new HttpClient();
            // GET で呼び出し
            var res = await client.GetAsync(&quot;http://localhost:8081/metro/sample1&quot;);
            // レスポンスを取得
            var cont = await res.Content.ReadAsStringAsync();
            textBoxOutput.Text = cont;
        }
    }
}

connect も get も非同期になるので、async/await を使う、ってのと HttpClient クラスを使うってのがミソです。

実行した結果がこんな感じ。input のところは無視していますが、きちんとレスポンスが返ってきます。

image

このソースの場合は localhost に送ったので、同じ PC に対してプロセス間通信をしていますが、別の PC に飛ばすこともできるので、実質 .NET リモートと同じように動作ができます。で、適当なシリアライズを考えてやれば、なにも MarshalByRefObject を継承する必要はなくて、異なる .NET Framework のバージョン間でも大丈夫という具合。当然、XML-RPC でも動くわけですから、相手が PHP や Java でも大丈夫。

まあ、このまま使うのはあまりにも面倒なので、適当にラップする必要がありますが…ってのと、Store とかに載せた場合はどうなるの?ってのがありますが。一応、プロセス間通信はできるということで。

カテゴリー: C#, windows 8 | 2件のコメント

Windows Developer Days の感想

感想をざっくりと30分位で記録しておきます。以前だときちっとレポートを書いたのだけど、年齢には勝てず…ってことにしておく。

Microsoft Windows Developer Days (WDD) ホーム
http://www.microsoft.com/ja-jp/events/wdd/default.aspx

「Developer」ってことなので、Wndows 8 の metro アプリケーション、Visual Studio 11 での開発ってのが中心になります。私としては、既に acer w500 に windows 8 cosumer preview を入れ、visual studio 11 beta で試して、って具合でもあり、あとは半年後のための情報収集ってことで、

  • Visual C++ 関係
  • Azure 関係
  • ASP.NET MVC 関係

ってところを中心に聴講してきました。

で、結論から先に書くと。

  • xaml/c++ が意外と有望かも。Direct3D 関係を直接扱ってゲーム関係で metro でいけます。
  • metro アプリケーションと従来型のデスクトップアプリケーションは、共存可能、つーか、使い分けないと駄目。
  • metro アプリは基本ワンタスクで動く(バックグラインドに入ればサスペンド)。なので、意外と大雑把な作りをしても ok かも。
  • Azure を含むクラウド関係は、10年越しで計画を立てて徐々に移行という感じ。
  • ASP.NET MVC は、Web API を実装したら、WCF とかいらんかも。

な様相を呈しております。

ワタクシ的には、Windows 95 か XP が出た頃のインパクトがあるかなぁと思っていて、完全に metro に移行するのか、それとも windows 7 に留まるのか、という感じがするのですが、どうなんでしょうねぇ。Windows 8 の使い勝手としては、metro スタイルとデスクトップスタイルは共存が可能/使い分けが可能なので、ゲームやブラウジングなんかは metro でやって、お仕事ツール(開発関係)はデスクトップスタイルという感じになると思うのです。実際、Visual Studio 自体は metro スタイルにならないし(そういう言及もありました)。

しかし、クライアント関係では「見栄え」のところで 、メトロスタイルが出てくるかもしれません。ただし、Silverlight がああいう感じになってしまったのを考えると、「見栄え」だけでは難しいところもあります。予算的にITにどれだけ投資できるか?というもありますからね。しかし、タブレットPC/スレートPC、スマートフォンでは、従来型のデスクトップアプリでは非常に扱いにくいのは確かです。かつての Windows CE や Windows Mobile を完全に廃して、「メトロスタイル」というものを打ち出して来たのは、英断だとは思います…が、本当ならば WPF や Siverlight を打ち出した時にデザインだけでも打ち出しておけば良かったのでは?と思ってしまいます。
まあ、Apple の特許に引っ掛かった(引っ掛かるかもしれない危険を回避した)とも言えますが。

そういう意味で、メトロスタイルというデザインは、Android のユーザーインターフェースが iPhone に酷似するようには、似ていません。なので、別路線を打ち出したということで、ひとまず「特許/意匠」関係で訴えられるリスクを回避しているのでしょう。少なくとも私はそう考えています。

なので、iPhone ほどスマートフォンを考え抜いてはいないものの、新しい「頂き」を構築したのが、今回の Windows 8 のメトロスタイルです。いまのところ「第一」とは言えないものの、「最低」ではありません。ほどよく中間的なデザインだと思います。Apple/iPhone が「ブランド路線」だとすると、Google/Android は「廉価版」路線になっています。5万円ほどの iPad に対して、2,3万円で Android タブレットが買えますからね。となると、Windows 8 のメトロスタイルはどうなるかというと、という問題があるのですが、実はブランドでも廉価でもない路線になるのではと思っています。

ここ2日間ほど、win8 on acer を使ってみて分かったのは、普段使っている Windows アプリを acer にインストールして持って行けるところです。従来の outlook や power point のインターフェースは、タッチパネルで操作するには非常に困難なのですが、動かない訳ではありません。これは、iPad や Android の場合は、別途アプリケーションをインストールしないといけないのに対して、「従来のアプリが使える」という大きなメリットが windows 8 にはあります。使いにくいけれども、使えないことはない、という無理矢理な路線は、そう、ms-dos や windwos 3.1 の頃の感覚に似ています。横目で、unix の堅牢さや mac の素晴らしいインターフェース(けれども非常に高価な)を横目に、なにやらプラモデル感覚でプログラミングをしていた頃を思い出させてくれます。
メトロスタイルという一見「デザイン的に優れた」と銘打ってはいるものの、裏の意味は…と勘繰ってしまったりしますね。

で、話を元に戻すと、Visual C++ のインテリセンスが復活したこと、XAML を VC++ から使えるようになったこと、WinRT という形で windows api を組み替えつつあること、(おそらく)DirectX 2D が復活すること、という事実から、microsoft は再び c++ 開発者を取り込みにかかっているように見えます。現在、様々なアプリケーションのサンプルは「C#」で提供されることが多いのですが(日本の場合は、歴史的にVBも含まれています)、これからは直接XAMLを扱えるようになることから無理矢理C#を使わなくてよい、ということになりそうです。

この無理矢理のところ、一例を出しておくと、

  • DirectX 関係は、元々 C言語インターフェース、COM インターフェースが多い。スピードを要求されるところで、C# を使うのは気が引ける。
  • OpenCV のような画像解析は、C/C++ インターフェースとなっている。
  • Fortran との接続も C/C++ のほうが楽。
  • おそらく、Python, Ruby, Java との相互運用も C# よりも C++ のほうが楽ではないか(想像だけど)。

ということがあります。C# の場合、.NET Framework の中に納まってしまうので、相互運用をしようとするとどうしてもランタイムなところが邪魔してしまうのですね。これは、C++/CLI ではなくて、C言語そのものからC#を呼び出すとその複雑さがよくわかります。そうなると、ネイティブなメモリのマッピングだけを考えると、C/C++ が有利なのですよ。まぁ、それ以外のところ(それ以外のところが非常に多いのですがw)は、C#やVBで書くわけですが。

ちなみに、Objective-C で iPhone アプリを書くと、結構べたべたなコードが出てきます。いわゆるプログラム言語のデザインパターンはC#やJavaのほうが豊富なのです。ですが、iPhone アプリ(iOSアプリ)のクラス群は結構厳格なポリシーに沿って書かれているんですよね。そのあたりがうまく出来ていて、

  • iPhone の場合は、なんにも考えなくても「そこそこのデザインの画面ができる」
  • Windows の場合は、なんにも考えないと「そこそこのデザインの画面しかできない」

という感じがしています。そうですね、このあたりは今週末の.NETラボの勉強会で話そうかな…とネタを考えてみたり。まだプレゼン資料ができていないので、考えるのは自由ですから。

カテゴリー: 雑談 | 3件のコメント