始めに
C++/CLI はおそらく旧来のライブラリを .net のアセンブリに変更されるのに一番利用されると考えています。
そのとき、一番面倒なのは、旧来の MBCS や wchar_t 型と System::String をどうやりとりすればいいのかという点です。
ここでは、MultiByte ないし、 WideChar の System::String 型とのやりとりについて記述しておきます。
MultiByte から System::String に
これは特に問題も疑問の余地もないですよね。
std::string text = "サンプル・テキスト"; // めんどくさいので char * はこれで代用
String^ textString = gcnew String(text.c_str());
String^ textString = Marshal::PtrToStringAnsi(text.c_str()); // Marshal を使うとこれ
WideChar から System::String に
これも特に疑念の余地はありませんよね。
std::wstring wide_text = L"ワイド文字列"; // めんどくさいので wchar_t * はこれで代用
String^ textString = gcnew String(wide_text.c_str());
String^ textString = Marshal::PtrToStringUni(wide_text.c_str()); // Marshal を使うとこれ
Marshal クラスにはそのほかに BSTR 用の PtrToStringBSTR や 自動識別の PtrToStringAuto も用意されています。TCHAR を使っているときは、PtrToStringAuto を使った方が便利でしょう。
System::String 型から MultiByte に
ここから段々と面倒な手続きが必要になってきます。
キーワードは、ピンニング、マーシャル・アズです。
追加で、System.Runtime.InteropServices の
Marshal クラスを見ておくといろいろと便利です。
std::string text; // 文字列を受け止めるバッファ
String^ textString = gcnew String("適当なサンプル文字列"); // 変換する文字列
// System::String をChar 型 = wchar_t 型の配列にする
array<Char>^ warr = textString->ToCharArray();
// 配列がガベージ・コレクトによって移動しないようピンニング
pin_ptr<Char> wptr = &warr[0];
// 変換後文字サイズを取得
char *buffer = 0;
int len = ::WideCharToMultiByte(CP_UTF8, 0, wptr, num, buffer, 0, NULL, NULL);
if ( len > 0 ) {
// 文字バッファを取得して
buffer = new char[len + 1];
memset(buffer, 0, len+1);
// UNICODE を MultiByte に変換
::WideCharToMultiByte(CP_UTF8, 0, wptr, num, buffer, len, NULL, NULL);
// バッファをMultiByteの文字型に代入
text = buffer;
if ( buffer ) delete [] buffer;
}
もっといい方法が .NET Framework 2.0 では提供されていました。
std::string text; // 文字列を受け止めるバッファ
String^ textString = gcnew String("適当なサンプル文字列"); // 変換する文字列
IntPtr mptr = Marshal::StringToHGlobalAnsi(textString);
text = static_cast<const char*>(mptr.ToPointer());
Marshal::FreeHGlobal(mptr);
System::String 型から WideChar 型に
これはちょっと微妙。どうやってコピーするかによると思う。
基本的にはピンニングして、コピーすればいい。
String^ textString = gcnew String("適当なサンプル文字列"); // 変換する文字列
// System::String をChar 型 = wchar_t 型の配列にする
array<Char>^ warr = textString->ToCharArray();
// 配列がガベージ・コレクトによって移動しないようピンニング
pin_ptr<Char> wptr = &warr[0];
// 文字列を受け止めるバッファ
std::wstring wide_text(wptr);
String^ textString = gcnew String("適当なサンプル文字列"); // 変換する文字列
IntPtr wptr = Marshal::StringToHGlobalUni(textString);
// 文字列を受け止めるバッファ
std::wstring wide_text(static_cast<const wchar_t*>(wptr.ToPointer()));
Marshal::FreeHGlobal(wptr);
System::String 型から TCHAR 型に
MFC ないし、ATL(WTL) の CString を使ってまとめて書くと次のようになります。
String^ textString = gcnew String("適当なサンプル文字列"); // 変換する文字列
IntPtr tptr;
#ifdef _UNICODE
tptr = Marshal::StringToHGlobalUni(textString);
#else
tptr = Marshal::StringToHGlobalAnsi(textString);
#endif
CString result_text = (LPCTSTR)tptr.ToPointer();
Marshal::FreeHGlobal(tptr);
すごく簡単ですね。
これで基本的な置き換えはおっけーです。
(´-`).。oO(そういえば、marshall_as<> はどこへ行ってしまったのだろう?)
(´-`).。oO(なるほど、System.Runtime.InteropServices.Marshal になったわけか…)
Topに戻る