66446


MFCでチェックボックスリストコントロールに追加した項目をプログラム終了時に保存し、プログラム開始時にその保存した内容をGetPrivateProfileStringA関数で読みだす処理を作っていますが、CStringをconst
char*にキャストする方法が分からず問題で困っています。

※`const TCHAR*`には変換できますが、`const char*`は変換できないので困っています。

[![画像の説明をここに入力](https://i.stack.imgur.com/7rOZC.png)](https://i.stack.imgur.com/7rOZC.png)

この処理作成のために作成途中のソースコードは以下です。  
GetPrivateProfileStringA関数で読みだすためにCstringをconst char*にキャストできない処理は、  
void ToDoDlg::GetIniSetting(CCheckListBox &m_check_list_box)内にあります。

ToDoDlg.h内

[code]

    #pragma once
    
    // ToDoDlg ダイアログ
    
    class ToDoDlg : public CDialogEx
    {
        DECLARE_DYNAMIC(ToDoDlg)
    
    public:
        ToDoDlg(CWnd* pParent = nullptr);   // 標準コンストラクター
        virtual ~ToDoDlg();
    
    // ダイアログ データ
    #ifdef AFX_DESIGN_TIME
        enum { IDD = IDD_TODO_DIALOG };
    #endif
    
    protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート
    
    
    // 実装
    protected:
        HICON m_hIcon;
        // 生成された、メッセージ割り当て関数
        virtual BOOL OnInitDialog();
        DECLARE_MESSAGE_MAP()
    
    private:
        /*privateメンバ変数*/
        // To Do項目チェックボックスコントロールリスト変数
        CCheckListBox m_check_list_box;
        // To Do項目表示文字列入力変数
        CString m_add_edit;
        // iniファイルへのフルパスを保持する変数
        CString m_ini_full_file;
    
        /*privateメンバ関数*/
        //afx_msg void OnEnKillfocusCaptionEdit();
        afx_msg void OnBnClickedItemAddButton();
        void AddItemToCheckBoxList();
        afx_msg void OnIdok();
        /*************************************************
        このアプリを起動したときのチェックボックスリストの項目を
        このアプリを最後に終了したときのチェックボックスリストの項目に設定する処理群
        *************************************************/
        afx_msg void OnDestroy();
        afx_msg void OnBnClickedCheckItemToDoneTabButton();
        void SaveCheckBoxListToIni();
        void GetIniSetting(CCheckListBox &m_check_list_box);
        /*************************************************/
    };
    
    
[/code]

ToDoDlg.cpp内

[code]

    // ToDoDlg.cpp : 実装ファイル
    //
    
    #include "stdafx.h"
    #include "LearningPokect.h"
    #include "ToDoDlg.h"
    #include "afxdialogex.h"
    
    // ToDoDlg ダイアログ
    
    IMPLEMENT_DYNAMIC(ToDoDlg, CDialogEx)
    
    ToDoDlg::ToDoDlg(CWnd* pParent /*=nullptr*/)
        : CDialogEx(IDD_TODO_DIALOG, pParent)
        , m_add_edit(_T(""))
    {
    }
    
    ToDoDlg::~ToDoDlg()
    {
    }
    
    void ToDoDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialogEx::DoDataExchange(pDX);
        DDX_Text(pDX, IDC_ITEM_ADD_EDIT, m_add_edit);
        DDX_Control(pDX, IDC_TO_DO_LIST, m_check_list_box);
    }
    
    
    BEGIN_MESSAGE_MAP(ToDoDlg, CDialogEx)
        ON_BN_CLICKED(IDC_ITEM_ADD_BUTTON, &ToDoDlg::OnBnClickedItemAddButton)
        ON_COMMAND(IDOK, &ToDoDlg::OnIdok)
        ON_WM_DESTROY()
        ON_BN_CLICKED(IDC_CHECK_ITEM_TO_DONE_TAB_BUTTON, &ToDoDlg::OnBnClickedCheckItemToDoneTabButton)
    END_MESSAGE_MAP()
    
    BOOL ToDoDlg::OnInitDialog()
    {
        CDialogEx::OnInitDialog();
    
        // "バージョン情報..." メニューをシステム メニューに追加します。
    
        // IDM_ABOUTBOX は、システム コマンドの範囲内になければなりません。
        ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
        ASSERT(IDM_ABOUTBOX < 0xF000);
    
        CMenu* pSysMenu = GetSystemMenu(FALSE);
        if (pSysMenu != nullptr)
        {
            BOOL bNameValid;
            CString strAboutMenu;
            bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
            ASSERT(bNameValid);
            if (!strAboutMenu.IsEmpty())
            {
                pSysMenu->AppendMenu(MF_SEPARATOR);
                pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
            }
        }
    
        // このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
        //  Framework は、この設定を自動的に行います。
        SetIcon(m_hIcon, TRUE);         // 大きいアイコンの設定
        SetIcon(m_hIcon, FALSE);        // 小さいアイコンの設定
    
        // TODO: 初期化をここに追加します。
    
        // MFCのチェックリストコントロールが二重に表示されるバグを直すコード
        // 参考：https://stackoverflow.com/questions/57951333/cchecklistbox-items-get-overlapped-on-selection-if-app-build-using-visual-studi
        m_check_list_box.SetFont(GetFont());
    
        return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
    }
    
    // ToDoDlg メッセージ ハンドラー
    
    /*Enter押下時のイベントハンドラ*/
    void ToDoDlg::OnIdok()
    {
        // TODO: ここにコマンド ハンドラー コードを追加します。
        AddItemToCheckBoxList();
    }
    
    // アプリケーションのバージョン情報に使われる CAboutDlg ダイアログ
    
    void ToDoDlg::OnBnClickedItemAddButton()
    {
        // TODO: ここにコントロール通知ハンドラー コードを追加します。
    
        AddItemToCheckBoxList();
    }
    
    /*************************************************
    
    関数名 AddItemToCheckBoxList
    
    機能   エディットコントロールから入力文字を取得し、チェックボックスリストコントロールにTo Do項目を追加する。
    
    戻り値 なし
    
    備考 参考サイト：忘れた。
    
    *************************************************/
    void ToDoDlg::AddItemToCheckBoxList()
    {
        /**/
    
        // エディットコントロールの値変数にエディットコントロールで
        // 入力された文字列を代入する
        UpdateData(TRUE);
    
        /*取得した文字列をチェックボックスリストコントロールに設定する
        ダイアログウィンドウを最初に表示する時に
        ToDoDlg::OnEnKillfocusCaptionEdit()が呼ばれた際
        チェックボックスリストコントロールに空文字の項目が作成されないように
        以下の条件文を作成した*/
        // エディットコントロールの値変数が空でない場合
        if (!m_add_edit.IsEmpty())
        {
            // チェックボックスリストコントロールに
            // エディットコントロールの値変数に格納されている
            // 文字列を追加する
            m_check_list_box.AddString(m_add_edit);
    
            // チェックボックスリストコントロールに
            // エディットコントロールの値変数に格納し終えたので、
            // エディットコントロールの値変数に入力されている文字列を削除する
            m_add_edit = _T("");
            UpdateData(FALSE);
    
    
        }
    }
    
    /*************************************************
    
    関数名 OnBnClickedCheckItemToDoneTabButton
    
    機能 チェックボックスコントロールにあるチェック項目を完了タブに移動する。
    
    戻り値 なし
    
    備考 参考サイト：忘れた。
    
    *************************************************/
    void ToDoDlg::OnBnClickedCheckItemToDoneTabButton()
    {
        // TODO: ここにコントロール通知ハンドラー コードを追加します。
        for (size_t i = 0; i < m_check_list_box.GetCount(); i++)
        {
            // チェックボックスリストコントロールのチェックボックスに✔が入っている場合
            if (m_check_list_box.GetCheck(i) == BST_CHECKED)
            {
                // iniファイルに保存する。
                //SaveCheckBoxListToIni();
            }
        }
    }
    
    /*************************************************
    
    関数名 SaveCheckBoxListToIni
    
    機能   チェックボックスリストにある項目とチェック状態を保持する。
    
    戻り値 なし
    
    備考 参考サイト：
    http://pg-sample.sagami-ss.net/?eid=29
    
    *************************************************/
    void ToDoDlg::SaveCheckBoxListToIni()
    {
        /*iniファイルを作成する*/
    
        /// 実行ファイルのファイルパスを取得する
    
        // iniファイルを保持するための一時メモリ領域
        LPTSTR p_cs_ini_file_full_path = m_ini_full_file.GetBuffer(_MAX_PATH);
        // ※_MAX_PATHが260なのは、次の計算による：1 + 2 + 256 + 1か[drive] [：\] [path] [null] = 260
    
        // p_cs_ini_file_full_pathにこのアプリのexeファイルへのフルパスを返す。
        GetModuleFileName(NULL, p_cs_ini_file_full_path, _MAX_PATH);
    
        // m_ini_full_fileにexeファイルへのフルパスからexeを除いたパスを設定する。
        m_ini_full_file.GetBufferSetLength(wcslen(p_cs_ini_file_full_path) - 3);
        // m_ini_full_fileにiniという文字列を加えてiniファイルへのフルパスを保持する。
        m_ini_full_file += _T("ini");
    
        // iniファイルにセーブされたCHECKBOXLISTDATAセクションデータを取得する。
        //GetIniSetting();
        //// 取得したiniファイルのCHECKBOXLISTDATAセクションデータを取得をこのアプリの座標に設定する。
        //this->SetWindowPlacement(&this_app_position);
    }
    
    /*************************************************
    
    関数名 GetIniSetting
    
    機能   チェックボックスリストの項目データをiniファイルから読込
    
    戻り値 WINDOWPLACEMENT構造体
    
    備考 参考サイト：
    http://pg-sample.sagami-ss.net/?eid=29
    
    *************************************************/
    void ToDoDlg::GetIniSetting(CCheckListBox &m_check_list_box)
    {
        //char temp_CHECKBOXLISTDATA_member[_MAX_PATH];
    
        //// Cstringをconst char*へ変換する方法が分からないので、質問サイトで質問する。
        //const TCHAR* forCast = (LPCTSTR)m_ini_full_file;
    
        //const char* charPointer_ini_full_file = (LPCSTR)m_ini_full_file;
    
        //GetPrivateProfileStringA("CHECKBOXLISTDATA", "checkBoxes[0]", NULL, temp_CHECKBOXLISTDATA_member, _MAX_PATH, ),
        //m_check_list_box.AddString(
    
        //this_app_position.ptMinPosition.x = GetPrivateProfileInt(L"WINDOWPLACEMENT", L"ptMinPosition.x", 0, m_ini_full_file);
    }
    
    /*************************************************
    
    関数名 OnDestroy
    
    機能   チェックボックスリストコントロールに追加した項目をiniファイルに保存する。
    
    戻り値 なし
    
    備考 参考サイト：忘れた。
    
    *************************************************/
    void ToDoDlg::OnDestroy()
    {
        CDialogEx::OnDestroy();
    
        // TODO: ここにメッセージ ハンドラー コードを追加します。
    
        // iniファイルを保持するための一時メモリ領域
        LPTSTR p_cs_ini_file_full_path = m_ini_full_file.GetBuffer(_MAX_PATH);
    
        // p_cs_ini_file_full_pathにこのアプリのexeファイルへのフルパスを返す。
        GetModuleFileName(NULL, p_cs_ini_file_full_path, _MAX_PATH);
    
        // m_ini_full_fileにexeファイルへのフルパスからexeを除いたパスを設定する。
        m_ini_full_file.GetBufferSetLength(wcslen(p_cs_ini_file_full_path) - 3);
        // m_ini_full_fileにiniという文字列を加えてiniファイルへのフルパスを保持する。
        m_ini_full_file += _T("ini");
    
        for (size_t i = 0; i < m_check_list_box.GetCount(); i++)
        {
            // チェックボックスリストのチェックボックスの文字列をiniファイルに書き込む。
            CString checkBoxName;
            m_check_list_box.GetText(i, checkBoxName);
            TCHAR temp_CHECKBOXLISTDATA_member[_MAX_PATH];
    
            swprintf_s(temp_CHECKBOXLISTDATA_member, L"checkBoxes[%d]", i);
    
            // CHECKBOXLISTDATAのチェックボックスの文字列は配列等で持たせる必要がある。
            WritePrivateProfileString(L"CHECKBOXLISTDATA", temp_CHECKBOXLISTDATA_member, checkBoxName, m_ini_full_file);
    
            // チェックボックスリストのチェックボックスに✔が入っている場合
            //if (m_check_list_box.GetCheck(i) == BST_CHECKED)
            //{
    
            //}
        }
    }
    
[/code]

ネットで調べて書いてある方法をいろいろと試しましたが、上手くいきませんでした。

解決策を教えて頂けるとありがたいです。どうかよろしくお願い致します。


CC BY-SA 4.0
2020-05-08T21:27:27.470
66441
2020-05-09T11:47:38.630
2020-05-09T00:35:50.220
21034
21034
question
c++ mfc
MFCでCStringをconst char*へ変換する方法が分からない


大前提としてATL/MFCは廃止こそされていませんがとても古いものであり、ドキュメントもまともにメンテナンスされていません。既に知識をお持ちの方が必要に応じて使用するものであり、
**新たに習得することはお勧めできません** 。具体的には[Visual Studio
2012のATLとMFCの文字列変換マクロ](https://docs.microsoft.com/ja-jp/previous-
versions/visualstudio/visual-
studio-2012/87zae4a3\(v=vs.110\))では適切に説明されていましたが、[現行の文字列変換マクロ](https://docs.microsoft.com/ja-
jp/cpp/atl/reference/string-conversion-macros?view=vs-2015)では無関係な`DEVMODE`
`TEXTMETRIC`などが登場しマクロ名も`デモデヤ2W`等まったく人間には理解できない内容となっています（誤訳ではなく原文から誤っている）。

* * *

> CStringをconst char*にキャストする方法が分からず

については前スレッド[ファイル名の文字列を作る処理でgetbufferを使ってバッファを取得する意味が分からない](https://ja.stackoverflow.com/a/65903/4236)も関連しますが、文字列を格納するバッファを用意し、その先頭を指すデータが`const
char*`となることを理解する必要があります。

そのためにはまず`CString`が保持している文字列を`char*`文字列に変換する必要があります。そのための機能として[ATL と MFC
の文字列変換マクロ](https://docs.microsoft.com/ja-jp/previous-
versions/visualstudio/visual-
studio-2012/87zae4a3\(v=vs.110\))が提供されています。`CString`が保持している文字列は`T`型であり`char*`は`A`型ですので、`CT2A`クラスを使用して変換を行います。これによって得られた変数は`const
char*`として使用できます。

[code]

    // ok
    CT2A afile(m_ini_full_file);
    GetPrivateProfileStringA(..., ..., ..., ..., ..., afile);
    
[/code]

なお、同ページの一時的なクラスのインスタンスに関する警告でも説明されていますが、

[code]

    // ok
    GetPrivateProfileStringA(..., ..., ..., ..., ..., CT2A(m_ini_full_file));
    
    // ng
    const char* afile = CT2A(m_ini_full_file);
    GetPrivateProfileStringA(..., ..., ..., ..., ..., afile);
    
[/code]

というように使い方に注意が必要です。これは、`CT2A`が保持している文字列バッファの生存期間によるものです。

* * *

なお、kunifさんが少し触れられていますが、より根本の問題として文字列とそれを扱うクラス・APIを理解する必要があります。WindowsにはANSI、Wide、TCHARの３種類の文字列があります。

  * `char*` / `wchar_t*` / `TCHAR*`
  * `CStringA` / `CStringW` / `CString`
  * `GetPrivateProfileStringA` / `GetPrivateProfileStringW` / `GetPrivateProfileString`

とそれぞれ用意されています。今回の質問は、TCHAR系の`CString`とANSI系の`GetPrivateProfileStringA`とを組み合わせたことがそもそもの問題です。


CC BY-SA 4.0
2020-05-09T02:19:05.887
66446
2020-05-09T02:19:05.887
4236
66441
answer


まあすでに答えは書かれているのですが、そもそもの問題として

  * `CString` から `const char*` に変換することが間違っていて
  * `CString` から `const TCHAR *` に変換することが想定される動作

なわけです。ゆえに

  * `GetPrivateProfileStringA()` を使うこと自体が間違いで
  * `GetPrivateProfileString()` を使うことが正しい
  * `GetPrivateProfileStringW()` を使うのが現代流かもしれない

のです。詳細は
[GetPrivateProfileString関数でエラー](https://ja.stackoverflow.com/questions/61092/)
で解説してある通り Win32API の歴史的事情による変則な扱いによります。

  * `GetPrivateProfileStringA()` を使うと多国語の共存ができない (Win95 系で動作する)
  * `GetPrivateProfileStringW()` を使うと多国語の共存ができる (Win95 系で動作しない)
  * `GetPrivateProfileString()` がどっちなのかを理解しないと先に進めない

わけですが、すでに Win95 系は死滅して久しいので、今時 `GetPrivateProfileStringA()`
を使う必然は全くないというあたりを認識していただくとこの辺の事情が理解できるようになると思います。


CC BY-SA 4.0
2020-05-09T11:47:38.630
66461
2020-05-09T11:47:38.630
8589
66441
answer