早速本題です。
strcpy_s を使っている人がどれだけいるかわかりませんが、少なくはないと思います。
私もそのひとりです。
今回は、この strcpy_s が失敗したときについてです。
失敗したときの挙動
strcpy_s 関数ですが、第二引数に「コピー先の文字列バッファのサイズ」を指定すると思います。指定サイズを超えてコピーが行われた場合、戻り値にエラー値が返ってきます。
と、思っていたのですが実際の挙動は少し違っていました。
MSDN より引用
strDestination または strSource が null ポインタの場合、またはコピー先文字列が小さすぎる場合は、「パラメータの検証」に説明されているように、無効なパラメータ ハンドラが呼び出されます。実行の継続が許可された場合、これらの関数は EINVAL を返し、errno を EINVAL に設定します。
どうやら、「無効なパラメータ ハンドラ」というものが呼ばれるようです。
試してみた
ということで、以下のコードで試してみました。#include <gtest/gtest.h> int main(int, char**) { const char a[] ="test"; char b[2]; ASSERT_EQ(0, strcpy_s(b, a)); return 0; }
デバッグビルドで実行すると
で、中止 or 無視 を選択するとプログラムが終了。
リリースビルドの場合、
このように WerFault.exe が実行される。
「プログラムを終了します」を選択すると文字通りプログラムが終了します。
この点が少し問題で、この時失敗も返さず、例外も投げず終了していくので、
クラッシュレポートを出力するようなプログラムの場合、何もできずに終了してしまいます。
gtest の対応は?
上の検証コードが gtest のアサーションのみ利用しているのは理由があって、gtest は上の挙動と異なるからです。
#include <gtest/gtest.h> int main(int, char**) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
TEST(CRT, strcpy_s) { const char a[] ="test"; char b[2]; ASSERT_EQ(0, strcpy_s(b, a)); }
このコードをリリースビルドで実行すると、
なにごともなく終了していきます。
gtest では、
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
XML が出力されない
さて、ここで困ったことがあります。この状態では、テスト結果の XML が出力されないのです。
XML が出力されていない時点で「何らか」の失敗があったことはわかりますが、
できることなら XML が出力されて欲しいものです。
XML 出力されるようにしてみる
「無効なパラメータ ハンドラ」を設定します。それには、_set_invalid_parameter_handler 関数を使用します。
こちらの第一引数にハンドラの関数ポインタを渡します。
void _invalid_parameter( const wchar_t * expression, // 引数式 const wchar_t * function, // CRT 関数の関数名 const wchar_t * file, // CRT ソースファイル名 unsigned int line, // CRT ソースファイルの行 uintptr_t pReserved );
そして、このハンドラで例外を投げるようにします。(例外の内容はなんでもいいです。)
あとは、RUN_ALL_TESTS の前で _set_invalid_parameter_handler を読んで登録するだけです。
まとめたコードがこちら
#include <gtest/gtest.h> static void OnInvalidParameter(const wchar_t * , const wchar_t * , const wchar_t * , unsigned int , uintptr_t) { throw std::invalid_argument("invalid parameter error"); }
int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); _set_invalid_parameter_handler(OnInvalidParameter); return RUN_ALL_TESTS(); }
TEST(CRT, strcpy_s) { const char a[] ="test"; char b[2]; ASSERT_EQ(0, strcpy_s(b, a)); }
実行すると、失敗がレポートされ最後まで実行されます。
最後に
今回のケースで問題になることって、極稀だと思います。ただ私の場合、身近なところで実際に問題になったことがあって、
それからこのことを初めて知りました。
プログラムが終了するパターンの一つなので、知っておいてソンはないと思います。
以上。
0 件のコメント:
コメントを投稿