I know this kind of question has already been asked. I also used the solution of this topic to my tests. However, I want to know how using this kind of function without memory leak neither exception.
Note:
LPTSTR ~ char* and
LPCTSTR ~ const char*
void add_to_buffer(LPTSTR* buffer, LPCTSTR msg) {
// Determine new size
int newSize = 0;
// Allocate new buffer
if (*buffer == NULL)
newSize = _tcsclen(msg) + 1; // strlen()
else
newSize = _tcslen(*buffer) + _tcsclen(msg) + 1;
LPTSTR newBuffer = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newSize); // malloc()
// Do the copy and concat
if (*buffer == NULL)
_tcscpy(newBuffer, msg); // strcpy()
else
{
_tcscpy(newBuffer, *buffer);
_tcscat(newBuffer, msg); // strcat()
// release old buffer
HeapFree(GetProcessHeap(), 0, *buffer); // free()
}
// store new pointer
*buffer = newBuffer;
}
Tests:
LPTSTR test = NULL;
add_to_buffer(&test, _T("User:\r\n"));
add_to_buffer(&test, _T("42"));
First call to add_to_buffer works. However, the second function call causes an exception at HeapFree. I'm sure this is a problem about pointers, but I do not understand how to fix it.
Is it a good method? How to fix my exception?
If you are compiling the code as multi-byte application this line
LPTSTR newBuffer = (LPTSTR)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
newSize
);
might allocate to few memory.
To fix this use:
LPTSTR newBuffer = (LPTSTR)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
newSize * sizeof(*newBuffer)
);
Besides this and the fact that the code lacks error checking for system calls the code looks fine.
However, to simplify things one can use HeapReAlloc() instead of the combination of HeapAlloc() and HeapFree().
If the program crashes anyway this might due to the memory management already being mashed up before this actual crash you observe.
If your program is unicode-enabled, you're not allocating enough memory - because string length (in symbols) and string size (in bytes) don't match.
If it isn't, I don't see reason of using non-standard-C types over standard ones. It shouldn't be a problem though.
Related
Can anyone help me understand why my code is failing on delete[] szPassword in the first block of code? I know that szPassword is only copying "a", and st2 is equal to 8:
TCHAR *szPassword = new TCHAR[2]();
StringCchCopy(szPassword, 2, L"ab");
SIZE_T st2 = sizeof(szPassword);
SecureZeroMemory(szPassword, st2);
delete[] szPassword;
However, when this is run, without getting the sizeof() value, it works fine:
TCHAR *szPassword = new TCHAR[2]();
StringCchCopy(szPassword, 2, L"ab");
SecureZeroMemory(szPassword, 2);
delete[] szPassword;
szPassword is a pointer, not an array and therefore sizeof(szPassword) will be 4 or 8. In a 64-bit application this is too much, you will attempt to write 8 bytes to a 4 byte buffer.
The C++ run-time is allowed to allocate more than you ask it to and it often does this so it can add special data to the end of the buffer so it can detect buffer overruns.
Do something like this instead:
const UINT charcount = 2;
TCHAR *szPassword = new TCHAR[charcount];
...
SecureZeroMemory(szPassword, charcount * sizeof(TCHAR));
delete[] szPassword;
If the buffer is always small-ish you can just use an array on the stack:
TCHAR szPassword[200];
...
SecureZeroMemory(szPassword, sizeof(szPassword));
I am trying to use GetComputerName() to retrieve the box's name. The code works great on windows 7, however, when i test on windows xp the code does not work (result is UNKNOWN). Any idea why?
int GetBoxName(BoxInfo &box_info)
{
int Ret;
DWORD dwLen;
const char* szUnk = "UNKNOWN\0";
// Get the size of the box name then allocate memory.
Ret = GetComputerName(NULL, &dwLen);
box_info.BoxName = new char[dwLen];
// Attempt to retrieve the box name.
if((Ret = GetComputerName(box_info.BoxName, &dwLen) == 0))
{
delete[] box_info.BoxName;
box_info.BoxName = new char[strlen(szUnk)];
box_info.BoxName = (char*)szUnk;
return 1;
}
return 0;
}
#Ben has given a good account of errors that you made. I'd like to show you how you would typically call GetComputerName. You are making it harder than it needs to be. The key information is the excerpt from the documentation:
The buffer size should be large enough to contain MAX_COMPUTERNAME_LENGTH + 1 characters.
You have a hard upper bound on the size of the buffer. You can therefore use a fixed size buffer, and only make a single call to GetComputerName.
std::string getComputerName()
{
char buffer[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(buffer, &len))
return std::string(buffer, len);
return "UNKNOWN";
}
Were you compiling for Unicode it would be:
std::wstring getComputerName()
{
wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(buffer, &len))
return std::wstring(buffer, len);
return L"UNKNOWN";
}
If you want to cater for the possibility of the computer name being longer than MAX_COMPUTERNAME_LENGTH then you can write it like this:
std::string getComputerName()
{
char buffer[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(buffer, &len))
{
return std::string(buffer, len);
}
if (GetLastError() == ERROR_BUFFER_OVERFLOW)
{
std::vector<char> name(len);
if (GetComputerName(&name[0], &len))
{
return std::string(&name[0], len);
}
}
return "UNKNOWN";
}
I don't know whether or not this can even happen. The docs hint that it can, although if it does happen then it renders MAX_COMPUTERNAME_LENGTH somewhat of a misnomer. If we pass a value of len that is less than MAX_COMPUTERNAME_LENGTH+1 then the function succeeds if the name fits. It does not automatically fail with ERROR_BUFFER_OVERFLOW. Of course, if the name returned by this function can never exceed MAX_COMPUTERNAME_LENGTH then the second version is rather paranoid.
FWIW, the code in your updated answer is still badly broken. You simply must not pass NULL for the first parameter to GetComputerName. The documentation could not be much clearer.
This makes no sense at all:
box_info.BoxName = new char[strlen(szUnk)];
box_info.BoxName = (char*)szUnk;
You allocate memory, then immediately lose track of it. And you are directing a non-const pointer to a string literal. And the amount of memory allocated here doesn't include space for the terminating NUL byte, so you would overrun the buffer.
Perhaps you wanted the second line to be
strcpy(box_info.BoxName, szUnk);
And why aren't you using a smart pointer to automatically deallocate the memory when needed, for example std::string or std::unique_ptr<char[]> ?
Finally, the documentation says
The buffer size should be large enough to contain MAX_COMPUTERNAME_LENGTH + 1 characters.
That is a pretty plain requirement. Makes you wonder the point of
If the buffer is too small, the function fails and GetLastError returns ERROR_BUFFER_OVERFLOW. The lpnSize parameter specifies the size of the buffer required, including the terminating null character.
This latter behavior appears to only be true for the Unicode version. Stanly Roark left a comment on the MSDN page that:
The ANSI version does not return the required length
I have to maintain an old application that uses ANSI.
I have noticed that, while the Unicode version returns the required buffer length, the ANSI version does not.
According to the Visual C++ runtime there is a heap corruption when calling free in the destructor. But I don't understand why there is a heap corruption, can anyone explain why? The precise error is:
CRT detected that the application wrote to memory after end of heap buffer.
Also, if I ignore the error the program doesn't crash, it keeps running and when I press a key it returns 0.
The class only contains a constructor and destructor and the private vars FILE* target and char* raw_data.
foo::foo (wchar_t* path)
{
size_t size;
target = _wfopen (path, L"rb+");
if (!target) {
char* error = strerror (errno);
printf ("The file could not be opened: %s\n", error);
_exit (1);
}
fseek (target, 0L, SEEK_END);
size = ftell (target);
fseek (target, 0, SEEK_SET);
raw_data = (char*) malloc (size);
size = fread (raw_data, 1, size, target);
raw_data[size] = '\0';
}
foo::~foo ()
{
fclose (target);
free (raw_data);
}
int main ()
{
nbt* klas = new nbt (L"C:\\Users\\Ruben\\level");
puts ("Success?!");
delete klas;
getchar ();
return 0;
}
When writing the NUL terminator as you do:
raw_data[size] = '\0';
... you are using one byte more than the bytes you allocated. There may be other errors but there is definitely an error on this line -- writing to memory you have not allocated is "undefined behaviour" and could explain the crash you're observing.
One sure problem is this code:
raw_data = (char*) malloc (size);
size = fread (raw_data, 1, size, target);
raw_data[size] = '\0';
You cannot access raw_data[size], because it is beyond the allocated size. Indexed access in C/C++ is zero based. As a result, the last element of raw_data that can be accessed with your existing code is raw_data[size-1]. To be able to set the byte which is at offset size to zero you need to change your malloc to:
raw_data = (char*) malloc (size+1);
Since this is a C++ application, you may want to use streams and new/delete instead of FILE pointers and malloc/free.
I've been trying to get this to work for like ages but with no avail (sad face).
int iChars = GetWindowTextLength (GetDlgItem(handle,ID))+1; // Room for '\0'
char* pstrText;
pstrText = (char*) malloc (sizeof(char)*iChars);
if (pstrText != NULL) {
//GetWindowText (GetDlgItem(handle,ID), pstrText, iChars);
GetDlgItemText(handle,ID,pstrText,iChars);
}
return pstrText; // Memory gets freed after it returns
Working example:
char* MWC::System::TextBox::GetText(){
int len = SendMessage(handle, WM_GETTEXTLENGTH, 0, 0);
char* buffer = new char[len];
SendMessage(handle, WM_GETTEXT, (WPARAM)len+1, (LPARAM)buffer);
return buffer;
}
The wParam parameter is wrong here:
SendMessage(handle, WM_GETTEXT, (WPARAM)len, (LPARAM)buffer);
You should pass len+1 because of the zero-terminator.
You are freeing the memory before returning!!!
if ((pstrText != NULL) {
GetDlgItemText(handle,ID,pstrText,sizeof(pstrText));
free (pstrText); // Freeing memory Here!
}
You must provide a way for the client to free when its no longer needed...
Hope this helps!
You already free the memory pointed to by pstrText before you return. You should return a string object that can actually contain the text and frees it automatically on release. Or you'll have to ask the caller to allocate memory for the string, but then you are just wrapping the API.
I have a big problem with std::wstring memory allocation. The program crash when I try to use this code:
size_t size;
mbstowcs_s(&size, NULL, 0, buffer, _TRUNCATE);
wchar_t *buffer2 = (wchar_t*)malloc(size + 1);
mbstowcs_s(&size, buffer, buffer_size, buffer, _TRUNCATE);
buffer2[size] = '\0';
std::wstring data(buffer);
the crash is on the last line and doesn't happen if I use the following line:
std::wstring data(L"hello");
the error is memory heap allocation failure and the result is the crash of the program. Why? What's wrong?
wchar_t *buffer2 = (wchar_t*)malloc((size + 1) * sizeof(wchar_t));
^^^^^^^^^^^^^^^^^
malloc allocates a number of bytes - you wan't a number of wchar_t's
If you're using c++, the correct way is:
wchar_t *buffer2 = new wchar_t[size+1];
If you use std::wstring I assume you are using C++, dont use malloc, use new & delete (just a side note)
std::vector seems to be a good way to make a buffer here. Its constructor receives elements number (not bytes) and you don't have to remember to delete the memory.