I tried using CRichEditCtrl::GetLine() to retrieve the text of a given line of a rich-edit control in an MFC application built with VS2015 in Unicode mode, and running on Windows 10.
I wrote this helper function:
CString GetLine(CRichEditCtrl& richEdit, const int lineNum)
{
int lineLength = richEdit.LineLength(richEdit.LineIndex(lineNum));
if (lineLength == 0)
{
// Empty line
return CString();
}
const int kMinBufferLength = sizeof(int) / sizeof(wchar_t);
const int bufferLength = max(kMinBufferLength, lineLength);
CString line;
wchar_t* buffer = line.GetBuffer(bufferLength);
lineLength = richEdit.GetLine(lineNum, buffer, bufferLength);
line.ReleaseBuffer(lineLength);
return line;
}
This code works fine, except for lines containing only one character. In this case, CRichEditCtrl::GetLine() returns 2 (instead of the expected 1), and the output buffer contains the correct character, followed by a \r.
Why is that? Why is the \r added only for single-character lines and not for lines containing more characters?
I was able to fix that adding a special case if like this:
// Code inserted after the richEdit.GetLine() call, before the line.ReleaseBuffer() call:
// *** Special Case ***
// It seems that when there's only one character (e.g. 'C') in the line,
// CRichEditCtrl::GetLine() returns 2, and appends a '\r' after
// the read character in the output buffer.
if ((lineLength == 2) && (buffer[1] == L'\r'))
{
// Chop off the spurious '\r'
lineLength = 1;
}
However, it's not clear to me the reason for this special-case behavior.
P.S: The CRichEditCtrl::GetLine() MFC code that is invoked is:
int CRichEditCtrl::GetLine(_In_ int nIndex, _Out_writes_to_(nMaxLength, return) LPTSTR lpszBuffer, _In_ int nMaxLength) const
{
ASSERT(::IsWindow(m_hWnd));
ENSURE(sizeof(nMaxLength)<=nMaxLength*sizeof(TCHAR)&&nMaxLength>0);
*(LPINT)lpszBuffer = nMaxLength;
return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);
}
So this seems just a tiny wrapper around the EM_GETLINE message.
The MSDN doc for EM_GETLINE states that "the return value is the number of TCHARs copied" (in my case, the wchar_ts). For one-character lines the return value is two, instead of the expected one. So, sounds like the rich-edit control is actually returning the single character followed by a spurious \r in this special case.
For lines containing more than one characters, the returned value is the actual number of characters, as expected (I tried with simple English/ASCII characters, to avoid complications of Unicode surrogate pairs and other stuff).
I got it to work without special-casing by using the other overload of CRichEditCtrl::GetLine():
*(int*) buffer = lineLength;
lineLength = richEdit.GetLine(lineNum, buffer);
The reference for EM_GETLINE says that you have to write the size of the buffer into the buffer, while this actually is the number of characters you request.
The reference for the macro Edit_GetLine() which sends EM_GETLINE has it correct:
cchMax The maximum number of characters to be copied to the buffer.
The macro writes the cchMax parameter to the buffer before calling SendMessage() which is exactly the same as my code above.
I also think that the condition in the 3-parameter overload of CRichEditCtrl::GetLine() which causes an exception if you request less than 2 characters, is incorrect.
The return value is zero (0) if the line is not valid.
If the line is empty it makes sense to return 1 and '\r' in the buffer. That would mean that '\r' is always returned when the line number is valid.
The function reference says that the buffer should be at least 4 bytes long, because a WORD is written to the buffer before being passed to SendMessage.
sizeof(nMaxLength) in the ENSURE function is the size of an int or WORD.
CRichEditCtrl::GetLine
CRichEditCtrl::GetLineCount has some code.
Related
I am working on a project that takes a Lua string and converts it into a C string – not at all difficult, of course. However, I run into trouble when attempting to convert a binary representation of a function, i.e. one produced by a call to string.dump, to a C string. I am having trouble reading the entire string.
While it is not the ultimate goal of the project, consider the following simple example where I print out the characters in a string one-by-one using a C function called chars that I have registered for use in Lua:
static void chars(char* cp) {
char* pointer = cp;
while (*pointer) {
printf("%c\n", *pointer);
++pointer;
}
return;
}
static int lua_chars(lua_State* L) {
lua_len(L, 1);
size_t len = static_cast<size_t>(lua_tonumber(L, -1)) + 1;
lua_pop(L, 1);
if (len > 0) {
char* cp = static_cast<char*>(malloc(len));
strcat(cp, lua_tostring(L, 1));
chars(cp);
free(cp);
}
return 0;
}
Calling chars from a Lua script would look like this:
chars("Hello World!")
and would print out the characters one by one with each followed by a newline.
Now to the actual issue. Consider this example where I declare a function in Lua, dump it with string.dump, and then pass that string to the function chars to print out its characters individually:
local function foo()
print("foo")
return
end
local s = assert(string.dump(foo))
chars(s)
The string s in its entirety, not printed with my function chars, looks something like this:
uaS?
xV(w#=stdin#A#$#&?&?printfoo_ENV
However, chars only prints the first five bytes:
u
a
S
(Note there are supposed to be two lines of whitespace before the 'u'.)
I am almost certain that this is due to null characters within the string, which I think interferes with lua_tostring's functionality. I have come across lua_Writer for reading chunks, but I have no idea how to use/code it. How can I successfully convert the entire string on the Lua stack to a C string?
I am almost certain that this is due to null characters within the
string
Yes, it's exactly because Lua strings can contain zeroes.
which I think interferes with lua_tostring's functionality.
And this is false. lua_tostring() works as intended. It's just strcat() you're using will only copy the data up to the nearest zero byte.
If you need to copy the string, use memcpy, passing it both the pointer to Lua string data and Lua string length (lua_len, lua_rawlen, etc).
But just for printing you don't even need to copy anything. Pass the len variable as an argument to chars(), and check that length instead of waiting for zero byte.
The Problem isn't lua_tostring but strcat which copies until it finds an null characters. Same Problem with your chars function.
That should work:
memcpy(cp, lua_tostring(L, 1), len);
chars(cp, len);
...
static void chars(char* cp, size_t len) {
for (size_t i = 0; i < len; ++i, ++cp) {
putchar(*cp);
}
}
bool sendMessageToGraphics(char* msg)
{
//char ea[] = "SSS";
char* chRequest = msg; // Client -> Server
DWORD cbBytesWritten, cbRequestBytes;
// Send one message to the pipe.
cbRequestBytes = sizeof(TCHAR) * (lstrlen(chRequest) + 1);
if (*msg - '8' == 0)
{
char new_msg[1024] = { 0 };
string answer = "0" + '\0';
copy(answer.begin(), answer.end(), new_msg);
char *request = new_msg;
WriteFile(hPipe, request, cbRequestBytes, &cbRequestBytes, NULL);
}
BOOL bResult = WriteFile( // Write to the pipe.
hPipe, // Handle of the pipe
chRequest, // Message to be written
cbRequestBytes, // Number of bytes to writ
&cbBytesWritten, // Number of bytes written
NULL); // Not overlapped
if (!bResult/*Failed*/ || cbRequestBytes != cbBytesWritten/*Failed*/)
{
_tprintf(_T("WriteFile failed w/err 0x%08lx\n"), GetLastError());
return false;
}
_tprintf(_T("Sends %ld bytes; Message: \"%s\"\n"),
cbBytesWritten, chRequest);
return true;
}
after the first writefile in running (In case of '8') the other writefile function doesn't work right, can someone understand why ?
the function sendMessageToGraphics need to send move to chess board
There are 2 problems in your code:
First of all, there's a (minor) problem where you initialize a string in your conditional statement. You initialize it as so:
string answer = "0" + '\0';
This does not do what you think it does. It will invoke the operator+ using const char* and char as its argument types. This will perform pointer addition, adding the value of '\0' to where your constant is stored. Since '\0' will be converted to the integer value of 0, it will not add anything to the constant. But your string ends up not having a '\0' terminator. You could solve this by changing the statement to:
string answer = std::string("0") + '\0';
But the real problem lies in the way you use your size variables. You first initialize the size variable to the string length of your input variable (including the terminating '\0' character). Then in your conditional statement you create a new string which you pass to WriteFile, yet you still use the original size. This may cause a buffer overrun, which is undefined behavior. You also set your size variable to however many bytes you wrote to the file. Then later on you use this same value again in the next call. You never actually check this value, so this could cause problems.
The easiest way to change this, is to make sure your sizes are set up correctly. For example, instead of the first call, you could do this:
WriteFile(hPipe, request, answer.size(), &cbBytesWritten, NULL);
Then check the return value WriteFile and the value of cbBytesWritten before you make the next call to WriteFile, that way you know your first call succeeded too.
Also, do not forget to remove your sizeof(TCHAR) part in your size calculation. You are never using TCHAR in your code. Your input is a regular char* and so is the string you use in your conditional. I would also advice replacing WriteFile by WriteFileA to show you are using such characters.
Last of all, make sure your server is actually reading bytes from the handle you write to. If your server does not read from the handle, the WriteFile function will freeze until it can write to the handle again.
#include<iostream>
#include<string.h>
#include<stdio.h>
int main()
{
char left[4];
for(int i=0; i<4; i++)
{
left[i]='0';
}
char str[10];
gets(str);
strcat(left,str);
puts(left);
return 0;
}
for any input it should concatenate 0000 with that string, but on one pc it's showing a diamond sign between "0000" and the input string...!
You append a possible nine (or more, gets have no bounds checking) character string to a three character string (which contains four character and no string terminator). No string termination at all. So when you print using puts it will continue to print until it finds a string termination character, which may be anywhere in memory. This is, in short, a school-book example of buffer overflow, and buffer overflows usually leads to undefined behavior which is what you're seeing.
In C and C++ all C-style strings must be terminated. They are terminated by a special character: '\0' (or plain ASCII zero). You also need to provide enough space for destination string in your strcat call.
Proper, working program:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void)
{
/* Size is 4 + 10 + 1, the last +1 for the string terminator */
char left[15] = "0000";
/* The initialization above sets the four first characters to '0'
* and properly terminates it by adding the (invisible) '\0' terminator
* which is included in the literal string.
*/
/* Space for ten characters, plus terminator */
char str[11];
/* Read string from user, with bounds-checking.
* Also check that something was truly read, as `fgets` returns
* `NULL` on error or other failure to read.
*/
if (fgets(str, sizeof(str), stdin) == NULL)
{
/* There might be an error */
if (ferror(stdin))
printf("Error reading input: %s\n", strerror(errno));
return 1;
}
/* Unfortunately `fgets` may leave the newline in the input string
* so we have to remove it.
* This is done by changing the newline to the string terminator.
*
* First check that the newline really is there though. This is done
* by first making sure there is something in the string (using `strlen`)
* and then to check if the last character is a newline. The use of `-1`
* is because strings like arrays starts their indexing at zero.
*/
if (strlen(str) > 0 && str[strlen(str) - 1] == '\n')
str[strlen(str) - 1] = '\0';
/* Here we know that `left` is currently four characters, and that `str`
* is at most ten characters (not including zero terminaton). Since the
* total length allocated for `left` is 15, we know that there is enough
* space in `left` to have `str` added to it.
*/
strcat(left, str);
/* Print the string */
printf("%s\n", left);
return 0;
}
There are two problems in the code.
First, left is not nul-terminated, so strcat will end up looking beyond the end of the array for the appropriate place to append characters. Put a '\0' at the end of the array.
Second, left is not large enough to hold the result of the call to strcat. There has to be enough room for the resulting string, including the nul terminator. So the size of left should at least 4 + 9, to allow for the three characters (plus nul terminator) that left starts out with, and 9 characters coming from str (assuming that gets hasn't caused an overflow).
Each of these errors results in undefined behavior, which accounts for the different results on different platforms.
I do not know why you are bothering to include <iostream> as you aren't using any C++ features in your code. Your entire program would be much shorter if you had:
#include <iostream>
#include <string>
int main()
{
std::string line;
std::cin >> line;
std::cout << "You entered: " << line;
return 0;
}
Since std::string is going to be null-terminated, there is no reason to force it to be 4-null-terminated.
Problem #1 - not a legal string:
char left[4];
for(int i=0; i<4; i++)
{
left[i]='0';
}
String must end with a zero char, '\0' not '0'.
This causes what you describe.
Problem #2 - fgets. You use it on a small buffer. Very dangerous.
Problem #3 - strcat. Yet again trying to fill a super small buffer which should have already been full with an extra string.
This code looks an invitation to a buffer overflow attack.
In C what we call a string is a null terminated character array.All the functions in the string.h library are based on this null at the end of the character array.Your character array is not null terminated and thus is not a string , So you can not use the string library function strcat here.
I have a small query regarding reading a set of characters from a structure. For example: A particular variable contains a value "3242C976*32" (char - type). How can I get only the first 8 bits of this variable. Kindly help.
Thanks.
Edit:
I'm trying to read in a signal:
For Ex: $ASWEER,2,X:3242C976*32
into this structure:
struct pg
{
char command[7]; // saves as $ASWEER,2,X:3242C976*32
char comma1[1]; // saves as ,2,X:3242C976*32
char groupID[1]; // saves as 2,X:3242C976*32
char comma2[1]; // etc
char handle[2]; // this is the problem, need it to save specifically each part, buts its not
char canID[8];
char checksum[3];
}m_pg;
...
When memcopying buffer into a structure, it works but because there is no carriage returns it saves the rest of the signal in each char variable. So, there is always garbage at the end.
you could..
convert your hex value in canID to float(depending on how you want to display it), e.g.
float value1 = HexToFloat(m_pg.canID); // find a conversion script for HexToFloat
CString val;
val.Format("0.3f",value1);
the garbage values aren't actually being stored in the structure, it only displays it as so, as there is no carriage return, so format the message however you want to and display it using the CString val;
If "3242C976*3F" is a c-string or std::string, you can just do:
char* str = "3242C976*3F";
char first_byte = str[0];
Or with an arbitrary memory block you can do:
SomeStruct memoryBlock;
char firstByte;
memcpy(&firstByte, &memoryBlock, 1);
Both copy the first 8bits or 1 byte from the string or arbitrary memory block just as well.
After the edit (original answer below)
Just copy by parts. In C, something like this should work (could also work in C++ but may not be idiomatic)
strncpy(m_pg.command, value, 7); // m.pg_command[7] = 0; // oops
strncpy(m_pg.comma, value+7, 1); // m.pg_comma[1] = 0; // oops
strncpy(m_pg.groupID, value+8, 1); // m.pg_groupID[1] = 0; // oops
strncpy(m_pg.comma2, value+9, 1); // m.pg_comma2[1] = 0; // oops
// etc
Also, you don't have space for the string terminator in the members of the structure (therefore the oopses above). They are NOT strings. Do not printf them!
Don't read more than 8 characters. In C, something like
char value[9]; /* 8 characters and a 0 terminator */
int ch;
scanf("%8s", value);
/* optionally ignore further input */
while (((ch = getchar()) != '\n') && (ch != EOF)) /* void */;
/* input terminated with ch (either '\n' or EOF) */
I believe the above code also "works" in C++, but it may not be idiomatic in that language
If you have a char pointer, you can just set str[8] = '\0'; Be careful though, because if the buffer is less than 8 (EDIT: 9) bytes, this could cause problems.
(I'm just assuming that the name of the variable that already is holding the string is called str. Substitute the name of your variable.)
It looks to me like you want to split at the comma, and save up to there. This can be done with strtok(), to split the string into tokens based on the comma, or strchr() to find the comma, and strcpy() to copy the string up to the comma.
NoobQuestion:
I heard that filling a char array can be terminated early with the null char. How is this done?
I've searched every single google result out there but still have come up empty handed.
Do you mean something like this:
char test[11] = "helloworld";
std::cout << test << std::endl;
test[2] = 0;
std::cout << test;
This outputs
helloworld
he
?
That's a convention called "null-terminated string". If you have a block of memory which you treat as a char buffer and there's a null character within that buffer then the null-terminated string is whatever is contained starting with the beginning of the buffer and up to and including the null character.
const int bufferLength = 256;
char buffer[bufferLength] = "somestring"; //10 character plus a null character put by the compiler - total 11 characters
here the compiler will place a null character after the "somestring" (it does so even if you don't ask to). So even though the buffer is of length 256 all the functions that work with null-terminated strings (like strlen()) will not read beyond the null character at position 10.
That is the "early termination" - whatever data is in the buffer beyond the null character it is ignored by any code designed to work with null-terminated strings. The last part is important - code could easily ignore the null character and then no "termination" would happen on null character.