Visual C++ 文字列 まとめ

第2.0版　(自作)文字列変換関数を追加

#はじめに
C# では文字列型は System.String だけです。一方、Visual C++ では、C 言語との互換性、Win16 との互換性、Win32 との互換性、テンプレート等々の関連で文字列とみなされる型はいろいろあります。

文字列は Visual Studio 2003 までは SJIS が標準だったらしいですが、Visual Studio 2005 からは Unicode (ワイド文字列) が標準になったそうで、混乱に輪がかかっています。よって、古いソースを Visual Studio 2017 でビルドすると、エラーがずらりと表示されます。

ここでは、それらについてまとめてみました。

#どんな「文字列」があるか？
Visual C++ にはどんな「文字列」があるか、ざっくり見てみましょう。もしかしたら、もっとあるかもしれませんが、比較的、目にするのはこんな感じです。

- char*, wchar_t*　(C 言語互換の文字列型)
- LPSTR などの Windows.h で定義されたマクロ (多数あり)
- string, wstring (STL の文字列)
- CString (MFC の文字列)
- CStringT (MFC のテンプレート文字列)
- BSTR (COM の文字列)
- CComBSTR (ATL の文字列)
- String ^ (CLI の文字列)

#２つの Win32 API 関数
Win32 API 関数には、関数名の最後に A が付くものと W が付くものがあります。前者はマルチバイト文字列 (SJIS) 対応バージョン、後者がワイド文字列 (Unicode) 対応バージョンです。

これらの関数で関数名の最後の A や W のないバージョンがあって、現在は W 付きのバージョンの別名になっています。「現在は」の意味は、過去は A 付きのバージョンの別名になっていたためです。これは、Windows 9x までは OS 内部でも文字列のコードが Unicode でなく、日本語版の場合は、SJIS になっていたためです。

次のサンプルは、コンピュータ名を表示するものですが、最初の方がワイド文字列 (Unicode) バージョン、後の方が マルチバイト文字列 (SJIS) バージョンです。

こちらは GetComputerNameW を使用しています。stdafx.h には #include &lt;Windows.h&gt; を追加しておく必要があります。

```C++
#include "stdafx.h"

int main()
{
	WCHAR compName[80];
	DWORD buffSize = sizeof(compName);
	if (GetComputerNameW(compName, &buffSize) != 0)
	{
		wprintf_s(L"%s\n", compName);
	}
	else
	{
		printf_s("エラー\n");
	}

	getchar();
	return 0;
}
```

こちらは GetComputerNameA を使用しています。stdafx.h には #include &lt;Windows.h&gt; を追加しておく必要があります。

```C++
#include "stdafx.h"

int main()
{
	CHAR compName[80];
	DWORD buffSize = sizeof(compName);
	if (GetComputerNameA(compName, &buffSize) != 0)
	{
		printf_s("%s\n", compName);
	}
	else
	{
		printf_s("エラー\n");
	}

	getchar();
	return 0;
}
```

#tchar.h
tchar.h はプロジェクトを作成すると stdafx.h に自動的に含まれています。これに含まれるマクロを使用することで、同じソースからマルチバイト文字列 (SJIS)、ワイド文字列 (Unicode) のアプリをビルドできます。

このヘッダーファイルには次のようなマクロが含まれています。

##汎用テキストマッピング用のプリプロセッサ ディレクティブ
次のディレクティブはプロジェクトのプロパティで設定します。

|\#define	|コンパイル後のアプリ状態                  |
|:-------------:|:--------------------------------------:|
|_UNICODE	|ワイド文字列 (Unicode) 対応              |
|_MBCS	        |マルチバイト文字列 (SJIS) 対応            |
|なし	        |ASCII 文字列対応                         |

##汎用テキストのデータ型のマップ
次のマクロを使うと、_UNICODE, _MBCS の定義に従って（上の表のような）アプリがビルドされます。

|マクロ   |なし	             |_MBCS            |_UNICODE                                |
|:-------|:------------------|:----------------|:---------------------------------------|
|_TCHAR	 |char	             |char	       |wchar_t                                 |
|_TINT	 |int	             |unsigned int     |wint_t                                  |
|_TSCHAR |signed char        |signed char      |wchar_t                                 |
|_TUCHAR |unsigned char      |unsigned char    |wchar_t                                 |
|_TXCHAR |char               |unsigned char    |wchar_t                                 |
|_T      |影響なし (プリプロセッサによって削除される)|影響なし (プリプロセッサによって削除される)|L (後続の文字または文字列を対応する Unicode の文字または文字列に変換する)|

※ _T は _TEXT の省略形なので、_TEXT と書くこともできる。

例えば、_UNICODE が定義されている場合、次の２行は同じ結果になります。_UNICODE が定義されていない場合は、最初のほうはエラーとなるが、２行目のほうは正しくコンパイルされます。

```C++
	wprintf_s(L"%s\n", str);
	wprintf_s(_T("%s\n"), str);
```

#Windows.h
Win32 API 関数を使う場合、下のソースのように stdafx.h に Windows.h を手動で追加する必要があります。

```C++
// stdafx.h : 標準のシステム インクルード ファイルのインクルード ファイル、または
// 参照回数が多く、かつあまり変更されない、プロジェクト専用のインクルード ファイル
// を記述します。
//
#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

// TODO: プログラムに必要な追加ヘッダーをここで参照してください
#include <Windows.h>

```

これにより Win32 API 関数を使う際に使用されるマクロ（型名）も使用可能になります。これらの型名は Win16 からのもので、16 / 32/ 64 ビットでコンパイルする場合に互換性があります。

これらの型には PSTR, LPSTR, LPCSTR, LPCTSTR など (その他多数) があります。これは推測ですが、P はポインタ, L は Long、C は定数、T は Text または Typed を意味します。L (Long) は、Win16 時代に関連するもので Win32 では L がなくても同じになります (たぶん)。T が付いた型は、Unicode、SJIS に関わらず正しくコンパイルできる型と思われます。

これらマクロの定義ですが、マルチバイト文字列の場合、次のような感じになるはずです(推測ですが)。ワイド文字列では char が wchar_t に変わります。

- PSTR char*
- LPSTR char*
- LPCSTR const char*
- LPCTSTR _TEXT(const char*)

因みに、Win16 時代だと PSTR は near char*、LPSTR は far char* とか near, far キーワードを使って表現されていたはずです。

#MFC, ATL ヘッダー
Visual Studio でプロジェクトを作成するとき、MFC や ATL の使用の有無を指定できます。これにより、CString などのクラスが使用できるようになります。

##CString
これは、MFC に含まれる文字列クラスです。MFC に含まれるクラスのメソッドのパラメータなどとしてよく使われます。これは推測ですが、旧型 Visual Basic の文字列をクラス化したもののように見えます。

C の文字列 (char*) を直接、扱うより便利なのですが、スレッドセーフではないようなので注意が必要です。

テンプレート版の CStringT というのもありますが、MFC のメソッドでは使われていないので、わざわざ覚える必要はないと思います。

##BSTR と CComBSTR
COM (Common Object Model) での文字列型は常に BSTR (ワイド文字列) です。ATL ではこれをカプセル化した CComBSTR というクラスでよく扱います。


#マルチバイト文字とワイド文字の変換

マルチバイト文字とワイド文字の変換のために、下のリンク先のようなマクロが用意されています。

　[ATL and MFC String Conversion Macros](https://msdn.microsoft.com/en-us/library/87zae4a3.aspx)

マクロは ATL3.0 と ATL7.0 のものがあり、どちらも利用可能です。
特徴ですが、ATL3.0 は関数のような使い勝手、ATL7.0 は宣言のような使い勝手です。
ATL3.0 のマクロを使う場合は、USES_CONVERSION マクロを事前に実行しておく必要があります。

```
USES_CONVERSION;
BSTR bstr = A2W(szMsg);
```

#(自作)文字列変換関数
Visual C++ には、いろいろな文字列がありますが、たまに相互に変換したいときがあります。
そんなときのために変換関数を作りました。
自作かつ検証も十分でないので、どれだけ実用になるのかも問題ですが。:tongue:

- Char^ CCharToClrChar(wchar_t c)
- wchar_t ClrCharToCChar(Char ^c)
- String^ BSTRToString(BSTR bstr)
- BSTR ClrStringToBSTR(String ^str)
- void ClrStringToBSTR(String ^str, BSTR* pbstr)
- BSTR AToW(LPSTR mbcs)
- void AToW(LPSTR mbcs, BSTR* wcs)
- LPSTR WToA(BSTR wcs)
- void WToA(BSTR wcs, LPSTR* mbcs)
- String^ CStrToCliStr(CString str)
- CString CliStrToCStr(String ^str)
- String^ StdToCliStr(std::wstring str)
- std::wstring CliStrToStd(String ^str)

##使用方法

後述する StringConvertLib.h をインクルードするだけで使用できます。stdafx.h の内容は特に修正しません。プロジェクトの文字コードは必ずワイド文字にします。

##関数一覧

宣言
  **Char^ CCharToClrChar(wchar_t c)**
機能
  ワイド文字を CLR 文字に変換する。
パラメータ
  [in] wchar_t c: ワイド文字
戻り値
  変換された CLR 文字

宣言
　**wchar_t ClrCharToCChar(Char ^c)**
機能
　CLR 文字からワイド文字に変換する。
パラメータ
　[in] Char ^c: CLR 文字
戻り値
　変換されたワイド文字

宣言
　**String^ BSTRToString(BSTR bstr)**
機能
　ワイド文字列 (BSTR) から CLR 文字列に変換する。BSTR は wchar_t* と同じ。
パラメータ
　[in] BSTR bstr: ワイド文字列
戻り値
　変換された CLR 文字列

宣言
　**BSTR ClrStringToBSTR(String ^str)**
機能
　CLR 文字列からワイド文字列 (BSTR) に変換する。BSTR は wchar_t* と同じ。
パラメータ
　[in] String ^str: CLR 文字列
戻り値
　変換されたワイド文字列 (BSTR)
注意
　戻り値は不要になったら SysAllocString 関数を使って解放すること。

宣言
　**void ClrStringToBSTR(String ^str, BSTR* pbstr)**
機能
　CLR 文字列からワイド文字列 (BSTR) に変換する。BSTR は wchar_t* と同じ。
パラメータ
　- [in] String ^str: CLR 文字列
　- [out] 変換されたワイド文字列 (BSTR)
戻り値
　なし
注意
　戻り値は不要になったら SysAllocString 関数を使って解放すること。

宣言
　**BSTR AToW(LPSTR mbcs)**
機能
　マルチバイト文字列からワイド文字列に変換する。
パラメータ
　[in] LPSTR mbcs: マルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列)
戻り値
　変換されたワイド文字列。BSTR は wchar_t* と同じ。
注意
　戻り値は不要になったら delete すること。

宣言
　**LPSTR WToA(BSTR wcs)**
機能
　ワイド文字列からマルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列) に変換する。
パラメータ
　[in] BSTR wcs: ワイド文字列。BSTR は wchar_t* と同じ。
戻り値
　変換されたマルチバイト文字列
注意
　戻り値は不要になったら delete すること。

宣言
　**void AToW(LPSTR mbcs, BSTR* wcs)**
機能
　マルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列) からワイド文字列に変換する。
パラメータ
　- [in] LPSTR mbc: マルチバイト文字列
　- [out] BSTR* wcs: 変換されたワイド文字列。BSTR* は wchar_t** と同じ。
戻り値
　なし
注意
　wcs は不要になったら delete すること。

宣言
　**void WToA(BSTR wcs, LPSTR* mbcs)**
機能
　ワイド文字列からマルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列) に変換する。
パラメータ
　- [in] BSTR wcs: ワイド文字列 (BSTR は wchar_t* と同じ)
　- [out] LPSTR* mbcs: 変換されたマルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列)。LPSTR* は char** と同じ
戻り値
　なし
注意
　mbcs は不要になったら delete すること。

宣言
　**String^ CStrToCliStr(CString str)**
機能
　CStringT から CLI String に変換する。
パラメータ
　[in] CString str: MFC 互換 CString 文字列
戻り値
　変換された CLR 文字列

宣言
　**CString CliStrToCStr(String ^str)**
機能
　CLI String から CStringT に変換する。
パラメータ
　[in] String ^str: CLR 文字列
戻り値
　変換された MFC 互換 CString 文字列

宣言
　**String^ StdToCliStr(std::wstring str)**
機能
　STL のワイド文字列を CLR 文字列に変換する。
パラメータ
　[in] std::wstring str: STL のワイド文字列
戻り値
　変換された CLR 文字列

宣言
　**std::wstring CliStrToStd(String ^str)**
機能
　CLR 文字列を STL のワイド文字列に変換する。
パラメータ
　[in] String ^str: CLR 文字列
戻り値
　変換された STL のワイド文字列

##使用例

```C++
// TestConvertString.cpp : メイン プロジェクト ファイルです。

#include "stdafx.h"
#include "StringConvertLib.h"
using namespace System;
using namespace StringConvertLib;

int main(array<System::String ^> ^args)
{
  Console::WriteLine(L"==== StringConvertLib のテスト ====\n");

  // C スタイル文字から CLR 文字へ
  Console::WriteLine("C スタイル文字から CLR 文字へ");
  Char ^c = CCharToClrChar('C');
  Console::WriteLine(c);

  // CLR 文字からCスタイル文字へ
  char cc = ClrCharToCChar(c);
  printf_s("%c\n", cc);

  // Cスタイル文字列から CLR 文字列へ
  String ^clistr = BSTRToString(L"Cスタイル文字列");
  Console::WriteLine(clistr);

  // CLR 文字列から C スタイル文字列へ
  String ^clistr2 = L"CLR 文字列";
  setlocale(LC_ALL, "ja-JP");
  BSTR bstr = ClrStringToBSTR(clistr2);
  wprintf_s(L"%ls\n", bstr);
  SysFreeString(bstr);

  // CLR 文字列から C スタイル文字列へ (marshaling バージョン)
  clistr2 = L"CLR 文字列 marshal";
  ClrStringToBSTR(clistr2, &bstr);
  wprintf_s(L"%ls\n", bstr);

  // マルチバイト文字列からワイド文字列へ
  BSTR bstr2 = AToW("AToW");
  wprintf_s(L"%ls\n", bstr2);
  delete bstr2;

  // ワイド文字列からマルチバイト文字列へ
  LPSTR pstr = WToA(L"WToA");
  printf_s("%s\n", pstr);
  delete pstr;

  // CStringT から CLI String へ
  CString atlstr = L"CStringT";
  String ^clistr3 = CStrToCliStr(atlstr);
  Console::WriteLine(clistr3);

  // CLI String から CStringT へ
  String ^clistr4 = L"CLI String";
  CString atlstr2 = CliStrToCStr(clistr4);
  wprintf_s(L"%ls\n", atlstr2);

  // std::wstring から CLI String へ
  std::wstring ststr = L"std::wstring";
  String ^clistr5 = StdToCliStr(ststr);
  Console::WriteLine(clistr5);

  // CLI String から std::wstring へ
  String ^clistr6 = L"CLI String";
  std::wstring ststr2 = CliStrToStd(clistr6);
  wprintf_s(L"%ls\n", ststr2.c_str());

  // 終わり
  getchar();
  return 0;
}
```

#ソース (StringConvetLib.h)

```C++
#pragma once
#include <Windows.h>
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <msclr/marshal.h>
#include <atlbase.h>
#include <atlconv.h>
#include <atlstr.h>
#include <string>

using namespace System;
using namespace msclr::interop;

#define BUFFLEN 2048

namespace StringConvertLib {
	//
	//  C スタイル文字から CLR 文字へ
	//  ==============================
	Char^ CCharToClrChar(wchar_t c)
	{
		Char ^gc = gcnew Char(c);
		return gc;
	}

	//
	//  CLR 文字からCスタイル文字へ
	//  ============================
	wchar_t ClrCharToCChar(Char ^c)
	{
		wchar_t wc = (wchar_t)c;
		return wc;
	}

	// Cスタイル文字列から CLR 文字列へ
	//   BSTR は wchar_t* と同じ
	String^ BSTRToString(BSTR bstr)
	{
		String^ str = gcnew String(bstr);
		return str;
	}

	// CLR 文字列から C スタイル文字列へ
	BSTR ClrStringToBSTR(String ^str)
	{
		array<Char>^ buff = str->ToCharArray();
		pin_ptr<Char> wptr = &buff[0];
		BSTR bstr = SysAllocString(wptr);
		return bstr;
	}

	// CLR 文字列から C スタイル文字列へ (marshaling バージョン)
	//   msclr/marshal.h が必要
	void ClrStringToBSTR(String ^str, BSTR* pbstr)
	{
		marshal_context^ context = gcnew marshal_context();
		const wchar_t* str2 = context->marshal_as<const wchar_t*>(str);
		*pbstr = SysAllocString(str2);
	}

	// マルチバイト文字列からワイド文字列へ
	BSTR AToW(LPSTR mbcs)
	{
		size_t rv;
		wchar_t buffer[BUFFLEN];
		mbstowcs_s(&rv, buffer, BUFFLEN, mbcs, _TRUNCATE);
		size_t n = 2 * wcslen((const wchar_t*)buffer) + 2;
		wchar_t* p = new wchar_t[n];
		wcscpy_s(p, n, buffer);
		return p;
	}

	// ワイド文字列からマルチバイト文字列へ
	LPSTR WToA(BSTR wcs)
	{
		size_t rv;
		char buffer[BUFFLEN];
		setlocale(LC_CTYPE, "ja-JP");
		wcstombs_s(&rv, buffer, BUFFLEN, wcs, _TRUNCATE);
		size_t n = strlen(buffer);
		n++;
		char* astr = new char[n];
		strcpy_s(astr, n, buffer);
		return astr;
	}

	// マルチバイト文字列からワイド文字列へ
	//   atlconv.h が必要
	void AToW(LPSTR mbcs, BSTR* wcs)
	{
		ATL::CA2W ws(mbcs);
		size_t n = 2 * wcslen((wchar_t*)ws) + 2;
		*wcs = new wchar_t[n];
		wcscpy_s((wchar_t*)*wcs, n, (wchar_t*)ws);
		return;
	}

	// ワイド文字列からマルチバイト文字列へ
	//   atlconv.h が必要
	void WToA(BSTR wcs, LPSTR* mbcs)
	{
		setlocale(LC_CTYPE, "ja-JP");
		ATL::CW2A str(wcs);
		size_t n = strlen((char*)str) + 1;
		*mbcs = new char[n];
		strcpy_s(*mbcs, n, (char*)str);
		return;
	}

	// CStringT から CLI String へ
	String^ CStrToCliStr(CString str)
	{
		String^ clistr = gcnew String(str.GetBuffer());
		return clistr;
	}

	// CLI String から CStringT へ
	CString CliStrToCStr(String ^str)
	{
		array<Char>^ buff = str->ToCharArray();
		pin_ptr<Char> wptr = &buff[0];
		return CString(wptr);
	}

	// std::wstring から CLI String へ
	String^ StdToCliStr(std::wstring str)
	{
		String ^clistr = gcnew String(str.c_str());
		return clistr;
	}

    // CLI String から std::wstring へ
	std::wstring CliStrToStd(String ^str)
	{
		array<Char>^ buff = str->ToCharArray();
		pin_ptr<Char> wptr = &buff[0];
		return std::wstring((wchar_t*)wptr);
	}
}

```

-