[MFC] UnicodeプロジェクトでのWriteString

ファイルの末尾に受け取ったCString文字列を書き込むという関数作成時のメモ。まー、ログを簡単に出力する為の関数です。

bool underwrite( CString& str ){

 CStdioFile file;
 CString filePath = _T(".\\foo.bar");

 // ファイルを書込専用、ほかのプロセスからは書込不可、
 // ファイルがなければ作成し、あればそのファイルを読み込む
 if( file.Open( filePath, CFile::modeWrite | CFile::shareDenyWrite |
   CFile::modeCreate | CFile::modeNoTruncate ) ){
  // ファイルオープン成功

  // ポインタをファイルの末尾に移動
  file.SeekToEnd();
  // 文字列書込
  file.WriteString( str );
  // ファイルを閉じる 
  file.Close();

 }else{
  return false;
 }
 return true;
}

上記のようなプログラムを組んだのだが、どういうわけか上手く日本語が書き込めない。
「0123あいうabc」のような文字列を与えた場合、ファイルに出力されるのは「0123」となってしまう。

原因はWriteStringがロケール依存関数だった為。WriteStringが呼ばれる前に以下を呼んでやる必要がある。

// #include "locale.h"
_tsetlocale( LC_ALL, _T("") ); //システム既定のロケールに設定
_tsetlocale( LC_ALL, _T("japanese") ); //日本語の場合

参考にしたところ

[MFC] Windowsアプリで引数を受け取る

GUIベース(Windowsフォームを提供)するアプリケーションを作成しているのだけど、ここにいてバッチ処理をしたいとかいう風に言われた。今まで苦労してGUIの機能を作ってきたのは何だったのだろう?

と、いうことでコマンドライン引数を受け取った場合にフォームを生成せずに処理できるように作り直すことになりましたとさ。

コマンドライン引数の受け取り

WindowsアプリケーションのInitInstance()の中に以下のコードを書けばいいらしい。

//VC2005, 文字コード: Unicode 

// コマンドライン引数を格納する配列
CStringArray cmdParams;

{ // コマンドライン引数の取得
 CString cmdParam( m_lpCmdLine );
 cmdParam.Trim();

 // パラメータの取得
 int curPos = 0;
 CString param = cmdParam.Tokenize( _T("/"), curPos );
  while( param != "" ){
  cmdParams.Add( param.Trim() );
	param = cmdParam.Tokenize( _T("/"), curPos );
 }
}

上記の例だと example.exe /test1 /test2とコマンドライン引数を与えると
 cmdParams[0]: test1
 cmdParams[1]: test2
となる。

コマンドライン引数取得の改良 2007/11/07

パラメータを半角スペースで区切る。ただし、"(ダブルクォーテーション)で括われていた場合、その中は半角スペースを含むことができるようにする。

// コマンドライン引数の取得
CString cmdParam( m_lpCmdLine );
cmdParam.Trim();
CStringArray cmdParams; // 引数を格納する動的配列

CString param;
int curPos;

// パラメータの分解
cmdParam.Trim();
do{
 if( cmdParam.GetLength() >= curPos && cmdParam.GetAt( curPos ) == '\"' ){
  // "で括われた引数
  ++curPos;
  param = cmdParam.Tokenize( _T("\""), curPos );
 } else {
  // 引数を半角スペースで分解
  param = cmdParam.Tokenize( _T(" "), curPos );
 }
 cmdParams.Add( param.Trim() );
}while( param != "" );
// 最後に空の文字列が追加されてしまうので取り除く
cmdParams.Remove( cmdParams.GetCount() );

処理の切り分け

引数が与えられていなければcmdParamsは空のはずなのでこれを使ってやればいい。

if( cmdParams.GetSize() > 0 ){
 // フォームを生成しない場合
 // 引数の値のエラートラップ、処理ルーチンの呼び出し
}else{
 // フォームを生成するコード
}

参考にしたサイト

コマンドプロンプトで、~.exe *** の *** を判定するには?

[MFC] 10進RGB値を16進RGB値に変換

RGB値を3つのint型変数で持っている時、このRGB値を16進表記にし、1つの文字列として表したい場合に何かいい方法って無いものでしょうか?
この手のものはよく使いそうなのでライブラリにありそうですが、探し方が悪かったのか見つけられませんでした。で、10進RGB値を受け取って16進RGB値の文字列を返す関数を作ってみた。

処理系はVisual C++ 2005 (VC8.0)です。

struct RGB{
 int red;
 int green;
 int blue;
};

CString convToHexRGB( RGB* rgb ){

 // 入力値チェック
 if( red >= 0 && red <= 255 && green >= 0 && green <= 255 &&
  blue >= 0 && blue <= 255 ){
   return CString::Format( "#%02X%02X%02X", rgb->red, rgb->green, rgb->blue );
 }

 // 入力値が不正な場合
 return "";
}

実際はRGBのそれぞれの値はunsigned char型で格納すれば0~255までの値しか入らないので入力値チェックも必要なくなるのだろうけど、とある制限によりint型でRGBの各値が格納されている為上記のような実装となりました。

以下はunsigned char型で実装した場合

struct RGB{
 unsigned char red;
 unsigned char green;
 unsigned char blue;
};

CString convToHexRGB( RGB* rgb ){
 return CString::Format( "#%02X%02X%02X", rgb->red, rgb->green, rgb->blue );
}

何でRGB値しか入らないのに、しかも仕様書で0~255までの値となっているにもかかわらずint型で入れなければならないんだろう…。
処理系固定なのに…。