特集

Vista時代のVisual C++の流儀(前編)

Vista到来。既存C/C++資産の.NET化を始めよう!

επιστημη(えぴすてーめー)
2007/01/31
Page1 Page2 Page3 Page4

●Shift-JISベースのC関数(Win32スタティック・ライブラリ)の準備

 例えばこんなShift-JISベースのC言語によるネイティブ・コード。ここでは「greet」という関数を実装しています。

#include <string.h>

const char* greet(const char* name) {
  static char result[128];
  strcpy(result, name);
  strcat(result, "さん、こんにちは");
  return result;
}
Shift-JISベースのCソース・ファイル(greet.c)

●Unicodeベースに変換する薄いラッパー(.NETダイナミック・ライブラリ)の作成

 これをラップするC++/CLIクラス「GreetLib」を作ります。

#ifndef GREETLIB_H__
#define GREETLIB_H__

using namespace System;

public ref class GreetLib {
public:
  static String^ Greet(String^ name);
};

#endif
.NETダイナミック・ライブラリのC++/CLIソース・ファイル(greetlib.h)
先ほどのShift-JISベースのCソース・ファイルから生成したWin32スタティック・ライブラリの薄いラッパーとなる。

 GreetLibクラスのGreetメソッドの中でネイティブ・コードとしてC言語で書かれたgreet関数を呼ぶのですが、その際にUnicode→Shift-JISおよび Shift-JIS→Unicodeの各文字コード変換が必要となります。

 ここではShift-JISだけでなく、ついでにEUC-JPやJIS(iso-2022-jp)などの各種エンコーディングに対応できるネイティブ・コード文字列⇔マネージ・コード文字列の変換を実装しておきましょう。

 ネイティブ・コード文字列であるstd::string型からマネージ・コード文字列System::String型への変換は簡単です。System名前空間のStringクラスにはエンコーディングを指定できるコンストラクタが定義されていますから。

// std::stringからSystem::Stringへの変換
System::String^ ToCLI(
  std::string& input,
  System::Text::Encoding^ encoding)
{
  return gcnew System::String(
    input.data(), 0, input.size(), encoding);
}
ネイティブ・コード文字列からマネージ・コード文字列への変換を行うC++/CLIコード
ToCLIメソッドは、Stringクラス(System名前空間)のコンストラクタを利用して文字コードの変換を行い、それを戻り値として返す。第1パラメータ「input」には変換前のネイティブ・コード文字列を指定する。第2パラメータ「encoding」には変換前の文字列に対するエンコーディングの指定としてEncodingクラス(System::Text名前空間)のオブジェクトを指定する。

 逆にSystem::String型からstd::string型への変換は少々面倒です。Encodingクラス(System::Text名前空間)のstaticメソッドConvertを使います。

// System::Stringからstd::stringへの変換
std::string FromCLI(
  String^ input,
  System::Text::Encoding^ encoding) {
  std::string result;
  if ( input != nullptr &&  input->Length > 0 ) {
    array<Byte>^ barray =
      System::Text::Encoding::Convert(
          Encoding::Unicode, // 変換元エンコーディング
          encoding,          // 変換先エンコーディング
          Encoding::Unicode->GetBytes(input));
    pin_ptr<Byte> pin = &barray[0];
    result.assign(reinterpret_cast<char*>(pin), barray->Length);
  }
  return result;
}
マネージ・コード文字列からネイティブ・コード文字列への変換を行うC++/CLIコード
FromCLIメソッドは、Encodingクラス(System::Text名前空間)のstaticメソッドConvertを利用して文字コードの変換を行い、それを戻り値として返す。第1パラメータ「input」には変換前のネイティブ・コード文字列を指定する。第2パラメータ「encoding」には変換前の文字列に対するエンコーディングの指定としてEncodingクラス(System::Text名前空間)のオブジェクトを指定する。

 上記2つの変換コードを使って、ラッパーとなるGreetLibクラスのメソッドは次のように実装できます。

String^ GreetLib::Greet(String^ name) {
  Encoding^ sjis = Encoding::GetEncoding("shift_jis");
  std::string input = FromCLI(name, sjis); // String → std::string
  std::string result = greet(input.c_str()); // greet呼び出し
  return ToCLI(result, sjis); // std::string → String^
}
greet関数のラッパーとなるGreetLibクラスのGreetメソッドを実装するC++/CLIコード

 これをビルドしてgreetlib.dllを生成します。

●.NETアプリケーションからのライブラリ(C++クラス)の利用

 それではVisual Basic 2005(以下VB)から greetlib.dllのGreetLibクラスを使ってみましょう。

 Windowsフォームにテキストボックス2つとボタン1つを配置し、ボタン・クリックのハンドラを仕込みます。

Public Class Form1

  Private Sub btnGreet_Click(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) _
                             Handles btnGreet.Click
    txtResult.Text = GreetLib.Greet(txtInput.Text)
  End Sub

End Class
Shift-JISベースのC関数を扱うVBのサンプル・コード(Form1.vb)
薄いラッパーであるGreetLibクラスのGreetメソッドを経由してgreet.libに実装されているShift-JISベースのgreet関数にアクセスしている。

 以上のサンプル・プログラムはここ(greet.zip)からダウンロードできます。

 サンプル・プログラムをコンパイルして実行すると、ネイティブ・コードのC関数greetが正しく呼ばれていることが確認できます。

Shift-JISベースのC関数を扱うサンプル・プログラムの時刻結果
ラッパーであるGreetLibクラスのGreetメソッドが、greet関数への入力はShift-JISに、出力はUnicodeに変換しているのを確認できる。

 1つ注意しておかなければならないのは、このコード変換はかなりコスト高な処理であるという点です。Unicode⇔マルチバイト変換を頻繁に行うとパフォーマンスを落とします。できることならネイティブ・コード側でUnicodeに対応し、wchar_t*型をパラメータとするよう修正しておくことを強くお勧めします。

●ネイティブ・コード側でのUnicode対応

 例えば先ほどのgreetであれば次のようになります。

const wchar_t* wgreet(const wchar_t* name) {
  static wchar_t result[128];
  wcscpy(result, name);
  wcscat(result, L"さん、こんにちは");
  return result;
}
Unicodeベースに対応したCソース・ファイル(greet.c)

 そうすればC++/CLIによるSystem::String⇔std::wstringには文字コード変換の必要がなく、以下のコードで実現できます。

#include "greetlib.h"
#include "greet.h"
#include <string>

using namespace System;
using namespace System::Text;

// std::string → System::String^
String^ ToCLI(std::string& input, Encoding^ encoding) {
  return gcnew String(input.data(), 0, input.size(), encoding);
}

// System::String^ → std::wstring
std::wstring FromCLI(String^ input) {
  std::wstring result;
  if ( input != nullptr && input->Length > 0 ) {
    pin_ptr<wchar_t> pin = &input->ToCharArray()[0];
    result.assign(pin, input->Length);
  }
  return result;
}

String^ GreetLib::Greet(String^ name) {
  return ToCLI(wgreet(FromCLI(name).c_str()));
}
文字コード変換が不要になったC++/CLIソース・ファイル(greetlib.cpp)
greet関数のラッパーとなるGreetLibクラスのGreetメソッドを実装している。

 以上のサンプル・プログラムはここ(greetunicode.zip)からダウンロードできます。

 次回はMFCから.NETへの実践的移行計画についてご紹介します。End of Article


 INDEX
  [特集]
  Vista時代のVisual C++の流儀(前編)
  Vista到来。既存C/C++資産の.NET化を始めよう!
    1.Vista時代にC/C++はもはやお払い箱なのか?
    2.C/C++資産をどこまで生かせる?
    3.ネイティブ・オブジェクトをマネージ・コードでくるむ
  4.文字コード変換
 
  Vista時代のVisual C++の流儀(中編)
  MFCから.NETへの実践的移行計画
    1.C++/CLIによるWindowsフォーム・アプリケーション
    2.言語をまたいだDocument/Viewアーキテクチャ
    3..NET移行前のMFCサンプル・アプリケーション
    4.MFCのDocument/Viewアーキテクチャの.NET化
    5.MFCで書かれたDocumentを.NET化する2つの方法
 
  Vista時代のVisual C++の流儀(後編)
  STL/CLRによるDocument/Viewアーキテクチャ
    1.STL/CLRとは
    2.STL/CLRの特徴
    3.Visual Studio 2005で試す
    4.おまけ:NUnitの活用


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間