How can I concatenate 2 wstrings?

I'm actually a little baffled that I can't pull this off since working with strings is somewhat straightforward. I'm even more baffled that doing a search yields next to nothing on this. I did find some promising leads but they weren't helpful and seemed to talk about everything but giving a straight answer on how to concatenate two wstrings.

So I'm resorting to asking here...if I have two wstring variables (say "str1" and "str2") and I want to combine them, what would I need to do to do that? All I am wanting to do is take str2 and add it to str1 (IE str1 += str2;...which doesn't work!).
Please post a small example illustrating your problem.

Thanks, Andy

This works!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    wstring hello = L"Hello";
    wstring world = L"world";

    wstring msg = hello;
    msg += L" ";
    msg += world;
    msg += L"!";
    msg += L"\n";

    reverse(hello.begin(), hello.end());
    reverse(world.begin(), world.end());

    wcout << msg;

    wstring gsm = L"!" + world + L" " + hello + L"\n";

    wcout << gsm;

    return 0;
}


Output:

Hello world!
!dlrow olleH

Last edited on
closed account (Dy7SLyTq)
overload some operators
For whatever reason, that basic thing isn't working for me. Here is my code that is attached to a button. You know how my program creates various permutations. Right now I am toying with taking items from a listbox and placing them into a large edit box. Then what I'm wanting to do is maybe do another set of permutations with another word and then append the new selection to the text in the large edit box.

I've tried many different variations of this with no luck. This is the latest one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
case IDB_COPY_TO_TEXTBOX:
			{
				// Get the listbox selection
				iListBoxSelection = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0);

				wstring temp;

				if (largeTextboxString == L"")
				{
					// Put the text from the listbox item into the largeTextboxString
					SendMessage(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)largeTextboxString.c_str());
				}
				else
				{
					// Put the text from the listbox item into the largeTextboxString
					SendMessage(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)temp.c_str());

					largeTextboxString += L" ";
					largeTextboxString += temp;
				}

				// Add the updated string to the large text box
				//SendMessage(hLargeTextBox, LB_ADDSTRING, 0, (LPARAM)largeTextboxString.c_str());
				SetWindowText(hLargeTextBox, largeTextboxString.c_str());

				return true;
			}
Of course "it does not work", but the error has nothing to do with std::wstring concatenation.

The problem is in this line:
1
2
// Put the text from the listbox item into the largeTextboxString
					SendMessage(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)largeTextboxString.c_str());


wstring::c_str() returns a read-only copy, you cannot write to it.
http://www.cplusplus.com/reference/string/basic_string/c_str/

So allocate a buffer, call WinApi function, copy the buffer to std::wstring and delete the buffer afterwards.

1
2
3
4
5
6
// Put the text from the listbox item into the largeTextboxString

WCHAR buf = new WCHAR[4096]; // 
					SendMessage(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)buf);
largeTextboxString = buf;
delete buf;


It is a good idea to call LB_GETTEXTLEN for geting the exact listbox string size instead of a fixed length buffer:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb761315(v=vs.85).aspx
Thanks! I had to make the line you gave:

WCHAR buff = new WCHAR[4096];

like this:

WCHAR* buff = new WCHAR[4096];

to get it to work. But ultimately I went with this:

wchar_t* buff = new wchar_t[4096];

I tried using LB_GETTEXTLEN as you suggested but when I strictly used it, I got errors but I noticed it would work with the hardcoded value you gave of 4096. I stepped into the program and noticed that my str_len variable did get the length of the string correctly but it was still throwing an error. So I experimented by adding 1 to it and now it works fine though I am still not sure exactly how or what is going on. Here is my new code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
case IDB_COPY_TO_TEXTBOX:
			{
				// Get the listbox selection
				iListBoxSelection = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0);

				int str_len = (int)SendMessage(hListBox, LB_GETTEXTLEN, 0, 0);

				wchar_t* buf = new wchar_t[str_len + 1];

				if (largeTextboxString == L"")
				{
					// Put the text from the listbox item into the buffer
					SendMessage(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)buf);

					// Add the buffer to the string
					largeTextboxString += buf;
				}
				else
				{
					// Put the text from the listbox item into the buffer
					SendMessage(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)buf);

					// Add the buffer to the string plus a space
					largeTextboxString += L" ";
					largeTextboxString += buf;
				}

				// Add the updated string to the large text box
				SetWindowText(hLargeTextBox, largeTextboxString.c_str());

				delete buf;

				return true;
			}
Please read this again, do not always pass 0 as WPARAM, because you will get the length of the first string, this is not what you want:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb761315%28v=vs.85%29.aspx

The correct usage will be:
int str_len = (int)SendMessage(hListBox, LB_GETTEXTLEN, iListBoxSelection, 0);
Two things

#1 As you're already working with standard containers, I would use std::vector for the buffer rather than a raw pointer.

#2 If you're working with wchar_t's it's prob better to use SendMessageW explictly.

SendMessage is actually a macro which evaluates to either SendMessageW (if UNICODE is defined) or SendMessageA (if it isn't). If your Visual Studio project is configured to use the Unicode character set, then all should be well. But if this gets changed, all could go horribly wrong. So either:

A. Switch all Win32 calls to their explicit W form and use L

Easy :-)

B. Replace all wchar_t's with TCHAR's, wrap all strings and chars in the _T() macro, etc.

Painful. :-(

And it would be a bit pointless. The TCHAR mechanism was introduced so you could build Unicode for Windows NT and ANSI for Windows 95 (which was originally too stupid to understand Unicode, though they later provided a conversion layer.)

Almost all standard Win32 calls which take string have A and W variants. The exceptions are COM calls, which have their own rules, and a few others (e.g. GetProcAddress only has an ANSI form.)

(Also note there is _UNICODE as well as UNICODE to worry about. But _UNICODE effects the CRT TCHAR macros rather that the Win32 ones, which are controlled by UNICODE.)

So...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
				// Get the listbox selection
				iListBoxSelection = (int)SendMessageW(hListBox, LB_GETCURSEL, 0, 0);

				if(LB_ERR != iListBoxSelection) // assumes a single-select list box
				{
					int str_len = (int)SendMessageW(hListBox, LB_GETTEXTLEN, iListBoxSelection, 0);
					// if it's not a read-only text box, you might want to check for zero length here?

					// use vector as buffer
					vector<wchar_t> buf(str_len + 1);

					// Put the text from the listbox item into the buffer
					SendMessageW(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)&buf[0]);

					if (!largeTextboxString.empty())
						largeTextboxString += L" ";
					largeTextboxString += &buf[0];
				}


where I've assumed

1
2
3
				HWND hListBox;
				int iListBoxSelection;
				wstring largeTextboxString;


and assumed the list box is single select to I can test against LB_ERR to check there's a selection.

I also simplified you if-else sequence a bit, and am using wstring::empty() to test for a blank string (or rather, here, to test that it's not blank.)

And note that when you pass a vector to a function that expects a buffer pointer, you need to use the form &buf[0], i.e. get the address of the first element in the buffer. (For C++03, this is only legal for std::vector, as this is the only standard container that must provde contiguous storage. See PS for the C++11 position.)

Andy

PS The storage rules for std::string have changed for C++11 -- the storage must (now) be contiguous, as it happens to already be with most common implentations (there are other parts of the C++ standard which make it hard work to use non-contiguous storage...)

The cplusplus.com entry on std::string is unclear, so here's the en.cppreference.com entry's take:

The elements of a basic_string are stored contiguously, that is, for a basic_string s, &*(s.begin() + n) == &*s.begin() + n for any n in [0, s.size()), or, equivalently, a pointer to s[0] can be passed to functions that expect a pointer to the first element of a CharT[] array.

std::basic_string
http://en.cppreference.com/w/cpp/string/basic_string

Microsoft Layer for Unicode
http://en.wikipedia.org/wiki/Microsoft_Layer_for_Unicode
Last edited on
Ok, here is my latest and greatest. I've also gone through the rest of my program to swap out SendMessage with SendMessageW. I'm also still iffy on why I still need the +1 added to the str_len. Is it because we have to account for the "\0" to indicate the end of the string?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
case IDB_COPY_TO_TEXTBOX:
			{
				// Get the listbox selection
				iListBoxSelection = (int)SendMessageW(hListBox, LB_GETCURSEL, 0, 0);

				if (LB_ERR != iListBoxSelection)
				{
					int str_len = (int)SendMessageW(hListBox, LB_GETTEXTLEN, iListBoxSelection, 0);

					vector<wchar_t> buf(str_len + 1);

					// Put the text from the listbox item into the buffer
					SendMessageW(hListBox, LB_GETTEXT, iListBoxSelection, (LPARAM)&buf[0]);

					if (!largeTextboxString.empty())
						largeTextboxString += L" ";
					largeTextboxString += &buf[0];
				}

				// Add the updated string to the large text box
				SetWindowTextW(hLargeTextBox, largeTextboxString.c_str());

				return true;
			}
The MSDN page clearly states:
The return value is the length of the string, in TCHARs, excluding the terminating null character.
Ok, it isn't needed. Why is the +1 needed then?
The +1 is needed for the null term.

But the length the LB_GETTEXTLEN message obtains does not account for the NULL, so you have to add 1 on to calculate the size of the buffer required, as is already being done, for use with LB_GETTEXT.

So...

Is it because we have to account for the "\0" to indicate the end of the string?

Yes.

Andy
Last edited on
Ahh, thanks :). Too bad the other guy couldn't just give a straight answer.
Topic archived. No new replies allowed.