How to compute LPWSTR size when using GetDlgItemText() function - c++

My request is small
I am using GetDlgItem() to get text from a text field , but I have a problem in the fourth parameter (i.e. size of buffer) .
LPWSTR txtbuff;
GetDlgItemText(hwnd, IDC_EDIT1, txtbuff, 50); // the fourth parameter (50)
MessageBox(NULL,txtbuff,L"Error message",MB_OK)
How can I compute size of txtbuff

You can use GetDlgItem to get the control's HWND and GetWindowTextLength to determine how many characters are held by your control.

You need to allocate a buffer yourself:
WCHAR txtbuff[50];
GetDlgItemText(hwnd, IDC_EDIT1, txtbuff, 50);
/* or... */
GetDlgItemText(hwnd, IDC_EDIT1, txtbuff, sizeof(textbuff)/sizeof(textbuff[0]));
MessageBox(NULL, txtbuff, L"Error message", MB_OK);
A simple LPWSTR variable is a pointer, in your example, you have a pointer, but it doesn't point anywhere yet. By creating the buffer explicitly, you specify the size, and can provide it to GetDlgItem.

Its not the size of the buffer. It is the amount of the string you actually want to copy. You could set it to any non zero size you want in theory. The size of your own buffer on the other hand is a different story, just make sure you dont overflow.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms645489%28v=vs.85%29.aspx

You can use sizeof like so:
TCHAR txtbuff[50];
GetDlgItemText(hwnd, IDC_EDIT1, txtbuff, sizeof(txtbuff) * sizeof(TCHAR));

Related

WinAPI GetWindowText as string

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]);
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.
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:
Get string from TextBox
Convert string to a std::vector<string>, using your split function (see the other question for how to do that).
Sort the vector in the usual way.
Convert the vector back to a string, which is the inverse of your split function (Hint: std::ostringstream).
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;
}
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.

Text formatting & font changing in hwnd windows

Me again guys, I've managed to learn up till now about most basics regarding window creation and message system, now I wanted to ask about formatting because I didn't manage to find anything about my particular case on google.
Here is what it looks like so far:
The boxes with 0s in them are Static windows since I didn't really get the Rect paint job. I also need it to be dynamic; the boxes will display an element from an int array that I'll transfer over to a wchar_t array for output.
Now is it possible to change the font, lets say increase it and make it bold? Or is it only possible using print text function?
Any help would be much appreciated since I'm really trying to make this "centered" so to speak.
EDIT:
Another question just so I don't make another post:
I just noticed that my stupid static windows don't update after I change the values in array I'm printing in them and repaint them. E.g. each zero is contained in wchar_t array[16][15]; and after I print this setup and change lets say array[13][0] = 'A'; nothing happens, is it due to Static window type or is it because of me being noobish and using MoveWindow to repaint them XD?
The windows message WM_SETFONT will do it. First there should be a font created, and then it is used in the parameter for WM_SETFONT.
When the font and window have been created, use
SendMessage(wnd, WM_SETFONT, (WPARAM)font, FALSE);
to set the default font for the window.
If you want to use a default windows font, you can create one like this:
HFONT font = NULL;
NONCLIENTMETRICS ncm;
memset(&ncm, 0, sizeof(NONCLIENTMETRICS));
ncm.cbSize = sizeof(NONCLIENTMETRICS);
if(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
sizeof(NONCLIENTMETRICS), &ncm, 0)) {
font = CreateFontIndirect(&ncm.lfMessageFont);
}
There are other default fonts in NONCLIENTMETRICS that you could use.
Of course you can also create a font from a typeface name and other information, but there is no guarantee that there is such a font on different systems.
HFONT CreateFont(
int nHeight, // height of font
int nWidth, // average character width
int nEscapement, // angle of escapement
int nOrientation, // base-line orientation angle
int fnWeight, // font weight
DWORD fdwItalic, // italic attribute option
DWORD fdwUnderline, // underline attribute option
DWORD fdwStrikeOut, // strikeout attribute option
DWORD fdwCharSet, // character set identifier
DWORD fdwOutputPrecision, // output precision
DWORD fdwClipPrecision, // clipping precision
DWORD fdwQuality, // output quality
DWORD fdwPitchAndFamily, // pitch and family
LPCTSTR lpszFace // typeface name
);

Crash after SendMessage to TEdit

I'm trying to get data from another program. And... it's giving me an error all the time! Is there something wrong?
HWND hWnd = FindWindow(NULL, L"MyProgram");
if (hWnd)
{
HWND TPageControl = FindWindowEx(hWnd, NULL, L"TPageControl", NULL);
TPageControl = FindWindowEx(hWnd, TPageControl, L"TPageControl", NULL);
HWND TTabSheet = FindWindowEx(TPageControl, NULL, L"TTabSheet", NULL);
HWND TEdit = FindWindowEx(TTabSheet, NULL, L"TEdit", NULL);
int editlength = SendMessage(TEdit, WM_GETTEXTLENGTH, 0, NULL);
TCHAR* Targets = new TCHAR( editlength + 1 );
int count = SendMessage(TEdit, EM_GETLINE, editlength + 1, (LPARAM) Targets);
std::wcout << Targets << "\n";
//delete Targets;
}
but if i'm debugging, it's working.
You are not following the documentation for EM_GETLINE. The first parameter specifies the line index. I'm assuming you are sending this message to a single-line edit control, and it simply gets ignored. The second parameter must hold the length of the buffer:
Before sending the message, set the first word of this buffer to the size, in TCHARs, of the buffer.
The remarks for edit controls are also relevant:
The copied line does not contain a terminating null character.
While parameters for EM_GETLINE get automatically marshaled across process boundaries (like all message parameters for messages in the range 0 to W_USER-1), you might want to consider sending WM_GETTEXT instead, if you are dealing with a single-line edit control:
int editlength = SendMessage(TEdit, WM_GETTEXTLENGTH, 0, NULL);
TCHAR* Targets = new TCHAR[editlength + 1];
int count = SendMessage(TEdit, WM_GETTEXT, editlength + 1, (LPARAM) Targets);
// NUL-terminate buffer in case the text did not fit
Targets[count] = _T('\0');
std::wcout << Targets << "\n";
If you are sending WM_GETTEXT to a hung application your application will hang as well. Call GetWindowText to work around this. The secret life of GetWindowText has additional background information.
If you need to retrieve a specific line from a multi-line edit control the following is more appropriate. In contrast to your code it sends an EM_LINEINDEX and EM_LINELENGTH message to retrieve the appropriate buffer size:
int characterIndex = SendMessage(TEdit, EM_LINEINDEX, lineIndex, 0);
int lineLength = SendMessage(TEdit, EM_LINELENGTH, characterIndex, 0);
TCHAR* pBuffer = new TCHAR[lineLength + 1];
// Set the size of the buffer
*(WORD*)pBuffer = lineLength + 1;
// Retrieve the line
int characterCount = SendMessage(TEdit, EM_GETLINE, lineIndex, (LPARAM)pBuffer);
// NUL-terminate buffer
pBuffer[characterCount] = _T('\0');
A word on why the initial code appears to work when run under a debugger: It's not the debugger, that makes a difference. It's the Debug Build that does. A debug configuration will fill allocated memory with specific byte patterns (0xCD for operator new[]()). The effect of this is that the buffer passed when sending EM_GETLINE is interpreted as having size 0xCDCD (52685 in decimal). In a release configuration on the other hand, the buffer contents are usually 0x00, i.e. the buffer is interpreted as having size 0. This is not to say that the debug build works. It merely masks an error.
I used GetWindowText, works like a sharm I did't want to use fancy calculations. The error really only appears on multiline text, since the buffer size would be calculated correctly.
MS Documentation GetWindowText

c++/win32 - LB_GETTEXT only returns one character

HWND listBox = GetDlgItem(hDlg, IDC_SCORES_LIST);
LRESULT r = SendMessage (listBox , LB_GETCURSEL, 0, 0);
LRESULT l = SendMessage (listBox , LB_GETTEXTLEN, (WPARAM)r, 0);
char* text = new char [l +1];
SendMessage (listBox, LB_GETTEXT, (WPARAM)r, (LPARAM)text);
The result of this code should be that something like "Level 2" is stored in "text". However for some reason at the end of this, only "L" is stored in "text". I'm racking my brains for what could be causing this, but I can't think of anything.
You are probably compiling in UNICODE, so the functions you are calling operate with UTF-16 text. You should use wchar_t instead of char, or even better, the TCHAR macro (which maps to either wchar_t of char depending on whether you compile in UNICODE or ANSI).

Parameters referenced from the same variable point to different locations

I'm trying to store HWND pointers in an int vector along with other data, I'm using the following code to get the data and store it on creation:
void createscalingwindow(HWND &cswpara0,DWORD cswpara1,const CHAR* cswpara2,
const CHAR* cswpara3,DWORD cswpara4,int cswpara5,
int cswpara6,int cswpara7,int cswpara8,HWND cswpara9,
HMENU cswpara10,HINSTANCE cswpara11,LPVOID cswpara12)
{
cswpara0 = CreateWindowEx (cswpara1, cswpara2, cswpara3, cswpara4, cswpara5,
cswpara6,cswpara7,cswpara8,cswpara9,cswpara10,
cswpara11,cswpara12);
sizevalues.push_back((int)&cswpara0);
snprintf (buffer, 20,"%d", sizevalues[zero]);
MessageBox (NULL, buffer, "pointer", NULL);
sizevalues.push_back(cswpara5);
sizevalues.push_back(cswpara6);
sizevalues.push_back(cswpara7);
sizevalues.push_back(cswpara8);
return;
}
This following code is a prototype that currently only shows the values in a messagebox, but I later plan to have it resize child windows to scale with the parent
void scalewindowsize (HWND &ownerwin, HWND &childwin)
{
/*check owner window*/
char buffer[100];
int checknumber = 0;
while (checknumber < sizevalues.size())
{
if (sizevalues[checknumber] == (int)&ownerwin)
{
snprintf (buffer, 100,"%d", sizevalues[checknumber]);
MessageBox (NULL, buffer, "foundit", NULL);
break;
}
snprintf (buffer, 20,"%d", (int)&ownerwin);
checknumber = (checknumber + 5);
MessageBox (NULL, buffer, "fail", NULL);
}
return;
}
The problem is that the first Messagebox in createscalingwindow produces a value of 4235304 while the second one produces an entirely different number (the number varies). Why is this?
UPDATE: Found out part of the cause, in order to reproduce this the HWND used as a parameter to scalewindowsize must be used in a window procedure with the same parameter HWND in that window procedure.
Don't store non-int values in an int vector. That's asking for trouble.
Instead create a class that has fields (with the proper types) for all your values, and create a vector that contains objects of that class.
Your numbers changing is however likely caused by you taking the address of a local variable and using it after the function declaring the variable returns. You should just push the value of the HWND, not the address where it's stored. Handles are plain numbers, so there's no need to pass them by reference, unless you plan to change them in the function (I don't see why you'd need to do that in createscalingwindow either - you could just return the value)