0

I want to learn more about C++ coding, especially to design and create a desktop application.

To begin, I want to create a notification app where I can make a task by giving the task a name and content. For this, I use 2 vectors (name and content).

I initialize the vectors like this:

std::vector <LPWSTR> NameTask; 
std::vector <LPWSTR> ContentTask; 

After, I simply create a button to add the name and content into the vectors:

if (wmId == ID_BUTTON) {
    //local variables (only for the Button event)
    int len_name = GetWindowTextLength(TextBox_Name) + 1; //give the lenght value of the text in the textbox
    int len_content = GetWindowTextLength(TextBox_content) + 1; //give the lenght value of the text in the textbox
    wchar_t Text_name[100] = L""; //Wchar is compatible with LPWSTR
    wchar_t Text_content[100] = L""; //Wchar is compatible with LPWSTR
            
    // get the text of the Text edit and put it in local variable name and content
    GetWindowText(TextBox_Name, Text_name, len_name);
    GetWindowText(TextBox_content, Text_content, len_content);
    //verify if the texts are empty
    if (wcslen(Text_name) == 0 || wcslen(Text_content) == 0) {
        MessageBox(hWnd, L"name and content can't be empty", L"MessageBox", MB_OK);
    }
    else {
        NameTask.push_back(Text_name); // set name of task in vector of NameTask
        ContentTask.push_back (Text_name); // set content of task in vector of ContentTask
        //ComboBox_AddString(Combobox, Text_name); // add the title of the task in the combotext
        //SetWindowText(TextBox_Name, L""); //empty the textbox
        SetWindowText(TextBox_content, NameTask.at(0)); // visualize first element of vector name
    }           
}

The problem that I have is that, when I add a new element to the vector, and then I go to visualize it, it always shows me the last element added. Even when I use SetWindowText(TextBox_content, NameTask.at(0));

And when I use another button to visualize the name and content, it gives a strange output:

left name and right content

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 4
    `std::wstring` should replace `wchar_t` (including `LPWSTR`) usage. – Jarod42 Feb 09 '22 at 13:31
  • Try with GetWindowTextW instead of GetWindowText (GetWindowText expect a string made of char) – Bolo Feb 09 '22 at 13:34
  • 1
    `NameTask.push_back(Text_name)` stores **pointer** on the **local** buffer, which goes out of scope, so dangling pointer. – Jarod42 Feb 09 '22 at 13:34
  • Does this answer your question? [Values changing after adding object pointer to vector](https://stackoverflow.com/questions/56056169/values-changing-after-adding-object-pointer-to-vector) – Raymond Chen Feb 09 '22 at 13:40
  • The vectors store pointers to your strings. As soon as you reach the `}` at the end, those strings go away. And then there is nothing to point to anymore ([dangling pointer](https://stackoverflow.com/questions/17997228/what-is-a-dangling-pointer)). So store `std::wstring` and not a pointer. – BoP Feb 09 '22 at 14:35
  • I change everyting to std::wstring. but when i add the values (Wstrings) and I read them from the vector I still get strange values. – MrEngineer Feb 09 '22 at 14:41
  • hallo everyone, i found my mistake. I will add my solution. and sorry everyone to bother with my stupide question – MrEngineer Feb 09 '22 at 15:07
  • @Bolo "*GetWindowText expect a string made of char*" - incorrect. `GetWindowText` is a preprocessor macro in `winuser.h` that maps to either `GetWindowTextW()` or `GetWindowTextA()` depending on whether or not `UNICODE` is defined, respectively (same with `SetWindowText` mapping to `SetWindowTextW()` or `SetWindowTextA()`). `UNICODE` must be defined in this case, because the code would fail to compile otherwise when trying to pass `wchar_t*` pointers to `GetWindowTextA()` and `SetWindowTextA()`. – Remy Lebeau Feb 09 '22 at 17:54

3 Answers3

0

The problem is that you are storing dangling pointers in your vectors. Your if block is allocating local arrays, then storing pointers to those arrays into the vectors. When the if block exits, the arrays are destroyed, but the vectors are still pointing at them.

You need to make a copy of the text data you are retrieving from your UI. You can use std::wstring to handle that.

Try something more like this:

struct Task
{
    std::wstring Name; 
    std::wstring Content; 
};

std::vector<Task> Tasks;

...

if (wmId == ID_BUTTON) {
    int len_name = GetWindowTextLength(TextBox_Name);
    int len_content = GetWindowTextLength(TextBox_content);

    if (len_name == 0 || len_content == 0) {
        MessageBox(hWnd, L"name and content can't be empty", L"MessageBox", MB_OK);
    }
    else {
        ++len_name;
        ++len_content;

        std::wstring Text_name(L'\0', len_name);
        std::wstring Text_content(L'\0', len_content);
            
        len_name = GetWindowText(TextBox_Name, &Text_name[0], len_name);
        Text_name.resize(len_name);

        len_content = GetWindowText(TextBox_content, &Text_content[0], len_content);
        Text_content.resize(len_content);

        Task task;
        task.Name = Text_name;
        task.Content = Text_content;
        Tasks.push_back(task);

        //ComboBox_AddString(Combobox, Text_name.c_str());
        //SetWindowText(TextBox_Name, L"");
        SetWindowText(TextBox_content, Text_name.c_str());
    }
}

...

// use Tasks[index].Name(.c_str()) and Tasks[index].Content(.c_str()) as needed...
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

I change it and it work. it may not be the ideal and correct way but i will let it how it is.

if (wmId == ID_BUTTON) {
            //local variables (only for the Button event)
            int len_name = GetWindowTextLength(TextBox_Name)+1; //give the lenght value of the text in the textbox
            int len_content = GetWindowTextLength(TextBox_content)+1; //give the lenght value of the text in the textbox
            wchar_t Text_name[len] = L""; //Wchar is compatible with LPWSTR
            wchar_t Text_content[len] = L""; //Wchar is compatible with LPWSTR
            
            // get the text of the Text edit and put it in local variable name and content
            GetWindowText(TextBox_Name,  (LPWSTR) Text_name, len_name);
            GetWindowText(TextBox_content, (LPWSTR)Text_content, len_content);
            //verify if the texts are empty
            if (len_name == 1 || len_content == 1) {
                MessageBox(hWnd, L"name and content can't be empty", L"MessageBox", MB_OK); 
            }
            else {
                NameTask.push_back(Text_name); // set name of task in vector of NameTask
                ContentTask.push_back(Text_content); // set content of task in vector of ContentTask
                ComboBox_AddString(Combobox,Text_name); //set name in combobox
                SetWindowText(TextBox_Name, L""); //empty the textbox
                SetWindowText(TextBox_content, L""); //empty the textbox
                
                
                
            }

        }
-1

I don't know what you really want to do. At first, i would change the wchar_t to std::wstring. This way you don't need to specify the size. But, here I have a sugestion

if (wmId == ID_BUTTON) {
        //local variables (only for the Button event)
        int len_name = GetWindowTextLength(TextBox_Name) + 1; //give the lenght value of the text in the textbox
        int len_content = GetWindowTextLength(TextBox_content) + 1; //give the lenght value of the text in the textbox
        std::wstring Text_name = L""; //Wchar is compatible with LPWSTR
        std::wstring Text_content = L""; //Wchar is compatible with LPWSTR
        
        // get the text of the Text edit and put it in local variable name and content
        GetWindowText(TextBox_Name, Text_name, len_name);
        GetWindowText(TextBox_content, Text_content, len_content);
        //verify if the texts are empty
        if (wcslen(Text_name) == 0 || wcslen(Text_content) == 0) {
            MessageBox(hWnd, L"name and content can't be empty", L"MessageBox", MB_OK);
        }
        else {
            NameTask.push_back(Text_name); // set name of task in vector of NameTask
            ContentTask.push_back (Text_name); // set content of task in vector of ContentTask
            //ComboBox_AddString(Combobox, Text_name); // add the title of the task in the combotext
            //SetWindowText(TextBox_Name, L""); //empty the textbox
            SetWindowText(TextBox_content, NameTask.at(0)); // visualize first element of vector name
        }           
    }
  • I have 2 Textboxes where i put the a name and a content. I extract those values ( with getWindowText). Now i want to put the Text_name value into the vector NameTask and the Text_content into the ContentTask vector by using push_back. but everytime iwhen i want to see the value in the 2 vectors they give those strange values like the photo (left name and right content) and I don't know how the vector has those strange values – MrEngineer Feb 09 '22 at 14:27
  • @GeorgeVictor This code will not compile, as you can't pass `std::wstring` objects to `GetWindowText()` or `SetWindowText()` as shown. – Remy Lebeau Feb 09 '22 at 17:57
  • @MrEngineer we know what you *want*, you don't need to repeat yourself. You already stated your issue in the question. But you don't seem to understand what George's answer is trying to tell you to accomplish that goal. – Remy Lebeau Feb 09 '22 at 17:58
  • @RemyLebeau I don't kmow if it is correct but to get the text from the texteditor I use it as next : GetWindowText(TextBox_Name, (LPWSTR) Text_name.c_str(), len_name); But again it seem to work but i don't know if it is the correct way – MrEngineer Feb 10 '22 at 07:59
  • @MrEngineer *technically* no, because the pointer returned from `c_str()` is to `const` data, so it is illegal to write to it. But in *practice*, `c_str()` returns a pointer to the string's internal char buffer, which is writable memory, so it will "work". The correct way to cast off `const` is to use `const_cast`. In C++17 and later, you can just use the non-const `Text_name.data()` instead. In C++11..14, you can use `&Text_name[0]` instead. – Remy Lebeau Feb 10 '22 at 16:26