1

I was wondering if it were possible to take text input from a text box (CreateWindowEx "EDIT") and store it as a string or even better a vector

I need to have a textbox that the user can enter or paste text and when I click a button it will alphabetize the words and count unique words etc...

so far I have it reading it in as characters (i dont know how to make it a string) and alphabetizes the characters

so if I type in: how now brown cow it will output: bchnnoooorwwww instead of: brown cow how now

my code under the WM_COMMAND case is

int length;
length = GetWindowTextLength(textbox) + 1;
vector<wchar_t> list(length);
GetWindowText(textbox, &list[0], length);
wstring stxt = &list[0];
wstring str(stxt);
sort(str.begin(), str.end());
SetWindowText(sortedOutput, &str[0]);
genpfault
  • 51,148
  • 11
  • 85
  • 139
Dweeb
  • 31
  • 1
  • 1
  • 3
  • When you say you want it in a vector and you don't know how to make it a string what do you mean? You have it in a vector and then put it in a string in your example. – Retired Ninja Aug 25 '17 at 03:47
  • Why do you need the intermediate `stxt` and `str` variables? Just use `list` in the `std::sort` call. Also, it isn't a good idea to name your variable `list`, as there is a `std::list` in C++. – PaulMcKenzie Aug 25 '17 at 03:48
  • I think his problem has nothing to do with the interaction with the edit box. He wants to sort words, not characters. So he wants a ``std::vector`` and sort that. So he looks for the equivalent of a function ``string -> string[]`` which is called ``words`` in Haskell. – BitTickler Aug 25 '17 at 03:49
  • I want to take in text from a text box and upon clicking a button I want to sort the individual words not the each letter. so like having an element contain a full word instead of a letter. Sorry if I am not making this clear and sorry if my code is all over... ive been looking at hundreds of examples and just trying to Frankenstein it together. I am very new to win API – Dweeb Aug 25 '17 at 03:51
  • `std::wstringstream ss(list.data()); std::wstring word; while(ss >> word) vec.push_back(word);` – Retired Ninja Aug 25 '17 at 03:51
  • https://stackoverflow.com/questions/39050225/extract-individual-words-from-string-c or https://stackoverflow.com/questions/236129/most-elegant-way-to-split-a-string – Retired Ninja Aug 25 '17 at 03:53
  • The Windows API was built for C, not C++, so I don't see a significantly better way of getting the text from an edit control. But splitting that string into an array of words is a completely separate problem. – Mark Ransom Aug 25 '17 at 04:22
  • I have written a working c++ program that takes input from the console and sorts it alphabetically, counts unique words, and counts how many times each word is repeated. The problem is now I need to take input from a text box and perform those functions when I press a button. I have never worked outside the console with c++ so I am having a hard time bridging my working code and a text box with a button – Dweeb Aug 25 '17 at 04:41
  • 1
    @Dweeb *I have never worked outside the console with c++ so I am having a hard time bridging my working code and a text box with a button* -- It shouldn't matter where the string you're trying to process comes from, whether it is a file, from the keyboard, a text box, a socket, hard-coded into the program, etc. As long as you have the string, the concern is to process that string. Maybe writing a function that takes a string and *only does that job of creating the array*, instead of stuffing everything in your `WM_COMMAND` handler probably explains the point better to you. – PaulMcKenzie Aug 25 '17 at 16:12
  • @Dweeb *I have never worked outside the console with c++* -- And neither have many of the best C++ progammers in the industry. Some of the most sophisticated programs written in C++ are "console programs". The problem is not console versus non-console, the issue is that given a string, break it down into an array of strings. Doing that task has nothing to do with whether you're writing a console app or not. – PaulMcKenzie Aug 25 '17 at 16:19

3 Answers3

2

This answer may be of use to you in devising a solution. I don't really know of one that is not hacky, but it can be done casting of the constness of c_string() from std::string.

https://stackoverflow.com/a/1986974/128581

A std::string's allocation is not guaranteed to be contiguous under the C++98/03 standard, but C++11 forces it to be. In practice, neither I nor Herb Sutter know of an implementation that does not use contiguous storage.

Notice that the &s[0] thing is always guaranteed to work by the C++11 standard, even in the 0-length string case. It would not be guaranteed if you did str.begin() or &*str.begin(), but for &s[0] the standard defines operator[] as:

Returns: *(begin() + pos) if pos < size(), otherwise a reference to an object of type T with value charT(); the referenced value shall not be modified Continuing on, data() is defined as:

Returns: A pointer p such that p + i == &operator for each i in [0,size()]. (notice the square brackets at both ends of the range)

Thus it follows you can do something like this:

int len = GetWindowTextLength(hwnd) + 1;
std::string s;
s.reserve(len);
GetWindowText(hwnd, const_cast<char*>(s.c_str()), len - 1);

Which is pretty ugly. Welcome any more "correct" answers, though.

Regarding when unicode is enabled on your build, you have to use a wstring or equivalent. Testing that out just a moment ago, this works:

std::wstring title;
title.reserve(GetWindowTextLength(m_window_handle) + 1);
GetWindowText(m_window_handle, const_cast<WCHAR *>(title.c_str()), title.capacity());

In general, regarding the windows api its useful to google their all caps typedefs and figure out what they really are.

Regarding splitting strings, std::string isn't particular good at this kind of manipulation. This is where std::stringstream (or wstringstream for unicode) comes in handy. I am fairly certain stringstream is not guaranteed to be contiguous in memory, so you can't really go around writing directly into its buffer.

// Initialize a stringstream so we can extract input using >> operator
std::wstringstream ss;
ss.str(title);

// Make a vector, so we can store our words as we're extracting them
// and so we can use sort later, which works on many stl containers
std::vector<std::wstring> words;
std::wstring word;

// This will evaluate to false and thus end the loop when its done
// reading the string word by word
while(ss >> word)
{
  words.push_back(word);
}

Then proceed with your sorting, but on the new vector words.

Josh
  • 12,602
  • 2
  • 41
  • 47
  • I saw this or something similar earlier tonight, the const_cast gives me a "is incompatible with parameter of type "LPWSTR" Would you recommend a different approach to this? I just need a textbox and button, I have the actual code that does the sorting and counting unique words as a console c++ project. I just don't know how to bridge them together – Dweeb Aug 25 '17 at 03:59
  • Ah, from memory, I'm guessing its because of an encoding issue. My code is for an older style non-unicode enabled build (probably not the default on vs 2015/2-17). LPWSTR is pointer to wide string. The equivalent in std library is wstring. Try std::wstring instead of string. Further, the only difference between a console project and a windows gui project is a few project settings (_CONSOLE define becomes _WINDOWS, linker system>subsystem gets changed) along with switching to WinMain/wWinMain with a specific signature instead of main(). – Josh Aug 25 '17 at 04:03
  • Updated my answer with tested solution for unicode version. – Josh Aug 25 '17 at 04:16
  • that did compile with no errors! sort(title.begin(), title.end()); SetWindowText(sortedOutput, &title[0]); outputs what I put in. i.e. doesnt alphabetize. Getting closer though! ah, maybe because it is not a vector and c.str isnt a function of vector... hmm – Dweeb Aug 25 '17 at 04:33
  • oh, didn't understand what you were doing with the string. you're looking to sort by words? use a wstringstream per retired ninjas comment. The >> operator will tokenize (split strings by whitespace) by default. Same thing works for lots of things in C++, like reading from files using ifstream etc. I'll update again. – Josh Aug 25 '17 at 04:38
  • Take text (a sentence or paragraph) a user types into a text box. When the user hits the button it will alphabetize the words in a different "STATIC" textbox, and also count total unique words and repeated words. Just need to be able to take the text a a vector of strings and not characters – Dweeb Aug 25 '17 at 04:45
  • Added a section at the end for you. You're normally not going to get people who are going to answer multiple unrelated questions, especially if they look like a homework assignment, which this does :-). – Josh Aug 25 '17 at 04:52
  • Hey thanks a lot! This is my first time posting so guess I will be spoiled. This is not for homework I promise, my c++ classes were console only and covered basic OOP topics. win32 is all new to me and trying to find resource. Thanks again I will continue to figure problem out. Not enough reputation to upvote you yet :( – Dweeb Aug 25 '17 at 05:03
  • [std::string::data()](http://en.cppreference.com/w/cpp/string/basic_string/data) was invented, so that you don't have apply `const_cast`s. – IInspectable Aug 25 '17 at 09:50
  • @IInspectable hey thats neat, didn't know about that - think I'd probably have to use /std:c++latest to get that to work on msvc, but good to know for the future. – Josh Aug 25 '17 at 17:06
  • @Dweeb You should be able to close this out as answered regardless of rep. – Josh Aug 25 '17 at 17:07
  • You should NEVER `const_cast` the returned `c_str()` of a `std::string` and then write to it. This is undefined behavior and can result in the internal metadata of the string getting out of sync with the contents!!! – tjwrona1992 Apr 06 '20 at 17:07
0

Your problem is not a winapi problem. While not the only way, you found a solution to transfer a string back and forth to your edit box.

How to turn that string into a list/vector of strings, with words being the elements of that list/vector is in fact an STL problem.

Basically, you are looking for the C++ equivalent of the C# function String.Split().

And there is already a good question and answer for that, here:

Most elegant way to split a string?

So, all you have to do is a sort of round trip:

  1. Get string from TextBox
  2. Convert string to a std::vector<string>, using your split function (see the other question for how to do that).
  3. Sort the vector in the usual way.
  4. Convert the vector back to a string, which is the inverse of your split function (Hint: std::ostringstream).
  5. Set the resulting string as the text of your TextBox.

Depending on your preferences regarding multi character strings and globalization, you might decide to stick to the ASCII versions at first. On windows, you can compile to MBCS or ASCII. The respective string types are then respectively (TCHAR and LPCTSTR or WCHAR and LPCWSTR or CHAR and LPCSTR). All win32 functions come in two flavors, distinguished by a trailing A or W at the end of the function name, respectively.

AFAIK, while there is std::string and std::wstring, there is no standard implementation for std::basic_string<TCHAR>, which would work along with your compile options.

As for the window handling, here some code example (snippets):

In InitInstance(), I created the dialog (IDD_FORMVIEW) with my input edit box, my button and my static output area as a child window of the main application window:

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Store instance handle in our global variable

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    HWND hWndChild = CreateDialogW(hInstance, MAKEINTRESOURCE(IDD_FORMVIEW), hWnd, dlgProc);
    if (NULL == hWndChild)
    {
        DWORD lastError = GetLastError();
        wchar_t msg[100];
        swprintf_s(msg, _countof(msg), L"error code: 0x%0X\n", lastError);
        OutputDebugString(msg);

    }
    if (!hWnd)
    {
        return FALSE;
    }
    ShowWindow(hWnd, nCmdShow);
    ShowWindow(hWndChild, SW_SHOW);
    UpdateWindow(hWnd);

    return TRUE;
}

CreateDialogW() takes as the last parameter a pointer to the dialog handler function, which is called dlgProc() in this example.

This is how that dlgProc() function looks like:

// Message handler for our menu which is a child window of the main window, here.
static INT_PTR CALLBACK dlgProc(
    _In_ HWND   hwndDlg,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    BOOL processed = false;

    switch (uMsg)
    {
    case WM_INITDIALOG:
        break;
    case WM_NOTIFY:
        break;
    case WM_COMMAND:
    {
        auto sourceId = LOWORD(wParam);
        auto code = HIWORD(wParam);
        if (IDC_BUTTON1 == sourceId)
        {
            if (BN_CLICKED == code)
            {
                wchar_t text[1024];
                GetDlgItemText(hwndDlg, IDC_EDIT1, text, _countof(text));
                // TODO: do your string processing here (string -> string)
                SetDlgItemText(hwndDlg, IDC_STATIC, text);
                processed = TRUE;
            }
        }
    }
        break;
    default: 
        break;
    }
    return processed;
}
BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • I will look into this, thanks! I've only written c++ console applications, I just need to figure out how to make it work using a textbox and button – Dweeb Aug 25 '17 at 04:09
  • @Dweeb Separation of concerns. Your code shows 2 things. The sorting of your string (which you do by character, not by word) and your Win32 UI handling. You got the UI handling basically right (you found the right functions). So I concluded, this is not your problem. You can simplify your code while you explore how the Win32 functions work by not converting but by getting the string, dumping it to debug output (``OutputDebugString()``), then setting a dummy content as a result. This way, you can tackle both problems independently of each other. – BitTickler Aug 25 '17 at 06:23
  • thank you for all of this information, I will be spending my day reading win32 tutorials so I can grasp all this better – Dweeb Aug 25 '17 at 16:28
0

I have just mixed a few lines of code to convert wchar_t to wstring to std::string. Here you go!

string GetWindowStringText(HWND hwnd)
{
    int len = GetWindowTextLength(hwnd) + 1;
    vector<wchar_t> buf(len);
    GetWindowText(hwnd, &buf[0], len);
    wstring wide = &buf[0];
    string s(wide.begin(), wide.end());
    return s;
}

This has a vector so you'll need to include it.

Adrian W
  • 4,563
  • 11
  • 38
  • 52
ReelDude
  • 1
  • 1
  • OP's code already used `vector`. Only `std::string` is new. So, you might want to recommend to include ``. – Adrian W May 03 '19 at 19:16