C++ std::string conversion problem on Windows - c++

This is my procedure:
bool Open(std::string filename)
{
...
HANDLE hFile = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
...
}
Error:'CreateFileW' : cannot convert parameter 1 from 'const char *' to 'LPCWSTR'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
Where is the problem?

A std::string consists of an array of char's, and so the c_str function returns a const char*.
A LPCWSTR is a Long Pointer to a Constant Wide String, or in other words, const wchar_t*.
So you have a couple of options. Either get the filename as a wide string (std::wstring), or specify that you want the non-wide version of CreateFile instead. That can be done either by calling CreateFileA or disabling UNICODE in your project settings.
CreateFile is a macro which either resolves to CreateFileA (the char version) or CreateFileW (the wide char version) depending on whether or not unicode is enabled.

You have specified std::string, whose character type is char. And the code you're using CreateFile() in must be being compiled under the definition of the pre-processor symbol UNICODE, since that selects the actual underlying function CreateFileW().
Either get rid of the UNICODE definition, or explicitly use CreateFileA().

It looks like you are compiling with Unicode support turned on. You may want to turn it off, or if not use std::wstring instead of std::string.
As others have suggested, you could call CreateFileA directly, but I'd strongly suggest you not do this - you will end up with an unmaintanable collection of Unicode and non-Unicode function calls.

The problem is you are trying to pass char* to a function requiring wchar_t*
You could write a function to convert string into wstring:
wstring Widen( const string& str );
Then you could call CreateFile like this:
HANDLE hFile = CreateFile( Widen(filename).c_str(), etc. );
Another technique I've seen used is to conditionally define tstring to be either string or wstring depending on the Unicode setting, and use tstring everywhere in your code.
bool Open(tstring filename)
{
...
HANDLE hFile = CreateFile( filename.c_str(), etc. );
...
}
It's a bit of a thorny issue I'm afraid and the best solution for you is something only you can decide. However, I'd agree with Neil and steer clear of directly calling CreateFileA as that will leave you in a mess eventually.

Related

How to concatenate a variable between strings in an LPSTR?

I do have this function defined in windows playsoundapi.h.
PlaySound(L"D:\\resources\\English\\A.wav", NULL, SND_LOOP);
I want to concatenate a variable to replace "A.wav" in c++.
The variable is of type char*
Can anyone suggest a solution to this please? Much appreciated.
In C++17 or above use std::filesystem::path which is more handy for such scenario:
using std::filesystem::path;
path file = ...; // L"A.wav" // here can be wide characters things and regular character things - proper conversion is done implicitly
path base{L"D:\\resources\\English"};
PlaySound((base / file).c_str(), NULL, SND_LOOP);
Note that std::filesystem::path::c_str() returns const wchar_t* on Windows and const char * on other platforms.
Return value
The native string representation of the pathname, using native syntax, native character type, and native character encoding. This string is suitable for use with OS APIs.
Simple enough
std::wstring var = ...;
PlaySound((L"D:\\resources\\English\\" + var).c_str(), NULL, SND_LOOP);
But if your variable is something other than a std::wstring, then that is a different question. Please add more details if that is the case.
EDIT
It seems the variable is type char*. One possible solution is to make a std::wstring variable from the char* variable
char* var = ...;
std::wstring tmp(var, var + strlen(var));
PlaySound((L"D:\\resources\\English\\" + tmp).c_str(), NULL, SND_LOOP);
This does assume that there are no encoding issues in copying from char to wchar_t but again that's a detail not provided in the question.
Also you should consider why the variable is char* in the first place. You are working with an API that requires wide characters, so why not use wide characters in your code?
Assign your char* string to a std::string, which you can then concatenate with your base path, and then use the std::string::c_str() method to get a const char* pointer that you can pass to PlaySound(), eg:
std::string fileName = "A.wav";
PlaySoundA(("D:\\resources\\English\\" + fileName).c_str(), NULL, SND_LOOP);

Conversion (const char*) var goes wrong

I need to convert from CString to double in Embedded Visual C++, which supports only old style C++. I am using the following code
CString str = "4.5";
double var = atof( (const char*) (LPCTSTR) str )
and resutlt is var=4.0, so I am loosing decimal digits.
I have made another test
LPCTSTR str = "4.5";
const char* var = (const char*) str
and result again var=4.0
Can anyone help me to get a correct result?
The issue here is, that you are lying to the compiler, and the compiler trusts you. Using Embedded Visual C++ I'm going to assume, that you are targeting Windows CE. Windows CE exposes a Unicode API surface only, so your project is very likely set to use Unicode (UTF-16 LE encoding).
In that case, CString expands to CStringW, which stores code units as wchar_t. When doing (const char*) (LPCTSTR) str you are then casting from a wchar_t const* to a char const*. Given the input, the first byte has the value 52 (the ASCII encoding for the character 4). The second byte has the value 0. That is interpreted as the terminator of the C-style string. In other words, you are passing the string "4" to your call to atof. Naturally, you'll get the value 4.0 as the result.
To fix the code, use something like the following:
CStringW str = L"4.5";
double var = _wtof( str.GetString() );
_wtof is a Microsoft-specific extension to its CRT.
Note two things in particular:
The code uses a CString variant with explicit character encoding (CStringW). Always be explicit about your string types. This helps read your code and catch bugs before they happen (although all those C-style casts in the original code defeats that entirely).
The code calls the CString::GetString member to retrieve a pointer to the immutable buffer. This, too, makes the code easier to read, by not using what looks to be a C-style cast (but is an operator instead).
Also consider defining the _CSTRING_DISABLE_NARROW_WIDE_CONVERSION macro to prevent inadvertent character set conversions from happening (e.g. CString str = "4.5";). This, too, helps you catch bugs early (unless you defeat that with C-style casts as well).
CString is not const char* To convert a TCHAR CString to ASCII, use the CT2A macro - this will also allow you to convert the string to UTF8 (or any other Windows code page):
// Convert using the local code page
CString str(_T("Hello, world!"));
CT2A ascii(str);
TRACE(_T("ASCII: %S\n"), ascii.m_psz);
// Convert to UTF8
CString str(_T("Some Unicode goodness"));
CT2A ascii(str, CP_UTF8);
TRACE(_T("UTF8: %S\n"), ascii.m_psz);
Found a solution using scanf
CString str="4.5"
double var=0.0;
_stscanf( str, _T("%lf"), &var );
This gives a correct result var=4.5
Thanks everyone for comments and help.

Am I converting properly from "const char *" to "TCHAR*"?

I'm trying to pass a process name as a TCHAR to the following void:
void GetBaseAddressByName(DWORD pID, TCHAR *pN)
By doing it like this:
GetBaseAddressByName(aProcs[i], (TCHAR*)"Process.exe");
So my question is: is what I am doing correct? Because I have tried both TEXT("Process.exe") and _T("Process.exe") with my project's Character Set both on Multi-Bite and Unicode and it just tells me that
argument of type "const char*" is incompatible with parameter of type "TCHAR*"
The short answer is no. TCHAR maps to either char or wchar_t depending on your project's Unicode/Multi-byte setting. So, in general, a cast like that is either unnecessary or incorrect. The correct way, as you said, is to use either the TEXT or _T macro. The reason you're getting an error is that you're trying to pass a const character string to a function that expects a mutable character string. The safeset way to get around the error is to copy your constant string into a local TCHAR buffer and then pass that to GetBaseAddressByName.
It is better to have a TCHAR array first, then copy into it.
#include "atlstr.h"
char const * procName = "processName.exe";
TCHAR szName [128];
_tcscpy(szName, A2T(procName));
GetBaseAddressByName(aProcs[i], szName);
As suggested by #Remy Lebeau in the comments, procName can be defined as TCHAR const * procName = TEXT("processName.exe");.
(TCHAR*)"Process.exe" is not a valid type-cast. It will "work" when the project charset is set to ANSI/MBCS, but it will produce garbage if the charset is set to Unicode.
Using TEXT("Process.exe") is the correct way to make a string literal use TCHAR characters.
GetBaseAddressByName(aProcs[i], TEXT("Process.exe"));
However, you need to change your pN parameter to const TCHAR * (or LPCTSTR) instead:
void GetBaseAddressByName(DWORD pID, const TCHAR *pN);
void GetBaseAddressByName(DWORD pID, LPCTSTR pN);
A string literal is const data, and you cannot pass a pointer-to-const-data where a pointer-to-non-const-data is expected (without casting the const away with const_cast). That is why you were still getting errors when trying to use the TEXT()/_T() macros.
You need a L, like L"Process.exe". Unicode strings are specified with L"".
That said, there is no reason to use TCHAR. Use unicode all the time, if doing Windows work only.

Convert char[] array to LPTSTR in vc++

I want to use the Win API CreateProcess for which accepts 2nd parameter as "LPTSTR".
But I've the path to my exe in a char array. My VS2013 project (static library) is Unicode encoding type.
Code snippert below.
IN this line
"appPath = (LPTSTR)TestEXEPath;"
of the below code snippet where the type cast is happening, I see that the characters in "appPath" gets converted to some junk characters whereas the RHS of this expression "TestEXEPath" does have valid characters.
However tehre is no compilation error here. It is at run time this characters get corrupted in "appPath".
I know this typecast is creating this problem. But how do I solve this, how do I type cast this char array to LPTSTR typr which is needed by "CreateProcess() API.
Or is there any better way of doing this so as to avoid the char array itself.
LPTSTR appPath;
char cwd[_MAX_PATH];
getcwd(cwd, _MAX_PATH);
char TestEXEPath[_MAX_PATH];
strcpy(TestEXEPath, cwd);
strcat(TestEXEPath, "\\pwrtest.exe /sleep /c:1");
appPath = (LPTSTR)TestEXEPath; // The characters in this gets converted to some junk characters.
.......................
......................
CreateProcess(NULL, appPath, NULL, NULL, FALSE, 0, NULL, workingDir, &sI, &pI))
You are compiling for Unicode, so LPTSTR expands to wchar_t*. But you have ANSI data, char*. In that case it is simplest to call CreateProcessA and pass the ANSI data.
BOOL retval = CreateProcessA(..., TestExePath, ...));
If you want to avoid using ANSI functions then you can stick to wchar_t arrays.
whar_t exepath[MAX_PATH + 100]; // enough room for cwd and the rest of command line
GetCurrentDirectory(MAX_PATH, exepath);
wcscat(exepath, L"\\pwrtest.exe /sleep /c:1");
BOOL retval = CreateProcess(..., exepath, ...);
Note that I switched from getcwd to GetCurrentDirectory in order to get a wide char version of the working directory.
Note also that your code should check for errors. I neglected to do that here due to laze. But in your real code, you should not be as lazy as I have been.
The fact that you had to cast should have set off warning signals for you. Well, judging from the question, it probably did. When you write:
appPath = (LPTSTR)TestEXEPath;
That simply tells the compiler to treat TestEXEPath as LPTSTR whether or not it really is. And the fact that the program won't compile without the cast tells you that TestEXEPath is not LPTSTR. The cast does not change that reality, it merely shuts the compiler up. Always a bad move.
With unicode, LPTSTR points to an array of wchar_t and not an array of char.
Here some additional explantions.
Try with:
TCHAR TestExePath[_MAX_PATH];
And use wcscat() and wcscpy() and the other wide c-string handling functions in <cwchar>.
Also take a look at the very convenient ATL conversion classes here: http://msdn.microsoft.com/en-us/library/87zae4a3.aspx
LPTSTR str = CA2T(TestEXEPath);
Or even easier just
CreateProcess(NULL, CA2T(TestEXEPath), NULL, NULL, FALSE, 0, NULL, workingDir, &sI, &pI))
No destruction is needed.

How to concatenate a LPCWSTR?

How would I take...
string modelPath = "blah/blah.obj"
and concatenate it with...
L" not found."
While passing it in as LPCWSTR. I tried to do
(LPCWSTR)(modelPath + " was not found.").c_str()
However that did not work. Here is a larger example of what it looks like now.
if(!fin)
{
MessageBox(0, L"Models/WheelFinal.txt not found.", 0, 0); //
return;
}
LPCWSTR is a L ong P ointer to a C onstant W ide STR ing. Wide strings, at least in Win32, are 16 bits, whereas (const) char strings (i.e. (C)STR or their pointer-counterparts LP(C)STR) are 8 bits.
Think of them on Win32 as typedef const char* LPCSTR and typedef const wchar_t* LPCWSTR.
std::string is an 8-bit string (using the underlying type char by default) whereas std::wstring is a wider character string (i.e. 16-bits on win32, using wchar_t by default).
If you can, use std::wstring to concatenate a L"string" as a drop-in replacement.
A note on MessageBox()
Windows has a funny habit of defining macros for API calls that switch out underlying calls given the program's multibyte configuration. For almost every API call that uses strings, there is a FunctionA and FunctionW call that takes an LPCSTR or LPWCSTR respectively.
MessageBox is one of them. In Visual Studio, you can go into project settings and change your Multi-Byte (wide/narrow) setting or you can simply call MessageBoxA/W directly in order to pass in different encodings of strings.
For example:
LPWCSTR wideString = L"Hello, ";
MessageBoxW(NULL, (std::wstring(wideString) + L"world!").c_str(), L"Hello!", MB_OK);
LPCSTR narrowString = "Hello, ";
MessageBoxA(NULL, (std::string(narrowString) + "world!").c_str(), "Hello!", MB_OK);
If you can change modelPath to std::wstring, it becomes easy:
MessageBox(nullptr, (modelPath + L" not found.").c_str(), nullptr, 0);
I changed your 0 pointer values into nullptr as well.
Since std::string represents a narrow string, std::wstring represents a wide string, and the two are wildly different, casting from one representation to the other does not work, while starting with the appropriate one does. On the other hand, one can properly convert between representations using the new <codecvt> header in C++11.