C++ builder XE4
ビルドした.exeファイルを実行した時、なぜか以下の違いが出た。
- Windows 7Pro (32bit, 日本語版) : OK
- Windows 8.1Pro (64bit, 日本語版) : OK
- Windows 8.1Pro (64bit, English) : Abnormal program termination
下記を参考にリモートデバッグでチェック
http://www.gesource.jp/weblog/?p=4889
(なおXE4では以下のファイルをコピー)
bccide.dll
bordbk180.dll
bordbk180N.dll
comp32x.dll
dcc32180.dll
rmtdbg180.exe
実行 > プロセスの読込み において「プログラムエントリポイントまで実行する(E)」をチェックしてもAbnormal program terminationが起きる。
このため、リモートデバッグはあきらめた。
##以下、調査した
自動生成フォームの変更 (結果:x)
- オプション > フォーム
において、「自動生成フォーム」にリストアップされているものすべてを「使用可能フォーム」に移す。 - 一番シンプルなフォームだけ「自動生成フォーム」にして、かつ「メインフォーム」にする。
結果、ダメだった。
cppとhをプロジェクトから順次削除 (原因特定できた)
複雑なcppとhを順次プロジェクトから削除して、エラーが出なくなる地点を見つける。
実際、一番シンプルなフォームだけ残してビルドしたらエラーが出なくなったので、この方法でなんらかの結果が出るだろう。
- Mxxx.cpp, MxxxIxxxs.cpp をプロジェクトから削除 >> ダメ
- OpxxxxLxxxSxxx.cpp をプロジェクトから削除 > ダメ
- CxxxdOxxx.cpp をプロジェクトから削除 > ダメ
- IxxxOxxxx.cpp, LoxxxRxxOxxx.cppを削除 > ダメ
- ThxxxMxxx.cpp, MxPxxxUxxx.cpp, MxxDxx.cppを削除 > 動いた
5番目の手順で削除したファイルが関係あるのだろうか。
5番目で削除したファイルを順次戻して、エラーが発生するファイルは MxPxxxUxxx.cpp であることがわかった。
あとは、このファイルの何が問題か見つけないといけない。
エラーコード
MxPxxxUxxx.cpp に定義していた以下のコードが Abnormal Program Terminationを発生するということが特定できた。
static const TDateTime kReturnInError_TDateTime = StrToDateTime(L"2000/1/1 00:00:01");
以下は問題ない。
static const TDateTime kReturnInError_TDateTime = (TDateTime)0;
日時の解釈が英語圏でちょっと違うのかもしれない。
以下もOKなので、constで日時を指定したい場合は、StrToDateTime()でなく以下のようにしておいたほうが英語OS対応にもいいのかもしれない。
static const TDateTime kReturnInError_TDateTime = EncodeDate(2000, 1, 1) + EncodeTime(0, 0, 1, 0);
調査完了。
結構、はまってしまった。
エラーの詳細
ButtonClickにてTDateTime dt = StrToDateTime(L"2000/1/1 00:00:01");
を実行すると「日付または時刻ではありません。」のエラーがでる。
該当の英語版WindowsのControl Panel > Regionを見たときに、日付表記がM/d/yyyy
のようになっているので、上記のフォーマットではだめになるのだろう。
こういうことがあるということは、安易にStrToDateTime()を使っていると、英語OS上で問題が起きてしまうのかもしれない。
###対策
StrToDateTime()でなくVarToDateTime()を使えば上記の問題は回避できるようだ。
参考1: http://www2.big.or.jp/~osamu/Delphi/Tips/key.cgi?key=42
文字通り StrToDateTime という関数もあるんですが、これは yyyy/mm/dd などの形式にしか対応していません。ちょっとひねって VarToDateTime を使えば、ほぼどんな形式でもうまく変換できます。string -> Variant は自動で変換されるので、そのまま文字列を渡すだけで使えます。
参考2: SO
If you want to convert some special DateTime-Formats you should better use VarToDateTime instead of StrToDateTime.
以下を試した。
TDateTime dt1 = VarToDateTime(L"2000/1/1 00:00:01");
ShowMessage(dt1.FormatString(L"yyyy/mm/dd hh:nn:ss"));
TDateTime dt2 = StrToDateTime(L"2000/1/1 00:00:01");
ShowMessage(dt2.FormatString(L"yyyy/mm/dd hh:nn:ss"));
上記のコードでdt1の結果は英語版Windowsにおいて意図通りの日時として認識された。
VarToDateTime()を使う時の注意点はよくわからないが、とりあえずはStrToDateTime()よりはよさそう。
## VarToDateTimeのエラー
(2015/10/05 追記)
TDateTime dt1 = VarToDateTime(L"2000/1/1 00:00:01");
は
TDateTime dt1 = VarToDateTime("2000/1/1 00:00:01");
のようにLプリフィックスなしでないとエラーがでるかもしれない。