I am trying to convert a std::string to a TCHAR* for use in CreateFile(). The code i have compiles, and works, but Visual Studio 2013 comes up with a compiler warning:
warning C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
I understand why i get the warning, as in my code i use std::copy, but I don't want to define D_SCL_SECURE_NO_WARNINGS if at all possible, as they have a point: std::copy is unsafe/unsecure. As a result, I'd like to find a way that doesn't throw this warning.
The code that produces the warning:
std::string filename = fileList->getFullPath(index);
TCHAR *t_filename = new TCHAR[filename.size() + 1];
t_filename[filename.size()] = 0;
std::copy(filename.begin(), filename.end(), t_filename);
audioreader.setFile(t_filename);
audioreader.setfile() calls CreateFile() internally, which is why i need to convert the string.
fileList and audioreader are instances of classes i wrote myself, but I'd rather not change the core implementation of either if at all possible, as it would mean I'd need to change a lot of implementation in other areas of my program, where this conversion only happens in that piece of code. The method I used to convert there was found in a solution i found at http://www.cplusplus.com/forum/general/12245/#msg58523
I've seen something similar in another question (Converting string to tchar in VC++) but i can't quite fathom how to adapt the answer to work with mine as the size of the string isn't constant. All other ways I've seen involve a straight (TCHAR *) cast (or something equally unsafe), which as far as i know about the way TCHAR and other windows string types are defined, is relatively risky as TCHAR could be single byte or multibyte characters depending on UNICODE definition.
Does anyone know a safe, reliable way to convert a std::string to a TCHAR* for use in functions like CreateFile()?
EDIT to address questions in the comments and answers:
Regarding UNICODE being defined or not: The project in VS2013 is a win32 console application, with #undef UNICODE at the top of the .cpp file containing main() - what is the difference between UNICODE and _UNICODE? as i assume the underscore in what Amadeus was asking is significant.
Not directly related to the question but may add perspective: This program is not going to be used outside the UK, so ANSI vs UNICODE does not matter for this. This is part of a personal project to create an audio server and client. As a result you may see some bits referencing network communication. The aim of this program is to get me using Xaudio and winsock. The conversion issue purely deals with the loading of the file on the server-side so it can open it and start reading chunks to transmit. I'm testing with .wav files found in c:/windows/media
Filename encoding: I read the filenames in at runtime by using FindFirstFileA() and FindNextFileA(). The names are retrieved by looking at cFilename in a WIN32_FIND_DATAA structure. They are stored in a vector<string> (wrapped in a unique_ptr if that matters) but that could be changed. I assume this is what Dan Korn means.
More info about the my classes and functions:
The following are spread between AudioReader.h, Audioreader.cpp, FileList.h, FileList.cpp and ClientSession.h. The fragment above is in ClientSession.cpp. Note that in most of my files i declare using namespace std;
shared_ptr<FileList> fileList; //ClientSession.h
AudioReader audioreader; //ClientSession.h
string _storedpath; //FileList.h
unique_ptr<vector<string>> _filenames; //FileList.h
//FileList.cpp
string FileList::getFullPath(int i)
{
string ret = "";
unique_lock<mutex> listLock(listmtx);
if (static_cast<size_t>(i) < _count)
{
ret = _storedpath + _filenames->at(i);
}
else
{
//rather than go out of bounds, return the last element, as returning an error over the network is difficult at present
ret = _storedpath + _filenames->at(_count - 1);
}
return ret;
}
unique_ptr<AudioReader_Impl> audioReaderImpl; //AudioReader.h
//AudioReader.cpp
HRESULT AudioReader::setFile(TCHAR * fileName)
{
return audioReaderImpl->setFile(fileName);
}
HANDLE AudioReader_Impl::fileHandle; //AudioReader.cpp
//AudioReader.cpp
HRESULT AudioReader_Impl::setFile(TCHAR * fileName)
{
fileHandle = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
return HRESULT_FROM_WIN32(GetLastError());
}
if (SetFilePointer(fileHandle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
If you do not need to support the string containing UTF-8 (or another multi-byte encoding) then simply use the ANSI version of Windows API:
handle = CreateFileA( filename.c_str(), .......)
You might need to rejig your code for this as you have the CreateFile buried in a function that expects TCHAR. That's not advised these days; it's a pain to splatter T versions of everything all over your code and it has flow-on effects (such as std::tstring that someone suggested - ugh!)
There hasn't been any need to support dual compilation from the same source code since about 1998. Windows API has to support both versions for backward compatibility but your own code does not have to.
If you do want to support the string containing UTF-8 (and this is a better idea than using UTF-16 everywhere) then you will need to convert it to a UTF-16 string in order to call the Windows API.
The usual way to do this is via the Windows API function MultiByteToWideChar which is a bit awkward to use correctly, but you could wrap it up in a function:
std::wstring make_wstring( std::string const &s );
that invokes MultiByteToWideChar to return a UTF-16 string that you can then pass to WinAPI functions by using its .c_str() function.
See this codereview thread for a possible implementation of such a function (although note discussion in the answers)
The root of your problem is that you are mixing TCHARs and non-TCHARs. Even if you get it to work on your machine, unless you do it precisely right, it will fail when non-ASCII characters are used in the path.
If you can use std::tstring instead of regular string, then you won't have to worry about format conversions or codepage versus Unicode issues.
If not, you can use conversion functions like MultiByteToWideChar but make sure you understand the encoding used in the source string or it will just make things worse.
Try this instead:
std::string filename = fileList->getFullPath(index);
#ifndef UNICODE
audioreader.setFile(filename.c_str());
#else
std::wstring w_filename;
int len = MultiByteToWideChar(CP_ACP, 0, filename.c_str(), filename.length(), NULL, 0);
if (len > 0)
{
w_filename.resize(len);
MultiByteToWideChar(CP_ACP, 0, filename.c_str(), filename.length(), &w_filename[0], len);
}
audioreader.setFile(w_filename.c_str());
#endif
Alternatively:
std::string filename = fileList->getFullPath(index);
#ifndef UNICODE
audioreader.setFile(filename.c_str());
#else
std::wstring_convert<std::codecvt<wchar_t, char, std::mbstate_t>> conv;
std::wstring w_filename = conv.from_bytes(filename);
audioreader.setFile(w_filename.c_str());
#endif
Related
I ran into a problem with the boost filestreams: I need to create and modify files in the user directory under windows. However the username contains an umlaut which makes this fail when compiled under MinGW as the standard is missing the wide_char open() API for filestreams which boost uses. see Read/Write file with unicode file name with plain C++/Boost, UTF-8-compliant IOstreams and https://svn.boost.org/trac10/ticket/9968
However I ran across the line, that this problem mainly occurs when trying to use a character outside the systems codepage. In my case I'm using only characters from the systems codepage as obviously the users directory exists. This makes me think, that this should work, if I could tell boost::path to expect all std::strings as beeing UTF8 but converting them to the system encoding when calling the string() member function (which happens in boost::fstream::open)
So basically: Is there any way to do that conversion (UTF8->system encoding) automatically using boost (and boost Locale)?
To be complete here is my code for setting the locale:
#ifdef _WIN32
// On windows we want to enforce the encoding (mostly UTF8). Also using "" would use the default which uses "wrong" separators
std::locale::global(boost::locale::generator().generate("C"));
#else
// In linux / OSX this suffices
std::locale::global(std::locale::classic());
#endif // _WIN32
// Use also the encoding (mostly UTF8) for bfs paths
bfs::path::imbue(std::locale());
It is a problem on Windows, because Windows uses UTF-16, not UTF-8. I use this function regularly to solve your very problem:
// get_filename_token.cpp
// Turns a UTF-8 filename into something you can pass to fstream::open() on
// Windows. Returns the argument on other systems.
// Copyright 2013 Michael Thomas Greer
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt )
#ifdef _WIN32
#include <string>
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
std::string get_filename_token( const std::string& filename )
{
// Convert the UTF-8 argument path to a Windows-friendly UTF-16 path
wchar_t* widepath = new wchar_t[ filename.length() + 1 ];
MultiByteToWideChar( CP_UTF8, 0, filename.c_str(), -1, widepath, filename.length() + 1 );
// Now get the 8.5 version of the name
DWORD n = GetShortPathNameW( widepath, NULL, 0 );
wchar_t* shortpath = new wchar_t[ n ];
GetShortPathNameW( widepath, shortpath, n );
// Convert the short version back to a C++-friendly char version
n = WideCharToMultiByte( CP_UTF8, 0, shortpath, -1, NULL, 0, NULL, NULL );
char* ansipath = new char[ n ];
WideCharToMultiByte( CP_UTF8, 0, shortpath, -1, ansipath, n, NULL, NULL );
std::string result( ansipath );
delete [] ansipath;
delete [] shortpath;
delete [] widepath;
return result;
}
#else
std::string get_filename_token( const std::string& filename )
{
// For all other systems, just return the argument UTF-8 string.
return filename;
}
#endif
(I have stripped out a few things to post here.)
I found 2 solutions using another library where both have their drawbacks.
Pathie (Docu) It looks like a full replacement of boost::filesystem providing UTF8 aware streams and path handling as well as symlink creation and other file/folder operations. Really cool is the builtin support for getting special directories (temp, HOME, programs folder and many more)
Drawback: Only works as a dynamic library as the static build has bugs. Also might be overkill if you already use boost.
Boost.NoWide (Docu) Provides alternatives to almost all file and stream handlers to support UTF8 on windows and falls back to standard functions on others. The filestreams accept UTF8 encoded values (for the name) and it uses boost itself.
Drawback: No path handling and does not accept bfs::path or wide strings (bfs::path internal format on Windows is UTF16) so a patch would be required, although it is simple. Also requires a build for windows if you want to use std::cout etc with UTF8 strings (yes that works directly!)
Another cool thing: It provides a class to convert the argc/argv to UTF8 on windows.
Short version I am using unicode. I am attempting to use a std::string, to a function that requires a const WCHAR string; DrawString(const WCHAR, ...
I compile with GCC. Everything is unicode, I have specified.
I have been trying to convert a string, into a wchar_t*. The purpose is so that I can output using a GDI+ function, its parameters require it so.
Here is how I have outputted string literals, no problems, debugs fine, works fine.
http://msdn.microsoft.com/en-us/library/ms535991%28v=vs.85%29.aspx for reference why:
// works fine
wchar_t* wcBuff;
wcBuff = (wchar_t*)L"Some text here.\0";
AddString(wcBuff, wcslen(wcBuff), &gFontFamilyInfo, FontStyleBold, 20, ptOrg_Controls, &strFormat_Info);
Now this is what I have been trying, all day, and a side note: my conversion function works fine, it is not an issue, nor creating one.
// problems
string s = "Level " + convert::intToString(6) + "\0";
// try 1 - Segfault
wchar_t* wcBuff = new wchar_t[s.length() + 1];
copy(s.begin(), s.end(), wcBuff);
// random tries, compiles, but access violations (my conversion function here has worked other places, do not know for sure here.
wchar_t* wcBuff;
wstring wstr = convert::stringToWideChar(s);
wstring strvalue = convert::stringToWideChar(s);
wcBuff = (wchar_t*)strvalue.c_str();
wcBuff = (wchar_t*)wstr.c_str();
wstring foo;
foo.assign(s.begin(), s.end());
wcBuff = (wchar_t*)foo.c_str();
Everything compiles, but then presents problems. Some runtime errors as soon as it reaches that point. Others access violations and segfaults. Some compiles and debugs no problem, but the strings output constantly changes with random characters.
Any ideas?
(this is not really an answer, but it's too big for a comment)
Try 1: you didn't null-terminate the string
Try 2: can't comment without seeing the conversion function. Remove the casts.
Try 3: Remove the casts, should be OK.
In all cases use wchar_t const *wcBuff. If "Try 3" fails then it means you have a bug somewhere else in your code, that is showing up here. Try to produce a MCVE. You should be able to get it down to about 10-20 lines.
Even if you manage to write the correct code for what you're intending, this is a fairly naive conversion as it doesn't handle characters outside the 0-127 range properly. You need to think about whether that is what you want, or whether you want to do a UTF-8 conversion, etc.
In Windows you can use MultiByteToWideChar.
#include <string>
int main() {
// Can use convenient wstring
std::wstring wstr = L"My wide string";
// When you need a whar_t* just do this
const wchar_t* s = wstr.c_str();
// unicode form of strcpy
wchar_t buf[100] = {0};
wcscpy (buf,s);
// And if you want to convert from string to wstring
std::string thin = "I only take up one byte per character!";
std::wstring wide(thin.begin(), thin.end());
return 0;
}
I first get my data into a wstring. Like this:
(Converting from string):
std::string sString = "This is my string text";
std::wstring str1(sString.begin(), sString.end());
(Converting from int):
wstring str1 = std::to_wstring(BirthDate);
Then, I use it in GDI+ Command like this:
graphics.DrawString(str1.c_str(), -1,
&font, PointF(10, 5), &st);
First thing first. GDI+ is a C++ library. It uses Microsoft C++ ABI. Microsoft C++ ABI is wildly incompatible with gcc so you might just forget about using it. You can try to use WinAPI or any other library that uses C calling conventions.
Now for the wstring question. wchar_t is 32 bits wide in gcc, but Windows APIs require it to be 16 bits wide. You cannot use any native Windows call that requires wchar_t.
You can use -fshort-wchar command line option in gcc, that would make wchar_t 16 bits wide and you will regain compatibility with Windows APIs, but lose compatibility with libc, so no library functions that act on wchar_t for you. std::wstring will probably work as it's header-only, but wprintf or wscpy or all other compiled stuff won't.
None of this is detected at compile time, as the only things gcc sees are header files. It cannot tell whether corresponding libraries are compiled with 16-bit wchar_t or 32-bit wchar_t.
You can use uint16_t when you need to pass an array of wchar_t to a Windows function. If you can use C++11, it has char16_t that you can use too. Here's an example that should work with Basic Multilingual Plane characters:
std::wstring myLittleNiceWstring;
...
std::vector<uint16_t> myUglyCompatibilityString;
std::copy(myLittleNiceWstring.begin(),
myLittleNiceWstring.end(),
std::back_inserter(myUglyCompatibilityString));
myUglyCompatibilityString.push_back(0);
UglyWindowsAPI(static_cast<WCHAR*>(myUglyCompatibilityString.data());
If you have non-BMP characters, you need to convert UTF32 to UTF16 rather than just copy characters with std::copy. You can use libiconv for that or write a conversion routine yourself (it's rather simple) or just boorow some code from the internet.
It is my opinion that Windows-centric development with GCC is rather difficult because of this and other issues. You can use gcc as long as you stick to POSIX-ish APIs.
I'm a Javascript developer, so go easy on me! I am trying to write just a patch of C++ to enable printing on a framework. I'm compiling with Unicode, and based on my research, that is what is messing me up.
I think this is a relatively simple thing that I'm over complicating. The application has a std::string that contains the current printer name. The script first checks if it is unset (if it is it utilizes GetDefaultPrinter which outputs a LPTSTR). Finally, the script takes either than std::string or the LPTSTR and converts it to a LPCTSTR for CreateDC.
Here is my code:
std::string PrinterName = window->getPrinter();
LPDWORD lPrinterNameLength;
LPWSTR szPrinterName;
LPCTSTR PrinterHandle;
if (PrinterName == "unset") {
GetDefaultPrinter( szPrinterName, &lPrinterNameLength );
PrinterHandle = szPrinterName; //Note sure the best way to convert here
} else {
PrinterHandle = PrinterName.c_str();
}
HDC hdc = CreateDC( L"WINSPOOL\0", PrinterHandle, NULL, NULL);
When compiling, I only get conversions errors. Such as
Cannot convert parameter 2 from LPDWORD * to LPDWORD (GetDefaultPrinter)
and
Cannot convert from 'const char *' to 'LPCTSTR' (On the PrinterHandle = PrinterName.c_str() line)
I've done quite a bit of SO research on this, but haven't come up with a concrete solution.
Any help is greatly appreciated!
Even if you're compiled for "Unicode" (wide characters strings), you can call the "ANSI" (narrow characters strings) versions of the API functions. Windows will do the conversions for you and call the wide character version under the covers.
For example, for most Windows APIs like CreateDC, there isn't actually a function with that name. Instead, there's a macro named CreateDC that expands to either CreateDCA or CreateDCW, which are the actual function names. When you're compiled for "Unicode", the macros expand to the -W versions (which are the native ones in all modern versions of the OS. Nothing prevents you from explicitly calling either version, regardless of whether you're compiled for Unicode. In most cases, the -A version will simply convert the narrow strings to wide ones for you and then call the corresponding -W version. (There are some caveats here related to creating windows, but I don't think they apply to DCs.)
std::string PrinterName = window->getPrinter();
if (PrinterName == "unset") {
char szPrinterName[MAX_PATH]; // simplified for illustration
DWORD cchPrinterNameLength = ARRAYSIZE(szPrinterName);
GetDefaultPrinterA(szPrinterName, &cchPrinterNameLength);
PrinterName = szPrinterName;
}
HDC hdc = CreateDCA("WINSPOOL", PrinterName.c_str(), NULL, NULL);
First of all, as mentioned in the comments, the proper way is to make a DWORD and pass the address:
DWORD lpPrinterNameLength;
...
GetDefaultPrinter(..., &lpPrinterNameLength);
Why it's like that is so that it can use and change a number:
On input, specifies the size, in characters, of the pszBuffer buffer. On output, receives the size, in characters, of the printer name string, including the terminating null character.
It would just take a DWORD, but the function changes the number in the variable passed in, so the function needs the address of the variable to change in order for those changes to reflect back to the caller.
Secondly, since window->getPrinter() returns a narrow string and you're using UNICODE, which makes the functions take wide strings, you should convert from the narrow string into a wide one. There are several ways to do this (such as the really easy one mentioned in ildjarn's comment), and even this one is slightly better with C++11, though the aforementioned note applies even better with that, but I'll use MultiByteToWideChar and C++03:
std::wstring narrowToWide(const std::string &narrow) {
std::vector<wchar_t> wide;
int length = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, narrow.c_str(), -1, NULL, 0);
if (!length) {
//error
}
wide.resize(length);
if (!MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, narrow.c_str(), -1, &wide[0], length)) {
//error, should probably check that the number of characters written is consistent as well
}
return std::wstring(wide.begin(), wide.end());
}
...
std::wstring PrinterName = narrowToWide(window->getPrinter());
//rest is same, but should be L"unset"
CreateDC( L"WINSPOOL\0", PrinterHandle, NULL, NULL);
I'm following this guide from MSDN on how to list the files in a directory (i'm using the current directory). In my case I need to put the information in the message part of my packet (char array of size 1016) to send it to the client. When I print packet.message on both the client and server only the first character of the filenames are shown. What's wrong? Here's a snippet of the relevant section of code:
WIN32_FIND_DATA f;
HANDLE h = FindFirstFile(TEXT("./*.*"), &f);
string file;
int size_needed;
do
{
sprintf(packet.message,"%s", &f.cFileName);
//Send packet
} while(FindNextFile(h, &f));
This is commonly caused by a wide character string being mistakenly treated as an ASCII string. The build is targeting UNICODE and cFileName contains a wide character string, but sprintf() is assuming it is an ASCII string.
FindFirstFile() will be mapped to either FindFirstFileA() or FindFirstFileW() depending if the build is or is not targeting UNICODE.
A solution would be to use FindFirstFileA() and ASCII strings explicitly.
Note that the & is unrequired in the sprintf():
sprintf(packet.message, "%s", f.cFileName);
As the application is consuming strings that are outside of its control (i.e file names) I would recommend using the safer _snprintf() to avoid buffer overruns:
/* From your comment on the question 'packet.message' is a 'char[1016]'
so 'sizeof()' will function correctly. */
if (_snprintf(packet.message, sizeof(packet.message), "%s", f.cFileName) > 0)
{
}
You're using the Unicode version of FindFirstFile, almost guaranteed, Either invoke the narrow version or change the format specifier of your print. Personally I would do the former:
WIN32_FIND_DATAA f;
HANDLE h = FindFirstFileA("./*.*", &f);
string file;
int size_needed;
do
{
sprintf(packet.message,"%s", f.cFileName);
//Send packet
} while(FindNextFileA(h, &f));
FindClose(h);
Alternatively, you can compile with MBCS or regular characters.
As others have mentioned, you are calling the Unicode version of FindFirstFile() and are passing Unicode data to the Ansi sprintf() function. The %s specifier expects Ansi input. You have a few choices to address the issue in your code:
continue using sprintf(), but change the %s specifier to %ls so it will accept Unicode input and convert it to Ansi when writing to your message buffer:
sprintf(packet.message, "%ls", f.cFileName);
This is not ideal, though, because it will use the Ansi encoding of the local machine, which may be different than the Ansi encoding used by the receiving machine.
change your message buffer to use TCHAR instead of char, and then switch to either wsprintf() or _stprintf() instead of sprintf(). Like FindFirstFile(), they will match whatever character format that TCHAR and TEXT() use:
TCHAR message[1016];
wsprintf(packet.message, TEXT("%s"), f.cFileName);
Or:
#include <tchar.h>
_TCHAR message[1016];
_stprintf(packet.message, _T("%s"), f.cFileName);
if you must use a char buffer, then you should accept Unicode data from the API and convert it to UTF-8 for transmission, and then the receiver can convert it back to Unicode and use it as needed.
WIN32_FIND_DATAW f;
HANDLE h = FindFirstFileW(L"./*.*", &f);
if (h)
{
do
{
WideCharToMultiByte(CP_UTF8, 0, f.cFileName, lstrlenW(f.cFileName), packet.message, sizeof(packet.message), NULL, NULL);
//Send packet
} while(FindNextFile(h, &f));
FindClose(h);
}
I have an EditBox HWND tbLog, and the following function (which doesn't work):
void appendLogText(char* newText)
{
int len = GetWindowTextLength(tbLog);
char* logText = new char[len + strlen(newText) + 1];
GetWindowText(tbLog, logText, len);
strcat(logText, newText);
SendMessage(tbLog, WM_SETTEXT, NULL, (LPARAM)TEXT(logText));
delete[] logText;
}
and I call it like so:
appendLogText("Put something in the Edit box.\r\n");
appendLogText("Put something else in the Edit box.\r\n");
First of all, what does TEXT() actually do? I have tried with/without it: (LPARAM)logText and (LPARAM)TEXT(logText) and there is no difference as far as I can see.
Second, what am I doing wrong in my append function? If I comment out my delete line, then the first time I run the append function, I get garbage coming up in my Editbox, followed by the message. The second time I run it, the program crashes. If I don't have it commented out, then it crashes even the first time.
I would rewrite the function, in C, like this:
void appendLogText(LPCTSTR newText)
{
DWORD l,r;
SendMessage(tbLog, EM_GETSEL,(WPARAM)&l,(LPARAM)&r);
SendMessage(tbLog, EM_SETSEL, -1, -1);
SendMessage(tbLog, EM_REPLACESEL, 0, (LPARAM)newText);
SendMessage(tbLog, EM_SETSEL,l,r);
}
Its important to save and restore the existing selection or the control becomes very annoying for anyone to use who wants to select and copy some text out of the control.
Also, the use of LPCTSTR ensures that the function can be called when you build with either a multibyte or unicode character set.
The TEXT macro was out of place, and should be used to wrap string literals:
LPCTSTR someString = TEXT("string literal");
Windows NT Operating systems are natively unicode so building multibyte applications is inefficient. Using TEXT() on string literals, and LPTSTR in place of 'char*' helps with this conversion to unicode. But really it would probably be most efficient to just switch explicitly to unicode programming on windows: in place of char, strlen, std::string, use wchar_t, std::wstring, wsclen, and L"string literals".
Switching your projects build settings to Unicode will make all the windows API functions expect unicode strings.
It has been brought to my very belated attention that passing -1 as the WPARAM of EM_SETSEL merely unselects any selection but does not move the insertion point. So the answer should be ammended to (also untested):
void appendLogText(HWND hWndLog, LPCTSTR newText)
{
int left,right;
int len = GetWindowTextLength(hWndLog);
SendMessage(hWndLog, EM_GETSEL,(WPARAM)&left,(LPARAM)&right);
SendMessage(hWndLog, EM_SETSEL, len, len);
SendMessage(hWndLog, EM_REPLACESEL, 0, (LPARAM)newText);
SendMessage(hWndLog, EM_SETSEL,left,right);
}
Tried SetWindowText() ?
http://msdn.microsoft.com/en-us/library/ms633546(v=vs.85).aspx