連載
» 2013年01月17日 15時24分 UPDATE

WinRT/Metro TIPS:シフトJISのデータを読み取るには?[WP 8]

Windows Phone 8では、Win32 APIを利用してシフトJISのバイト列データをUnicode文字列に変換できるようになった。本TIPSでは、その実装方法を説明する。

[山本康彦,BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 前回、Windows Phone 8(以降、WP 8)では「GetEncoding("Shift-JIS")」というコードが動かず、シフトJISのデータを読み取れないと書いた。WP 7.xまでは実際にどうしようもなく、シフトJISでエンコードされたデータを読み取るには自前で変換テーブルを実装するしかなかった。だが、WP 8からはWin32 APIを利用してシフトJISのデータをUnicodeに変換することが可能になっている。本稿では、WP 8でWin32 APIを使う方法と、シフトJISをUnicodeに変換するコードを紹介する。本稿のサンプルは「Windows Store app samples:MetroTips #20」からダウンロードできる。

事前準備

 WP 8向けのアプリを開発するには、SLAT対応CPUを搭載したPC上の64bit版Win 8 Pro以上Windows Phone SDK 8.0(無償)が必要となる。

Win32 APIを使うには?

 C++/CXで「Windows Phoneランタイム・コンポーネント」(以降、WinPRTコンポーネント)をARM CPU用に作成する(エミュレータで使うにはx86 CPU用にする)。

 WP 8で利用できるWin32 APIは、MSDNの「MSDN: Windows Phone 8 でサポートされる Win32 API」に掲載されている。ただし、WP 8ではC#/VB(Visual Basic)のDllImport属性がサポートされておらず、C++/CXを使ってWinPRTコンポーネントを作らねばならない。

 C++/CXとは「Visual C++ component extensions」(Visual C++ コンポーネント拡張)の略称であり、かつて「Metro」と呼ばれていたWindowsストア・アプリ/Windows Phoneアプリのための一連の拡張をC++言語に施したものだ。

 WinPRTコンポーネントとは、Windows 8/RT用のWindowsストア・アプリのWindowsランタイム(WinRT)コンポーネントと大部分が同様のもの(=サブセット)だと思ってよいだろう。ただし、WinPRTコンポーネントはC#/VBでは作成できず、C++/CXのみがサポートされている。

 また、WinPRTコンポーネントがサポートされているプラットフォームは「Win32」(=x86 CPU向け)と「ARM」(=ARM CPU向け)の2種類だけで、「Any CPU」はない。従って、WP 8エミュレータでテストするときは「Win32」でビルドし、実機にリリースするときは「ARM」でと作り分ける必要がある。

 Windows Phone SDK 8.0に含まれているVisual Studio 2012(以降、VS 2012)でWinPRTコンポーネントを作るには、[新しいプロジェクト]ダイアログで「Visual C++」カテゴリを選び、次いで「Windows Phoneランタイム コンポーネント」テンプレートを選ぶ(次の画像)。

ap-winrttips_0020_01.gif [新しいプロジェクト]ダイアログで「Windows Phoneランタイム コンポーネント」を選ぶところ

C++/CXでコーディングするには?

 この話題を本稿で詳しく解説している余裕はないので、MSDNの「Visual C++ の言語リファレンス (C++/CX)」を参照してほしい*1

*1 このドキュメントはWindows 8/RTのWindowsストア・アプリ用に書かれているので、細部では異なる可能性がある。


 Visual C++とC#での開発経験があるならば、「^」(ハット)と「ref new」を理解すればサンプル・コードなどが読めるようになるだろう。「^」はC++のauto_ptrに似ていて、「自動的に参照がカウントされるWindowsランタイム・オブジェクトへのポインタ」だ。また、「ref new」はWindowsランタイム・オブジェクトのインスタンスを作るが、ポインタではなくハットを返す。「ref new」して「^」で受けたオブジェクトはGC(ガベージ・コレクタ)が管理してくれるので、明示的にメモリを解放する必要がない。

シフトJISをUnicodeに変換するには?

 Win32 APIのMultiByteToWideChar関数を使う。先ほど作成したWinPRTコンポーネントのプロジェクトに、次のようなコードを記述する。

// WPRuntimeComponentSample.h
#pragma once
#include <ppltasks.h>  // 型定義やWin32 APIの定義などを含む

#define  CP_SJIS  932

namespace WPRuntimeComponentSample
{
  public ref class Win32ApiSample sealed  // WinPRTが公開できるのはsealedのみ
  {
  public:
    static Platform::String^ MultiByteToWideChar(const Platform::Array<byte>^ buff);
  };
}

ヘッダーファイル(C++/CX)

// WPRuntimeComponentSample.cpp
#include "pch.h"
#include "WPRuntimeComponentSample.h"

using namespace WPRuntimeComponentSample;
using namespace Platform;

Platform::String^ Win32ApiSample::MultiByteToWideChar(const Platform::Array<byte>^ buff)
{
  LPCSTR pBuff = (LPCSTR)(buff->Data);
  if(pBuff == NULL)
    return ref new Platform::String();  // 空文字を返す

  const int nSize = ::MultiByteToWideChar(CP_SJIS, 0, pBuff, -1, NULL, 0);
  BYTE* buffUtf16 = new BYTE[nSize * 2 + 2];

  ::MultiByteToWideChar(CP_SJIS, 0, pBuff, -1, (LPWSTR)buffUtf16, nSize);

  Platform::String^ result = ref new Platform::String((LPWSTR)buffUtf16);
  delete[] buffUtf16;  // *で受けたオブジェクトは従来どおり自前で解放する

  return result;
}

cppファイル(C++/CX)

C#/VBから呼び出すには?

 普通の手順で利用できる。プロジェクトを参照し、名前空間名・クラス名・メソッド名を使って呼び出す。ただし、プラットフォームには要注意だ。

 WP 8アプリのプロジェクトを作り、上記のWinPRTコンポーネントのプロジェクトへの参照を追加し、次のようなコードを試してみよう。

public MainPage()
{
  ……略……

  // サンプル・データ
  byte[] buff = new byte[] {
    0x8E, 0x8E, 0x8C, 0xB1, 0x82, 0xC5, 0x82, 0xB7,
    // “試験です”というシフトJIS文字列のバイト列データ(変換対象)
  };

  // Unicode形式データとしてUnicode文字列に変換
  textBlock1.Text = Encoding.Unicode.GetString(buff, 0, 8);

  // シフトJIS形式データとしてUnicode文字列に変換(WinPRTコンポーネント)
  textBlock2.Text =
    WPRuntimeComponentSample.Win32ApiSample.MultiByteToWideChar(buff);
}

Public Sub New()
  ……略……

  ' サンプル・データ
  Dim buff() As Byte = { _
    &H8E, &H8E, &H8C, &HB1, &H82, &HC5, &H82, &HB7 _
  } ' “試験です”というシフトJIS文字列のバイト列データ(変換対象)

  ' Unicode形式データをUnicode文字列に変換
  textBlock1.Text = Encoding.Unicode.GetString(buff, 0, 8)

  ' シフトJIS形式データをUnicode文字列に変換(WinPRTコンポーネント)
  textBlock2.Text = _
    WPRuntimeComponentSample.Win32ApiSample.MultiByteToWideChar(buff)
End Sub

WinPRTコンポーネントを呼び出すコード例(上:C#、下:VB)

 バイト列データをUnicode形式として取り扱って文字列(.NETでは文字はUnicode文字として処理されるため、厳密にはUnicode文字列)に変換した方は文字化けし、シフトJIS形式として変換した方は正しく表示されるはずだ。

 なお、WinPRTコンポーネントのプロジェクトへの参照を追加すると、[構成マネージャー]ダイアログ(=メニューバーの[ビルド]−[構成マネージャー]で表示される)は次の画像のようになっているはずだ。

ap-winrttips_0020_02.gif [構成マネージャー]ダイアログ(エミュレータ用の例)
WP 8アプリの[プラットフォーム]が「x86」に変わっている。

 WinPRTコンポーネントのプラットフォームは、デフォルトの「Win32」になっている。それを参照するWP 8アプリの[プラットフォーム]は「x86」でなければならないので、自動的に「Any CPU」から「x86」に変更されているのだ。エミュレータで実行する場合は、この設定のままでよい。

 実機にインストールするパッケージを作る場合には、次の画像のように[プラットフォーム]を「ARM」にする。

ap-winrttips_0020_03.gif [構成マネージャー]ダイアログ(実機用の例)
実機用には「ARM」にする。

実行結果

 次の画像に示すように、シフトJIS形式のバイト列データを文字化けせずに表示できた。

ap-winrttips_0020_04.gif 実行結果
シフトJIS文字列のバイト列データを、Unicode形式として取り扱って文字列に変換した上の[UNICODE として]のテキストブロック表示は文字化けしており、シフトJIS形式として変換した下の[Shift JIS として]のテキストブロックは文字化けせずに表示できていることからシフトJIS形式のデータを正しく文字列に変換できていると分かる。

 なお、ほぼ同じC++/CXのコードを使ったWP 8アプリを筆者はWindows Phoneストアで公開している。もちろんストアの審査も問題なく通っている。また、知人の協力により実機HTC 8X(国内未発売)で実際に動作することも確認できた。

まとめ

 C++/CXでWinPRTコンポーネントを作れば、Win32 APIが利用できる。Win32 APIのMultiByteToWideChar関数を使って、シフトJIS文字列のバイト列データを文字列(.NETでは文字はUnicode文字として処理されるため、厳密にはUnicode文字列)に変換できる。

 WinPRTコンポーネントの資料はまだ少ないのが現状だが、次のドキュメントを参考にしてほしい。

以下のドキュメントはWindows 8/RT用なので、細部では異なる可能性がある。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。