Visual Studio Experss 2012 RC for Windows 8 を試してみる

msdn を持っている(というか、MVPなので借りている状態)ので、正式版をリリース後でも Visual Studio 2012 Professional を使えるので特に問題はないのですが、一応、Express Edition のほうも試しておきましょうということで。

ダウンロード | Microsoft Visual Studio 2012
http://www.microsoft.com/visualstudio/11/ja-jp/downloads#express

なところで、無償版である Experss Edition がダウンロードできます。真ん中の「Express for Windows 8」をクリックしてダウンロードページに進みます。
ちなみに、「Express for Web」のほうは、ASP.NET MVC のような Web アプリケーション開発のためだけの環境です。「Express for Windows 8」のほうは、metro アプリケーションの開発環境ですね。どちらも無償で使える Visual Studio の環境です。ええと、注意しないといけないのは、Windows 8 の上でしか動かないのですよね。いままでの Windows 7 では動かないので、別途 Windows 8 のコンピュータを用意することになります。あるいは、仮想環境(VMWareなど)を使うとか。

ちなみに、従来型の desktop の開発環境は有償版である Visual Studio 2012 Professional でしか使えません。7万円程度と結構高価なので趣味に使うにはちょっとと思う場合には、Visual Studio 2010 Express のほうを使ってみてください。

Microsoft Visual Studio Express
http://www.microsoft.com/japan/msdn/vstudio/express/

.NET Framework のバージョンは 4.0 ですが、基本的なところは十分使えます。Visual Studio 2012 は 4.5 対応なので、その差分に気を付けないといけませんが、まぁ、趣味のツールを作る分には十分かと。

御馴染みの「新しいプロジェクト」を開くと、見事に「Windows Metro style」しか作れません。metro アプリを作るとなると、Windows Store に登録しなくちゃ駄目、という先入観がありますが(実際、microsoft社はそういう薦め方なのですが)、個人で使う分に「開発ライセンス」というものを取得すれば、別に Windows Store にアップしなくても使えます。
この開発ライセンスは

  • windows live id を取得して
  • 1ヶ月毎に開発環境で更新
  • たぶん、1ヶ月毎に実行環境で更新

すれば、ローカルコンピュータで使えます。普段使いのコンピュータであれば、1か月ごとの更新ぐらいならば、あまり手間ではないでしょう。
ただし、友達にアプリを配布ってことになると、ちょっと面倒ですよね。

さて、面白いことに Express Edition の場合には、Professional 版ではなかった「ストア」というメニューがあります。これは、有償版(いまではテスト的に無償でダウンロードできますが)の Visual Studio 2012 には無かったメニューです。

開発ライセンスの取得は、「ストア」→「開発ライセンスの取得」でできます。Windows Live ID は無料なので、無い場合は適当に取ってください。



良く見ると有効期限が「2012/07/07」になっていますね。今日が「2012/06/07」なので、丁度1ヶ月の有効期限となっているのです。1か月後にどうなるかというと、単に最初の開発ライセンスを要求するダイアログが出るだけです。再び、Windows live Id を入力して登録すれば ok です。「期限切れになる前に~」と書かれていますが、期限が切れてしまった場合でも最初に起動すると「開発ライセンス」を要求されるので大丈夫です。ただ、その時にネットワークに繋がっていないと、(多分)ライセンスの更新ができなくてアプリを起動できないってことになるので、ノートブックにインストールしている場合は早めに更新しておくのが吉かと。

開発ライセンスを取得した後に「ストア」→「開発者アカウントを開く」を選択すると、デベロッパーセンターに接続します。


Windows Store 用の登録者コードを貰って、年間4,900円払えば公開できますよ、ってな感じです。現在登録者コードは、microsoft さんの dev camp に参加する(だったっけ?)ことになっています。まあ、個人の場合は正式公開を待ってもよいかなと。

Visual Studio 2012 Pro で作った metro アプリを Experss Edition で開くと、きちんと開けます。ざっと見たところ、metro アプリ開発に関しては有償版の Visual Studio と変わらないようです。多分、機能制限もないでしょう。

最終的には windows store を利用するんでしょうが、ツールを使ったり単純に無償で配布する場合は、ソースコードを github などで公開するか、パッケージを作る良いです。アプリパッケージについては後日。

追記 2012/06/09

とか思ったらこんなものが、desktop 版も出すような記述が。

Visual Studio Express 2012 for Windows Desktop – The Visual Studio Blog – Site Home – MSDN Blogs
http://blogs.msdn.com/b/visualstudio/archive/2012/06/08/visual-studio-express-2012-for-windows-desktop.aspx

 

カテゴリー: 開発, windows 8 | 1件のコメント

[win8] SurfaceImageSource を使って、metro アプリに部分的に DirectX 描画する

[win8] 画像加工をDirectXに任せて、UIはC#にする技 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3454

なところで、metro アプリで DirectX を使おうとしていたのですが、もっと良い方法がありました。既に、SurfaceImageSource ということで DirectX を使えるように用意がされているのですね。

わんくま大阪#49の資料
http://jyurimaru.info/data/20120602wankuma_osaka49/20120602_sao.pdf
SurfaceImageSource
http://blogs.msdn.com/b/windowsappdev_ja/archive/2012/03/22/xaml-directx.aspx
DirectX and XAML interop (Metro style apps using C++ and DirectX) (Preliminary)
http://64.4.10.18/ja-jp/library/hh825871.aspx

なところに、SurfaceImageSource を使って、DirectX で描画できます。と書いてあるのです。

SurfaceImageSource^ surfaceImageSource =
    ref new SurfaceImageSource(rectangle1->Width, rectangle1->Height, true);
ImageBrush^ brush = ref new ImageBrush();
brush->ImageSource = surfaceImageSource;
rectangle1->Fill = brush;

ImageBrush オブジェクトに設定して、XAML の UI コントロール(ここでは、Rectangle コントロール)の Fill プロパティに設定しています。簡単に言えば、背景で使う bitmap 型のブラシを作って rectangle コントロールに設定している訳です。bitmap オブジェクトへの描画を DirectX でやるということです。リアルタイムに描画、とは違うのですが、まぁ普通の画像を描画するだけならばこれで十分です。コントロール同士の重なりや、透明度も指定できるので相当汎用的に使えます。

が…問題はですね、この surfaceImageSource ってのにどうやって描画するんですか?というのが、なかなかぶち当たりません。つーか、「省略」してるだろッ!!!(意図的か無意識的かわかりませんが)ってな具合です。DirectX に詳しければ ok なんだけど、詳しくないと途端に分からんという(私を含む)有様 orz

■どうやって、SurfaceImageSource に描画するのか?

結論から言うと、

Direct2D Quickstart for Windows 8 Release Preview
http://msdn.microsoft.com/en-us/library/windows/desktop/hh780340(v=vs.85).aspx

あたりに書いてありました。直接ではないのですが、「Step 3: Create an ID2D1Device and an ID2D1DeviceContext」の「ID2D1DeviceContext::CreateBitmapFromDxgiSurface」の記述が肝です。
SurfaceImageSource オブジェクトを、描画コンテキスト(ID2D1DeviceContext)に結びつけるために、CreateBitmapFromDxgiSurface メソッドを使う訳ですね。なるほど。知らないと分かりませんがな orz

結びつける先は、bitmap でなくても良いらしくテクスチャを使っているサンプルもあるのですが、ComPtr<ID2D1Bitmap1> を使うのが一番楽っぽいので、こんな風に書きます。
Native の surface で、BeginDraw/EndDraw を使うのですが、その間にコンテキストの方の BeginDraw/EndDraw を突っ込めば ok … というか、このコンテキストを使った描画部分は別に、surface の描画の中にある必要はなくって、bitmap に書き込んであるので別のところで描画しても大丈夫です。そのあたりは後程。

	ComPtr<IDXGISurface> surface;
	RECT updateRect = { 0,0,400,300 };
	POINT offset = { 0,0 };

	HRESULT beginDrawHR = m_sisNative->BeginDraw(updateRect, &surface, &offset);
	// draw to IDXGISurface (the surface paramater)

	ComPtr<ID2D1Bitmap1> bitmap;
	m_d2dContext->CreateBitmapFromDxgiSurface( surface.Get(), NULL, &bitmap );
	m_d2dContext->SetTarget( bitmap.Get());

	m_d2dContext->BeginDraw();
	m_d2dContext->Clear(D2D1::ColorF(D2D1::ColorF::Green));
	
	Platform::String^ text = &quot;日本語&quot;;
    IDWriteTextFormat *pTextFormat = NULL;
    ID2D1SolidColorBrush *pBlackBrush = NULL;

		// static const WCHAR sc_fontName[] = L&quot;Calibri&quot;;
		static const WCHAR sc_fontName[] = L&quot;HGP明朝E&quot;;
        static const FLOAT sc_fontSize = 32;

        m_dwriteFactory->CreateTextFormat(
            sc_fontName,
            NULL,
            DWRITE_FONT_WEIGHT_NORMAL,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            sc_fontSize,
            L&quot;&quot;, //locale
            &pTextFormat
            );

        m_d2dContext->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::Black),
            &pBlackBrush
            );

        m_d2dContext->DrawText(
			text->Data(),
			text->Length(),
            pTextFormat,
            D2D1::RectF(0, 0, 400,300),
            pBlackBrush);
	
	m_d2dContext->EndDraw();

	m_sisNative->EndDraw();
	// The SurfaceImageSource object's underlying ISurfaceImageSourceNative object contains the completed bitmap.
	ImageBrush^ brush = ref new ImageBrush();
	brush->ImageSource = surfaceImageSource;

	// fill;
	rect1->Fill = brush;

これだと、再描画するたびに bitmap を作成するので処理の無駄ですよね。あらかじめ別のところで bitmap を作成しておくのがベターです。

実行すると、こんな風に扱えます。きちんと透明度(Opacity)も実行できているし、コントロールのz-index(描画順序)も有効なので、かなり使えます。

さて、グラフ描画のような静的な画像はこれでいいのですが、少しアニメーションをしようとするとどうなるのか?が問題ですよね。このあたりは、後で調べることにしましょう。
定期的に ImageBrush の中身を変えて、再描画をすれば変わるような気もするけど…どうなのかな?

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

Community Open Day 2012 の準備

Community Open Day 2012
http://cod.ms/
東京会場
http://cod.ms/Pages/place_tokyoa.aspx

6/9(土)の東京会場の13:00から「Visual Studio 11 beta とスレートPCを使ったデバッグ講座」ということで発表をするのですが、そのアプリの準備。
基本は「スレートPCにインストールした windows 8 に metro アプリをインストールして、リモートデバッグをするよ」という話です。

将来的に顧客が「スレートPC」を使ったときには、開発環境を入れないので、テスト用環境として機器を1台購入すると考えられます。普通のPCでも最終的にはインストール対象マシンを用意しますよね。この場合、通常の PC と異なる点は、

  • Visual Studio からのリモートデバッグはどうやるのか?
  • デバッグ出力は何処に出すのか?
  • スレートPC特有のテスト/デバッグ項目は何か?

ってところです。そのあたりを、

  • acer w500 にインストールした windows 8 consumer preview 版
  • ノートPC のvmare 上にインストールした windows 8 + visual studio 11 beta

で実演するというお話になります。ノートPCに vmware を入れるのは、ベースが windows 7 なので将来的にはノートPCなりデスクトップマシンに windows 8 + visual studio 11 という組み合わせになると思います。ノートPCに入れておくと、こういった実演が楽というのがありますが、ちょっとパワー不足なんですよね。

そこで今週はデモアプリ作り、来週にはざっとプレゼン作りをするわけですが、例によってプレゼン資料はチープなものになりそうです。毎度、.NET ラボ勉強会でのは発表ではチープすぎて、場合によってはテキストファイルだったりする訳で、そのあたりはご容赦。

ご覧の通り、ネタアプリはこんな感じ。iPad のメモ帳を丸ごと画面キャプチャして使っています(笑)。

スレートPC 特有の回転にも対応しています

ってことで、デバッグ実演用にもう少し機能を追加しないと駄目なのですが、ひとまずネタとしては面白いかと。
ええ、このままだ意匠関係で windows store にアップできませんよね~。どうせならば、少し意匠をクリアさせて完成したものをアップしてみたいと思ったり、どうなんでしょうねぇ。

カテゴリー: C#, windows 8 | Community Open Day 2012 の準備 はコメントを受け付けていません

ボタンの hover 切り替えのための画像を作成するツール

チープなツールシリーズ第1弾ッ!!! ということで(多分、第1弾でおしまい。単なるアクセス解析のためのブログ更新だし)。

マウスをホバーさせる時の画像の作り方/スクリプトの書き方は色々あるわけですが、CSS を使って、

hoverでリンク画像を切り替える – スタイルシートTIPS ふぁくとりー
http://www.nishishi.com/css/link-image-hoverchange.html

な方法が使えます。

.menu#menu1 a {
	display: block;
	width: 160px;
	height: 60px;
	text-indent: -4000px;
	background-image: url(./image/menu1.png);
}
.menu a:hover {
	text-decoration: none;
	background-position: top right;
}

昔は JavaScript で切り替えたのですが、CSS だとこれが便利かなと。ホバー前の画像とホバー時の画像がひとつにまとまっているのでロード時に読み込まれるのがいいのです。ホバー時に読み込むと、読み込みが遅かったりしてなかなか画像がでなかったりするので。
で、このホバー用の画像、photoshop とかを持っていると作るのは簡単なんでしょうが(多分、Blend でも良いかと)、適当な画像ツールがない私としてはひと苦労で、大抵は Excel のワードアートを使っているという始末。

これを横に並べるのが面倒…なので、それ専用のツールを作りました。

構想はお昼の散歩時に15分ぐらいで、製作は30分ぐらい。前後合わせると1時間ちょっとというところでしょうか。この手のツールは、画像加工をするときのイライラ度合を考えると、手元で作っていたほうが精神的によいのです。
で、ソースの全文はこんな感じ。約100行位。

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

        private Bitmap bmpNormal;
        private Bitmap bmpHover;
        private Bitmap bmpMenu;

        private void Form1_Load(object sender, EventArgs e)
        {
			Properties.Settings.Default.Reload();
			textLeft.Text = Properties.Settings.Default.ImageLeft.ToString();
			textTop.Text = Properties.Settings.Default.ImageTop.ToString();
			textWidth.Text = Properties.Settings.Default.ImageWidth.ToString();
			textHeight.Text = Properties.Settings.Default.ImageHeight.ToString();
        }

		private void Form1_FormClosed(object sender, FormClosedEventArgs e)
		{
			Properties.Settings.Default.ImageLeft = int.Parse(textLeft.Text);
			Properties.Settings.Default.ImageTop = int.Parse(textTop.Text);
			Properties.Settings.Default.ImageWidth = int.Parse(textWidth.Text);
			Properties.Settings.Default.ImageHeight = int.Parse(textHeight.Text);
			Properties.Settings.Default.Save();
		}

		private void btnNormal_Click(object sender, EventArgs e)
        {
            bmpNormal = new Bitmap(Clipboard.GetImage());
            picNormal.Image = bmpNormal;
        }

        private void btnHover_Click(object sender, EventArgs e)
        {
            bmpHover = new Bitmap(Clipboard.GetImage());
            picHover.Image = bmpHover;
        }

        private void btnToClip_Click(object sender, EventArgs e)
        {
            Rectangle rect = new Rectangle(
                int.Parse(textLeft.Text),
                int.Parse(textTop.Text),
                int.Parse(textWidth.Text),
                int.Parse(textHeight.Text));

            bmpMenu = new Bitmap(rect.Width*2, rect.Height);
            Graphics g = Graphics.FromImage(bmpMenu);
            g.FillRectangle(Brushes.White, new Rectangle(0, 0, rect.Width * 2, rect.Height));
            g.DrawImage(bmpNormal,
                new Rectangle(0, 0, rect.Width, rect.Height),
                rect,
                GraphicsUnit.Pixel);

            g.DrawImage(bmpHover,
                new Rectangle(rect.Width,0, rect.Width, rect.Height),
                rect,
                GraphicsUnit.Pixel);
			Clipboard.SetImage(bmpMenu);

            picMenu_MouseLeave(sender, e);
        }

        private void picMenu_MouseEnter(object sender, EventArgs e)
        {
            if (bmpMenu == null) return;
            Rectangle rect = new Rectangle(
                int.Parse(textLeft.Text),
                int.Parse(textTop.Text),
                int.Parse(textWidth.Text),
                int.Parse(textHeight.Text));
            Bitmap bmp = new Bitmap(rect.Width, rect.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(bmpMenu,
                new Rectangle(0, 0, rect.Width, rect.Height),
                new Rectangle(rect.Width, 0, rect.Width, rect.Height),
                GraphicsUnit.Pixel);
            picMenu.Image = bmp;
        }

        private void picMenu_MouseLeave(object sender, EventArgs e)
        {
            if (bmpMenu == null) return;
            Rectangle rect = new Rectangle(
                int.Parse(textLeft.Text),
                int.Parse(textTop.Text),
                int.Parse(textWidth.Text),
                int.Parse(textHeight.Text));
            Bitmap bmp = new Bitmap(rect.Width, rect.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(bmpMenu,
                new Rectangle(0, 0, rect.Width, rect.Height),
                new Rectangle(0, 0, rect.Width, rect.Height),
                GraphicsUnit.Pixel);
			picMenu.Image = bmp;
        }
    }
}

本来ならば、画像ファイルの保存や、Excel との連携、ドラッグアンドドロップなどを考えたりするのですが、そんなのは面倒なのでクリップボード経由で Clipboard.GetImage() で画像を取り込んでいきます。保存先もクリップボードなので、IrfanView を使うかペイントに貼りつけます。

MouseEnter/MouseLeave イベントのところは、試しにマウスをhoverさせて確認できるようにしてあるところですね。このところ、hover されるたびに new Bitmap をしているので、hover するたびにメモリががんがん増えるという…苦笑レベルなのですが、まあツールなので良しとしましょう。本来ならば、Bitmap オブジェクトを使い廻すようにすればよいのです。

さて、これを作っているときに思ったのですが、この操作は metro アプリでは苦手な部類になります。
手順を簡単に書いてみると、

  1. Excel で通常画像とホバー画像の二種類の画像を作成する。
  2. Excel で通常画像を選択して、クリップボードへコピー
  3. 画像加工ツールを起動して、「Set Normal」ボタンをクリックしてペースト
  4. Excel に戻って、ホバー画像を選択して、クリップボードへコピー
  5. 画像ツールに戻って、「Set Hover」ボタンをクリックしてペースト
  6. 「ToClip」ボタンを押して、加工画像をクリップボードへ
  7. ペイントに、クリップボードがから画像をペースト
  8. ペイントで、ファイル名を付けて保存する。

という具合。ユースケースから見ると、Excel、画像ツール、ペイントを行き来します。
metro アプリの場合、新しいアプリを起動させてしまうと元のアプリに戻るのが結構面倒です。左からタスクを出して元のアプリを選択して、って具合になります。デスクトップアプリだと複数の Window を並べておいてクリックします。あるいは、Ctrl+Tab で移動します。まぁ Ctrl+Tab 自体は metro でも有効なのですが、「Window を並べておいて、mouse でクリックする」という動作はできないわけです。

この不便さは、実は iPhone/iPad にもあって、twitter アプリでブラウザを起動すると元に戻ることが面倒なので、アプリ内ブラウザで凌いでいます。本来ならばメールアプリやその他のツールと行き来できたほうがいいのですが、以前はクリップボードが無かった時代があったり、シングルタスクであったりしたものですから、このあたりの連携は微妙なところがあります。

windows 8 の metro app の場合は「プロトコル」ということで「mailto://」みたいなのが自由に作成できます。ただし、このプロトコルや通知は、事前にデータ形式を決めておかないと駄目という微妙なところがあって、なんか妙なところでハマりそうなんですよねぇ。どうなんですかね?

クリップボードの場合は、複数の形式のデータを同時に置くことができるので、例えば Excel の場合は、

  • テキストデータ
  • HTML形式のデータ
  • リッチテキスト形式のデータ
  • 画像データ
  • Excel 独自の書式付きのデータ

などが、いちどにクリップボードに送られます。で、クリップボードからデータを取り出す側で、適当な形式のデータのみ取り出せるという方式になっているのです。metro app の通知の場合には、どうなんでしょう。共有コントラクトあたりを使うか、それとも通知だけプロトコルを使って後はクリップボード経由とか?

「コントラクト」でMetroスタイル・アプリのサンドボックスを乗り越える! - @IT
http://www.atmarkit.co.jp/fdotnet/chushin/readyforwin8app_02/readyforwin8app_02_02.html

カテゴリー: ツール | ボタンの hover 切り替えのための画像を作成するツール はコメントを受け付けていません

static.ak.facebook.com からのアクセスを拒否する方法

最近、アクセスログを見ていると短時間(1時間以内)のアクセスが3000程伸びることがあり、妙な感じだったので、アクセスログを詳しくみてみました。
リファラーを見ると、「static.ak.facebook.com」からのアクセスとなっているのですが、どうやら、facebook のタブページをするときに使う何かのようですね。何なのかは知らないのですが、プロキシか何のようです。

で、ログを見ると、

xxx.179.39.32 - - [27/May/2012:19:52:47 -0600] "GET /blog/archives/2904?fb_xd_fragment HTTP/1.1" 200 56594 "http://static.ak.facebook.com/connect/xd_arbiter.php?version=6" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
xxx.179.39.32 - - [27/May/2012:19:52:48 -0600] "GET /blog/archives/2904?fb_xd_fragment HTTP/1.1" 200 56791 "http://static.ak.facebook.com/connect/xd_arbiter.php?version=6" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
xxx.179.39.32 - - [27/May/2012:19:52:48 -0600] "GET /blog/archives/2904?fb_xd_fragment HTTP/1.1" 200 56749 "http://static.ak.facebook.com/connect/xd_arbiter.php?version=6" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
xxx.179.39.32 - - [27/May/2012:19:52:49 -0600] "GET /blog/archives/2904?fb_xd_fragment HTTP/1.1" 200 56739 "http://static.ak.facebook.com/connect/xd_arbiter.php?version=6" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
xxx.179.39.32 - - [27/May/2012:19:52:51 -0600] "GET /blog/archives/2904?fb_xd_fragment HTTP/1.1" 200 56751 "http://static.ak.facebook.com/connect/xd_arbiter.php?version=6" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
xxx.179.39.32 - - [27/May/2012:19:52:51 -0600] "GET /blog/archives/2904?fb_xd_fragment HTTP/1.1" 200 56766 "http://static.ak.facebook.com/connect/xd_arbiter.php?version=6" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"

な感じで、1秒に1回程度のアクセスが2,30行続くという実に迷惑なアクセスの仕方がッ!!! 何かのページを開いていて、そこから定期的にアクセスをしているようなのです。最初はクロールのためのロボットが暴走しているのかと思ったのですが、アクセス元の IP を見るとばらばらなので、facebook が提供しているページか facebook のアプリページが駄目なようですね。

という訳で、仕方がないので .htaccess を使って static.ak.facebook.com を拒否します。

.htaccessで参照元(Referer)によるアクセス制限する方法
http://www.shtml.jp/htaccess/referer.html

当たりを参考にして、指定した referer を拒否します。

# limit access
SetEnvIf Referer "^http://static\.ak\.facebook\.com/" ref_ng
order allow,deny
allow from all
deny from env=ref_ng

ひとまず、これで様子を見ることにしましょう。

カテゴリー: 開発 | 2件のコメント

[win8] C++/CX から C# が使えるよ、という話

[win8] c++/cx から c# のライブラリは使えない…と思ったが使える | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3429

のところで「使えない~」ということを確認したかったのですが、「使えるよ」というのを教えて頂きました。

C# のプロジェクト出力で「WinWD ライブラリ」にすると、C++/CX や Javascript から使えるようになります。公開するクラスは sealed、メソッドの引数や戻り値はネイティブの型のみ(だったと思う)しかダメなので、若干制限はありますが、それなりに使えます。と言いますか、結構使えます。

どういう使い方をするかというと、

  • C++/CX では .NET Framework が扱えないので、ライブラリを使ったほうが良い処理は C# でライブラリを作る。
  • String 関係のような便利メソッドを持っているクラスを一度、C# 側で処理して C++/CX に戻す

というのが考えらます。先日のパターンで云えば、

  • DirectX のようなライブラリは C++/CX で記述する。
  • UI 部分は、C# で作成する。
  • ライブラリで C++/CX ではややこしい部分は C# で書く

という分け方です。つまりは、C# -> C++/CX -> C# という使い方ができるという訳です。実に便利。

試しに、System.String の Format メソッドを使うパターンを書いてみましょう。

■C# でライブラリを作成する。

namespace CSharpString
{
    public sealed class CsString
    {
		/// <summary>
		/// 可変引数のメソッド
		/// </summary>
		/// <param name=&quot;fmt&quot;></param>
		/// <param name=&quot;args&quot;></param>
		/// <returns></returns>
		
		[Windows.Foundation.Metadata.DefaultOverload]
		public static string XFormat(string fmt, params object[] args)
		{
			return string.Format(fmt, args);
		}

		public static string Format(string fmt)
		{
			return fmt;
		}
		public static string Format(string fmt, object arg1)
		{
			return string.Format(fmt, arg1);
		}
		public static string Format(string fmt, object arg1, object arg2)
		{
			return string.Format(fmt, arg1, arg2);
		}
    }
}

■C++/CX で C# のライブラリを利用する。

void UseCSharpStringInCppCx::BlankPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
	String^ name = L&quot;tomoaki&quot;;
	int age = 44;
	this->textBox1->Text = CSharpString::CsString::Format( &quot;masuda {0} {1}&quot;, name, age ); 
}



void UseCSharpStringInCppCx::BlankPage::Button_Click_2(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
	String^ o = CSharpString::CsString::Format(L&quot;only masuda&quot;);
	textBox1->Text = o ;
	String^ o1 = CSharpString::CsString::XFormat(L&quot;masuda {0} {1}&quot;, &quot;tomoaki&quot;, 30 );
}

■でも、可変引数は使えない

public static string XFormat(string fmt, params object[] args)
{
	return string.Format(fmt, args);
}

C# のほうで、可変引数(params object[])を使っているのですが、実は C++/CX では使えません。今回の場合は、String::Format をエミュレートしたかったので(C++/CX では Platform::String なので編集関係のメソッドがない)可変引数が欲しかったのですが、無理みたいですね。公開するメソッドの型が COM に準じるのでこういう風になっているのかもしれません。

でも、まぁ、普段は可変引数なんて使わないわけで、先のC#のメソッドのように、引数を複数指定すればokなので、これは良しということで。

カテゴリー: C#, C++/CX, windows 8 | [win8] C++/CX から C# が使えるよ、という話 はコメントを受け付けていません

[win8] 画像加工をDirectXに任せて、UIはC#にする技

[win8] スタート画面のタイルを動的に作成する(前哨戦) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3443

からちょっと横道に逸れて、画像データ(BitmapImage)に直接書き込む方法を紹介しておきます。
metro の Image コントロールには Source プロパティに BitmapImage オブジェクトを直接指定できます。BitmapImage オブジェクトは画像ファイルから指定もできるのですが、直接メモリから作ることもできます、ってことです。単純なところでは、こんな感じ。

private async void Button_Click_2(object sender, RoutedEventArgs e)
{
	var mem = new InMemoryRandomAccessStream();
	var enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, mem);

	int width = 140, height = 140;
	int plane = 4;
	byte[] data = new byte[width * height * plane];
	int offset = 0;
	for (int y = 0; y < width; y++)
	{
		for (int x = 0; x < height; x++)
		{
			data[offset + 0] = 0xFF; // R
			data[offset + 1] = 0x00; // G
			data[offset + 2] = 0x00; // B
			data[offset + 3] = 0xFF; // Alpha
			offset += 4;
		}
	}
	enc.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight,
		(uint)width, (uint)height, 96, 96, data);
	await enc.FlushAsync();

	BitmapImage bmp = new BitmapImage();
	bmp.SetSource(mem);
	image1.Source = bmp;
}

これを実行すると、140×140 の矩形が赤で塗りつぶされます(これ自体のサンプルがあたのですが、URLを忘却。見つけたら記録しておきます)。

メモリ上で操作する場合は、byte[] と InMemoryRandomAccessStream を使います。このあたりいくつかの方法があるのですが、結論から言えば「方法」自体はどうでもよくて、.NET Framework のように System.Drawing が使えると便利なんですけどねぇ、という具合です。

こんな風に色を塗りつぶすだけならば簡単にでるし、画像を貼りつけることも可能です。回転などのメソッドもあったので、そのくらいはできそうなのですが、根本的に機能不足は否めません。

なので、先の記事のように DirectX を直接扱います。 DirectX は C++/CX から扱うことになるので、C#やVBのプログラマには辛いところがあります。また、C++/CX から直接.NET Framework を扱えない(C#を経由して扱うことは可能)なので、これも痛しかゆしという具合です。なので、本格的なゲームを作る場合には全画面を DirectX を使うのが良いのでしょうが、ちょっとしたパズルゲームや画像の加工ツールぐらいならば、

  • DirectX を扱う画像加工は C++/CX に押し込める
  • UI を扱う部分は C# で扱う

分け方をするほうがベターです。

ひとまず、全ソースは github からダウンロードできます。

win8/DynamicMakeWritableBitmap
https://github.com/moonmile/win8/tree/master/DynamicMakeWritableBitmap

余計なところは随分削ったつもりなのですが、まだライブラリにするには冗長な感じなので、いまいちなのですが。Direct2D アプリケーションを作る時に自動作成される DirectXBase.* から抜き出してきたのが、次のソースです。

#pragma once

namespace DX
{
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            // Set a breakpoint on this line to catch DirectX API errors
            throw Platform::Exception::CreateException(hr);
        }
    }
}

namespace MakeBitmapLib
{
    public ref class DXBitmap sealed
    {
    public:
        DXBitmap();
		void Initialize();
		Platform::Array<unsigned char>^ MakeText( Platform::String^ text );

protected:
    // Direct2D Objects
    Microsoft::WRL::ComPtr<ID2D1Factory1>          m_d2dFactory;
    Microsoft::WRL::ComPtr<ID2D1Device>            m_d2dDevice;
    Microsoft::WRL::ComPtr<ID2D1DeviceContext>     m_d2dContext;
    Microsoft::WRL::ComPtr<ID2D1Bitmap1>           m_d2dTargetBitmap;

    // DirectWrite & Windows Imaging Component Objects
    Microsoft::WRL::ComPtr<IDWriteFactory1>        m_dwriteFactory;
    Microsoft::WRL::ComPtr<IWICImagingFactory2>    m_wicFactory;

    // Direct3D Objects
    Microsoft::WRL::ComPtr<ID3D11Device1>          m_d3dDevice;
    Microsoft::WRL::ComPtr<ID3D11DeviceContext1>   m_d3dContext;
    Microsoft::WRL::ComPtr<IDXGISwapChain1>        m_swapChain;
    Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_renderTargetView;
    Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_depthStencilView;

	D3D_FEATURE_LEVEL                              m_featureLevel;
    Windows::Foundation::Size                      m_renderTargetSize;
    Windows::Foundation::Rect                      m_windowBounds;
    Windows::UI::Core::CoreWindow^                 m_window;
    float                                          m_dpi;
	};
}

protected なところは、DirectX 関係で触る COM ですね。余分なところも初期化されてしまいますが、まぁ、そのままコピーしてき持ってきています。

そして、この DirectX を扱う為のヘッダファイルを pch.h に入れておきます。pch.h はプリコンパイルヘッダなので、固定化されているヘッダファイルを突っ込んでおきます。

#pragma once
</p>
<p>
#include <wrl.h>
#include <d3d11_1.h>
#include <d2d1_1.h>
#include <d2d1effects.h>
#include <dwrite_1.h>
#include <wincodec.h>

C++ のソースコードは次な感じですね。Initialize メソッドで初期化しておいて、MakeText メソッドで画像を作成します。Initialize メソッドの中身は DirectXBase.cpp のものを適宜コピーして使っています。

// WinRTComponent.cpp
#include &quot;pch.h&quot;
#include &quot;WinRTComponent.h&quot;

using namespace MakeBitmapLib;
using namespace Platform;
using namespace Microsoft::WRL;
using namespace Windows::UI::Core;
using namespace Windows::Foundation;
using namespace D2D1;
using namespace Windows::Storage::Streams;

DXBitmap::DXBitmap()
{
}
void DXBitmap::Initialize()
{
	// CreateDeviceIndependentResources
    D2D1_FACTORY_OPTIONS options;
    ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));

#if defined(_DEBUG)
     // If the project is in a debug build, enable Direct2D debugging via SDK Layers.
    options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif

    DX::ThrowIfFailed(
        D2D1CreateFactory(
            D2D1_FACTORY_TYPE_SINGLE_THREADED,
            __uuidof(ID2D1Factory1),
            &options,
            &m_d2dFactory
            )
        );

    DX::ThrowIfFailed(
        DWriteCreateFactory(
            DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            &m_dwriteFactory
            )
        );

	DX::ThrowIfFailed(
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_PPV_ARGS(&m_wicFactory)
            )
        );

    // This flag adds support for surfaces with a different color channel ordering than the API default.
    // It is recommended usage, and is required for compatibility with Direct2D.
    UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#if defined(_DEBUG)
    // If the project is in a debug build, enable debugging via SDK Layers with this flag.
    creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    // This array defines the set of DirectX hardware feature levels this app will support.
    // Note the ordering should be preserved.
    // Don't forget to declare your application's minimum required feature level in its
    // description.  All applications are assumed to support 9.1 unless otherwise stated.
    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };

    // Create the DX11 API device object, and get a corresponding context.
    ComPtr<ID3D11Device> d3dDevice;
    ComPtr<ID3D11DeviceContext> d3dContext;
    DX::ThrowIfFailed(
        D3D11CreateDevice(
            nullptr,                  // specify null to use the default adapter
            D3D_DRIVER_TYPE_HARDWARE,
            nullptr,                  // leave as nullptr unless software device
            creationFlags,            // optionally set debug and Direct2D compatibility flags
            featureLevels,            // list of feature levels this app can support
            ARRAYSIZE(featureLevels), // number of entries in above list
            D3D11_SDK_VERSION,        // always set this to D3D11_SDK_VERSION for modern
            &d3dDevice,               // returns the Direct3D device created
            &m_featureLevel,          // returns feature level of device created
            &d3dContext               // returns the device immediate context
            )
        );

    // Get the DirectX11.1 device by QI off the DirectX11 one.
    DX::ThrowIfFailed(
        d3dDevice.As(&m_d3dDevice)
        );

    // And get the corresponding device context in the same way.
    DX::ThrowIfFailed(
        d3dContext.As(&m_d3dContext)
        );

    // Obtain the underlying DXGI device of the Direct3D11.1 device.
    ComPtr<IDXGIDevice> dxgiDevice;
    DX::ThrowIfFailed(
        m_d3dDevice.As(&dxgiDevice)
        );

    // Obtain the Direct2D device for 2D rendering.
    DX::ThrowIfFailed(
        m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice)
        );

    // And get its corresponding device context object.
    DX::ThrowIfFailed(
        m_d2dDevice->CreateDeviceContext(
            D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
            &m_d2dContext
            )
        );

    // Save the DPI of this display in our class.
    m_d2dContext->SetDpi(m_dpi, m_dpi);

    // Release the swap chain (if it exists) as it will be incompatible with
    // the new device.
    m_swapChain = nullptr;

}
Platform::Array<unsigned char>^ DXBitmap::MakeText( Platform::String^ text )
{
	IWICStream *pStream = NULL;
    IWICBitmapEncoder *pEncoder = NULL;
    IWICBitmapFrameEncode *pFrameEncode = NULL;
    static const UINT sc_bitmapWidth = 140;
    static const UINT sc_bitmapHeight = 140;
    IWICBitmap *pWICBitmap = NULL;
    ID2D1RenderTarget *pRT = NULL;
    IDWriteTextFormat *pTextFormat = NULL;
    ID2D1SolidColorBrush *pBlackBrush = NULL;

	DX::ThrowIfFailed(
		m_wicFactory->CreateBitmap(
            sc_bitmapWidth,
            sc_bitmapHeight,
            GUID_WICPixelFormat32bppBGR,
            WICBitmapCacheOnLoad,
            &pWICBitmap
			));

	DX::ThrowIfFailed(
        m_d2dFactory->CreateWicBitmapRenderTarget(
            pWICBitmap,
            D2D1::RenderTargetProperties(),
            &pRT
            ));

        pRT->BeginDraw();
        pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
        D2D1_SIZE_F rtSize = pRT->GetSize();

		// static const WCHAR sc_fontName[] = L&quot;Calibri&quot;;
		static const WCHAR sc_fontName[] = L&quot;HGP明朝E&quot;;
        static const FLOAT sc_fontSize = 32;

        m_dwriteFactory->CreateTextFormat(
            sc_fontName,
            NULL,
            DWRITE_FONT_WEIGHT_NORMAL,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            sc_fontSize,
            L&quot;&quot;, //locale
            &pTextFormat
            );

        pRT->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::Black),
            &pBlackBrush
            );

        pRT->DrawText(
			text->Data(),
			text->Length(),
            pTextFormat,
            D2D1::RectF(0, 0, rtSize.width, rtSize.height),
            pBlackBrush);

		pRT->EndDraw();

    // Save image to file
    DX::ThrowIfFailed(
		m_wicFactory->CreateStream(&pStream));

    WICPixelFormatGUID format = GUID_WICPixelFormatDontCare;
    //    static const WCHAR filename[] = L&quot;output.png&quot;;

	unsigned char buff[sc_bitmapWidth*sc_bitmapHeight*4];

//	DX::ThrowIfFailed(
//		pStream->InitializeFromFilename(filename, GENERIC_WRITE));
	DX::ThrowIfFailed(
		pStream->InitializeFromMemory( buff, sizeof(buff)));
    DX::ThrowIfFailed(
		m_wicFactory->CreateEncoder(GUID_ContainerFormatPng, NULL, &pEncoder));
	DX::ThrowIfFailed(
        pEncoder->Initialize(pStream, WICBitmapEncoderNoCache));
    DX::ThrowIfFailed(
        pEncoder->CreateNewFrame(&pFrameEncode, NULL));
	DX::ThrowIfFailed(
		pFrameEncode->Initialize(NULL));
	DX::ThrowIfFailed(
        pFrameEncode->SetSize(sc_bitmapWidth, sc_bitmapHeight));
    DX::ThrowIfFailed(
        pFrameEncode->SetPixelFormat(&format));
    DX::ThrowIfFailed(
        pFrameEncode->WriteSource(pWICBitmap, NULL));
	DX::ThrowIfFailed(
        pFrameEncode->Commit());
	DX::ThrowIfFailed(
        pEncoder->Commit());

	Platform::Array<unsigned char>^ buffer = ref new Platform::Array<unsigned char>(sizeof(buff));
	for( int i=0; i<sizeof(buff); i++ ) {
		buffer[i] = buff[i];
	}
	return buffer;
}

最後の byte[] から array に移し替えるのがダサいのですが、まあ暫定的にこうしておきます。適当なコピーメソッドが見当たらなかったので。
実は、ここで問題なのは buff のサイズなのですが、勝手に width * height * 4 と決めつけています。コードを見るとわかりますが、BITMAP 形式から PNG 形式に変換しているので、サイズが正しくないのですよね。このあたり、は改修が必要です。

そうそう、ビルドをするときには、DirectX のライブラリが必要です。リンカーの入力として、以下のライブラリを追加しておきます。

d2d1.lib
d3d11.lib
dxgi.lib
ole32.lib
windowscodecs.lib
dwrite.lib

面倒な場合は、以下を直接貼り付けてしまってください。これは Direct2D アプリケーションで使っているライブラリと同じです。

kernel32.lib;user32.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;%(AdditionalDependencies)

さて、これで C++/CX ライブラリのほうは準備ができたので、このライブラリを利用する C# のコードを書きます。

using Windows.UI.Xaml.Media.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.Graphics.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;

private async void Button_Click_3(object sender, RoutedEventArgs e)
{
	var dxbmp = new MakeBitmapLib.DXBitmap();
	dxbmp.Initialize();
	byte[] data = dxbmp.MakeText("あれこれ");

	var mem = new InMemoryRandomAccessStream();
	await mem.WriteAsync(data.AsBuffer());
	// await mem.FlushAsync();
	BitmapImage bmp = new BitmapImage();
	bmp.SetSource(mem);
	image1.Source = bmp;
}

using がたくさんありますが、これが必須なのですよね~。このあたりの微妙なところもあって若干使いづらいのですが、これで文字が書けます。

まあ、字だけではつまらないので、そのうちに画像も付けておきます。

さて、C# のコードですが思ったよりも複雑ですよね。実は本来ならば、次のようなコードにしたいのです。

private void Button_Click_3(object sender, RoutedEventArgs e)
{
	var dxbmp = new MakeBitmapLib.DXBitmap();
	dxbmp.Initialize();
	image1.Source = dxbmp.MakeText("あれこれ");
}

こうするとすっきりして、これならばライブラリとして使えそうな感じですよね~。何故、こうしないか/こうできなかったかというと、

  • WriteAsync メソッドが非同期なので、async/await を作らないといけない。
  • async/await の使い方はわかったけど、「作り方」が分からない。
  • なので、メソッドに括り出すことができない。
  • 更に、C++/CX で async/await を作り方が分からない。

ってな訳なんですよね~。
という訳で、単なる画像加工にも非同期が関わってしまうというややこしさなのですが、まぁ、このあたりはもうちょっと調べていきましょうか。

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

[win8] スタート画面のタイルを動的に作成する(前哨戦)

途中なのですが、windows 8 のスタート画面のタイルを動的に作成することができたので、報告。
通常は、TileTemplateType あたりを使ってテンプレートを使ってタイルを作るのですが、これだと microsoft 社が提供するタイルしか作れません。まあ、標準的なタイルを作る場合にはこれでも良いのですが、どうせならばオリジナルのタイルが作りたいですよね、というのが発端です。

TileTemplateType enumeration (Preliminary)
http://msdn.microsoft.com/en-us/library/windows.ui.notifications.tiletemplatetype.aspx

テンプレートを使ったタイルの制限としては、

  • 標準的なフォーマットに従うために、画像位置やフォントが変更できない。

があります。これを、画像テンプレートの を使って動的に作成してしまおうという作戦です。画像を自前で作成できるので、自由な画像の配置、自由なフォントを使えるハズです。

ひとまず、こんな風に「HGP明朝E」のフォントを使ってタイルができました。いまのところ文字しか書いていませんが、背景に画像を貼り付けるできる予定です。左下に小さいアイコンがでてるのですが、これを消したいんですけどね~。TileSquareImage テンプレートが自動で表示しているので、どうすればよいのか?と思案中です。

普通にタイルだけを変更する場合は、あらかじめタイルを画像で作っておけば良いのですが、これを動的に作成する(時刻の表示や、ネットに接続してメッセージを出すとか)を考えると、(今のところは)動的に画像ファイルを作成しないといけないという状況です。ええ、タイル用のオリジナル XAML を設定できるようにしてくれればいいだけなんですけどね。将来的にはできるようになって欲しいなぁと。

■技術的な準備

さて、この動的なタイルを実現させるためには、いくつかクリアしないといけない技術的な問題…と言いますかクリアしないといけない壁があって、

  • DirectX で画像ファイルを作成する
  • C++/CX の metro からタイルの更新 TileUpdateManager を呼び出す
  • そもそも C++/CX で metro アプリを作らないと駄目?かも
  • C# から C++/CX のライブラリを呼び出せばOK?

というところです。metro アプリの場合、画像の切り出しやファイル保存の機能は WriteableBitmap class あたりで用意されています。この BitmapSouce になる WriteableBitmapを使えば、画像の加工もできるので、動的に画像を作ること、ちょっと頑張れば色相を変更させたり、一定の色で塗りつぶすことはできます。
が、.NET 4.5 では、画像加工の方法はいくらでも出てくるのですが、metro version になるとメソッドがグッと減っています。単純な加工ぐらいならば使えないことはないのですが、指定フォントで表示したり楕円を書いたり、透明度を設定したりということができません。まぁ、ピクセル単位で頑張ればできないこともないのですが、頑張るのは無駄かと。

そこで、画像作成はいきおい DirectX を使うようになります。DirectX で適当なビットマップを作って、それに描画しておき、そこからタイル用の png 形式のファイルに書き出せばよいわけです。

という訳で、画像ファイルを作成するためには DirectX を使うことになるのですが、これが C++/CX からしか扱えない(多分)らしいのですよね。おそらく、DirectX 関係のヘッダファイルを読み込んで適当な構造体を定義しないといけないので、見た感じでは C# や VB では無理です。

まぁ、C++/CX で metro アプリケーションを作れるようになったので、C++/CX だけで作っても良いのですがもうちょっと .NET Framework 寄りにしたいですよね。と言いますか、C++/CX からは .NET Framework を扱えないので、適当な画面は C# あたりで作る方が便利なのです。

なので、作成方法としては、

  • DirectX を扱う部分を C++/CX でライブラリとして作成
  • metro 画面自体は、C# で作成して、ライブラリにパラメータを渡す

な方式がベターかなと。

■暫定的なソースコードを公開

C++/CX だけで作ったものですが、一応 github に載せておきます。

win8/ChangeTileCppCx
https://github.com/moonmile/win8/tree/master/ChangeTileCppCx

作ったときの手順をざっと書いておくと

  1. c++/cx metro アプリケーションを作成
  2. ダミープロジェクトで direct2D のアプリケーションを作成。
  3. ダミープロジェクトから DirectXBase.* 関係のファイルをコピー。
  4. pch.h に DirectX 関係のヘッダを入れ込み
  5. c++/cx metro プロジェクトの DirectX 関係のライブラリを入れ込み
  6. 画像ファイルを動的に作成する DirectXLib.cpp を作成
  7. 上記のファイルに適当な namespace を付ける
  8. BlankPage.xml.cpp から、DirectXLib のクラスを呼び出し

ってな具合です。難関はいくつかあるのですが、

  • DirectX の扱いがそもそもよく分からん(c++から使う DirectX とは微妙に違うので、サンプルが少ない…つーか、まるでない)
  • ファイル書き出すは DirectX の関数からは直接できないので、メモリストリームを使って WinRT 経由で出力
  • ファイル書き出しが非同期なので、Task<>を使って書く必要あり。

なところですね。このあたりはぼちぼちと解説を書いていきます。

まあ、C++/CX で DirectX+metro の組み合わせでいけると「楽しい」かもしれません。metro の C#/VB では画像関係がチープになっているので、ちょっと小細工をした画像を作ろうとすると DirectX を直接触る必要があるので、C++/CX で画像関係のコンポーネントを作っておいて C#/VB から扱う、というスタイルが標準的になるような気がします。

カテゴリー: C++/CX, windows 8 | [win8] スタート画面のタイルを動的に作成する(前哨戦) はコメントを受け付けていません

[win8] 動的にImageコントロールに画像を配置する

動的にタイル画像(スタート画面の画像)を DirectX で作成して、スタート画面に設定することはできたので、その調節中。
DirectX 自体は(多分)直接 C# から扱うことができないので、ライブラリを c++/cx で作成して c# から使うという手順になりそうです。
で、その前に、c++/cx 単体で動くことを確認中というところ。

眠るシーラカンスと水底のプログラマー ≫ Windows8 Metro:初めてのアプリはWindowsPhoneからの移植
http://coelacanth.heteml.jp/blog/?p=612

metro アプリケーションではファイルアクセスが基本、非同期になってしまったので、C# では非同期用の async/awit を使うことになりそうなのですが、c++/cx ではそれらしい文法追加がされていないので、task<> と lambda を駆使しないと駄目なんですよね~、これがちょっと厄介なのですが…これは別途。

で、まずは動的に作成したファイルを metro アプリ上で表示する簡単なチェックツールを作っていたのですが、ちょっとハマったので記録しておきます。

試しなのであらかじめ用意しておいた、logo.png, logo1.png, logo2.png のファイルを切り替えます。

プロジェクト構成はこんな感じで。Images フォルダ内に3つの画像ファイルを収めておきます。

■C# で画像を切り替える

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    string fname = (string)((Button)sender).Content;
    fname = "ms-appx:///Images/" + fname + ".png";
    BitmapImage bi = new BitmapImage(new Uri(fname));
    image1.Source = bi;
}

BitmapImage オブジェクトを作成して、Image コントロールに Source プロパティに設定、というところです。この場合は、アプリケーションリソースが対象なので「ms-appx://」となっていますが、アプリケーションのローカルに保存した場合には「ms-appdata://」を使います。このフォルダは、「C:\Users\<ユーザ名>\AppData\Local\Packages\<アプリID>\LocalState\」になります。まぁ、ここのアクセスは後ほど。

■C++/CX で画像を切り替える

WinRT のクラスだけを使っているので、C++/CX への変換は楽チンです。そのまま書き写すだけ。

void DynamicLoadImageCppCx::BlankPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
	String^ fname = (String^)((Button^)sender)->Content;
	fname = &quot;ms-appx:///Images/&quot; + fname + &quot;.png&quot; ;
	Imaging::BitmapImage^ bi = ref new Imaging::BitmapImage( ref new Uri( fname ));
	this->image1->Source = bi ;
}

C# と同じように、BitmapImage オブジェクトを作成して Image コントロールの Source プロパティに設定という具合。namespace がちょっとややこしいので、「Imaging::BitmapImage」を使っていますが、正式名称は「Windows::UI::Xaml::Media::Imaging::BitmapImage」ですね。最初に「using namespace Windows::UI::Xaml::Media::Imaging;」としてしまっても ok です。

Windows Phone や Siverlight とは結構違うので、このあたりハマり処かもしれませんねぇ。ええ、DirectX を使うと更にハマってしまいそうな予感なのですが、そのあたりは後日。

ソースコードは github https://github.com/moonmile/win8/tree/master/DynamicLoadImage からどうぞ。

カテゴリー: C#, C++/CX, windows 8 | [win8] 動的にImageコントロールに画像を配置する はコメントを受け付けていません

[win8] c++/cx から c# のライブラリは使えない…と思ったが使える

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&quot;masuda&quot;, L&quot;tomoaki&quot; );
	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=&quot;x&quot;></param>
        /// <param name=&quot;y&quot;></param>
        /// <returns></returns>
        public int Add(int x, int y)
        {
            return x + y;
        }
        /// <summary>
        /// System.String 型を返す
        /// </summary>
        /// <param name=&quot;x&quot;></param>
        /// <param name=&quot;y&quot;></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&quot;masuda&quot;, L&quot;tomoaki&quot; );
	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#, C++/CX, windows 8 | 2件のコメント