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="fmt"></param>
		/// <param name="args"></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"tomoaki";
	int age = 44;
	this->textBox1->Text = CSharpString::CsString::Format( "masuda {0} {1}", name, age ); 
}



void UseCSharpStringInCppCx::BlankPage::Button_Click_2(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
	String^ o = CSharpString::CsString::Format(L"only masuda");
	textBox1->Text = o ;
	String^ o1 = CSharpString::CsString::XFormat(L"masuda {0} {1}", "tomoaki", 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 "pch.h"
#include "WinRTComponent.h"

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"Calibri";
		static const WCHAR sc_fontName[] = L"HGP明朝E";
        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"", //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"output.png";

	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 = "ms-appx:///Images/" + fname + ".png" ;
	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"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#, C++/CX, windows 8 | 2件のコメント

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 コマンドで設定する はコメントを受け付けていません