

Visual Studio 2015 Community
EditionのMFCでwin32ダイアログベースアプリを作成していて、release版でのddx_textの不可解な挙動に困っています。Debug版では正常に見えます。  
以下、double変数を表示するエディットボックス3個と更新用ボタン1個をもつアプリをbugcheck.exeとして、挙動を説明します。

アプリ起動直後、エディットボックスにはdouble変数f1, f2, f3の初期値が表示されます(f3は0)。  
f2のエディットボックスに0を入力するとその表示は0になります。  
しかしその後ボタンを押してダイアログをUpdateData(false)すると、f2とf3の表示値が5になります。(入れた場所と数字で若干バリエーションあり)  
どうやらDDX_Text(pDX, IDC_EDIT1,
m_f1)等の第三パラメーターに0を入れると結果がおかしくなる場合があるのではないかと辺りを付けましたが、0を使わない訳には行かないので、困っています。

以下bugcheckについて、自動生成部分からの差分相当のコードを示します。

\--bugcheckDlg.h--

[code]

    class CbugcheckDlg : public CDialogEx
    {
    略
    public:
    double m_f1;
    double m_f2;
    double m_f3;  // Read only
    afx_msg void OnBnClickedButton1(); //Update controls
    afx_msg void OnEnChangeEdit1();
    afx_msg void OnEnChangeEdit2();
    
    }
    
[/code]

\--bugcheckDlg.cpp--

[code]

    CbugcheckDlg::CbugcheckDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_BUGCHECK_DIALOG, pParent)
    , m_f1(1.5)
    , m_f2(0.1)
    , m_f3(0.0)
    {
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
    
    void CbugcheckDlg::DoDataExchange(CDataExchange* pDX)
    {
    CDialogEx::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EDIT1, m_f1);
    DDX_Text(pDX, IDC_EDIT2, m_f2);
    DDX_Text(pDX, IDC_EDIT3, m_f3);
    }
    
    BEGIN_MESSAGE_MAP(CbugcheckDlg, CDialogEx)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON1, &CbugcheckDlg::OnBnClickedButton1)
    ON_EN_CHANGE(IDC_EDIT1, &CbugcheckDlg::OnEnChangeEdit1)
    ON_EN_CHANGE(IDC_EDIT2, &CbugcheckDlg::OnEnChangeEdit2)
    END_MESSAGE_MAP()
    
    void CbugcheckDlg::OnBnClickedButton1()
    {
    UpdateData(false);
    }
    
    void CbugcheckDlg::OnEnChangeEdit1()
    {
    UpdateData(true);
    }
    
    void CbugcheckDlg::OnEnChangeEdit2()
    {
    UpdateData(true);
    }
    
[/code]

\--resource.h--

[code]

    #define IDC_EDIT1                       1000
    #define IDC_BUTTON1                     1001
    #define IDC_EDIT2                       1002
    #define IDC_EDIT3                       1003
    
[/code]

\--bugcheck.rc--

[code]

    IDD_BUGCHECK_DIALOG DIALOGEX 0, 0, 202, 129
    STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
    EXSTYLE WS_EX_APPWINDOW
    CAPTION "bugcheck"
    FONT 9, "MS UI Gothic", 0, 0, 0x1
    BEGIN
    DEFPUSHBUTTON   "OK",IDOK,67,91,50,14
    PUSHBUTTON      "キャンセル",IDCANCEL,121,91,50,14
    EDITTEXT        IDC_EDIT1,44,31,40,14,ES_AUTOHSCROLL
    PUSHBUTTON      "UpdateData(false)",IDC_BUTTON1,99,33,63,14
    EDITTEXT        IDC_EDIT2,44,52,40,14,ES_AUTOHSCROLL
    EDITTEXT        IDC_EDIT3,44,71,40,14,ES_AUTOHSCROLL | ES_READONLY
    LTEXT           "f1",IDC_STATIC,28,34,8,8
    LTEXT           "f2",IDC_STATIC,29,55,8,8
    LTEXT           "f3",IDC_STATIC,30,75,8,8
    END
    
[/code]

\--ソースはここまで--


CC BY-SA 3.0
2015-09-14T09:55:20.067
16592
2015-10-30T21:32:17.833
2015-09-14T11:35:04.083
2383
12321
question
c++ visual-studio mfc
VC2015で、double変数ddx_textのトラブル


`DDX_Text(...,
double&)`→`AfxTextFloatFormat()`で呼んでいる`_sntscanf_s()`の動作がおかしいようです。  
MFCでなくても以下のWin32コンソールアプリケーションで再現します。

[code]

    #include <stdio.h>
    #include <stdlib.h>
    #include <tchar.h>
    
    int main()
    {
        const int TEXT_BUFFER_SIZE = 400;
        TCHAR szBuffer[TEXT_BUFFER_SIZE] = _T("2.3");
        double d;
        if (_sntscanf_s(szBuffer, _countof(szBuffer), _T("%lf"), &d) != 1) {
            return 1;
        }
        else {
            _tprintf_s(_T("%f\n"), d);
        }
        _tcscpy_s(szBuffer, _T("0"));
        if (_sntscanf_s(szBuffer, _countof(szBuffer), _T("%lf"), &d) != 1) {
            return 1;
        }
        else {
            _tprintf_s(_T("%f\n"), d);
        }
        return 0;
    }
    
[/code]

Debugビルドでは、

[code]

    2.300000
    0.000000
    
[/code]

と正常に表示されますが、Releaseビルドでは

[code]

    2.300000
    3.000000
    
[/code]

と表示されます。

`DDX_Text(...,
CString&)`で取り出して、自分で`stringstream`等で`double`へ変換するぐらいしか、回避方法は浮かびません。


CC BY-SA 3.0
2015-09-15T08:03:08.643
16629
2015-09-15T08:03:08.643
7291
16592
answer


alphaさんの説明を読んでもう少し調べました。

[code]

    char buffer[] = { '0', '\0', '1', '2', '3', '\0' };
    double d;
    snscanf(buffer, std::size(buffer), "%lf", &d);
    
[/code]

とやると`"0\0"`を通り越して`"123"`をパースしてしまうようです。  
この問題が発生するのは`scanf()`系の中でもバッファーサイズを指定するsnバージョンのようです。バッファーサイズを指定しない`sscanf()`などでは`"0\0"`で停止するため発生しませんでした。

`DDX_Text()`がテンプレート関数やインライン関数であれば`snscanf()`などを再定義する回避策もありましたがそうもいかないようで、`DDX_Text()`を直接再定義するくらいしか手はなさそうです。

* * *

[bug in
_sntscanf_s](https://connect.microsoft.com/VisualStudio/feedback/details/1773279/bug-
in-sntscanf-s)にてバグ報告がなされており、Windows 10 Build
10576で修正されていることを確認しました。（Windowsに含まれているDLLを使用する必要があるためMulti-threaded
DLLでリリースビルドを行う必要があります。デバッグビルドではVS付属のDLLを使ってしまいます。）  
今後、Windows UpdateでDLLの更新、VS2015 Update1等でDebug DLLやソースコードの更新が行われるものと思われます。


CC BY-SA 3.0
2015-09-16T12:24:49.960
16697
2015-10-30T21:32:17.833
2015-10-30T21:32:17.833
4236
4236
16592
answer