Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my colon character disappear when I go from char[] to string?

Tags:

c++

In an old Windows application I'm working on I need to get a path from an environment variable and then append onto it to build a path to a file. So the code looks something like this:

static std::string PathRoot; // Private variable stored in class' header file

char EnvVarValue[1024];
if (! GetEnvironmentVariable(L"ENV_ROOT", (LPWSTR) EnvVarValue, 1024))
{
    cout << "Could not retrieve the ROOT env variable" << endl;
    return;
}
else
{
    PathRoot = EnvVarValue;
}

// Added just for testing purposes - Returning -1
int foundAt = PathRoot.find_first_of(':');

std::string FullFilePath = PathRoot;
FullFilePath.append("\\data\\Config.xml");

The environment value for ENV_ROOT is set to "c:\RootDir" in the Windows System Control Panel. But when I run the program I keep ending up with a string in FullFilePath that is missing the colon char and anything that followed in the root folder. It looks like this: "c\data\Config.xml".

Using the Visual Studio debugger I looked at EnvVarValue after passing the GetEnvironmentVariable line and it shows me an array that seems to have all the characters I'd expect, including the colon. But after it gets assigned to PathRoot, mousing over PathRoot only shows the C and drilling down it says something about a bad ptr. As I noted the find_first_of() call doesn't find the colon char. And when the append is done it only keeps the initial C and drops the rest of the RootDir value.

So there seems to be something about the colon character that is confusing the string constructor. Yes, there are a number of ways I could work around this by leaving the colon out of the env variable and adding it later in the code. But I'd prefer to find a way to have it read and used properly from the environment variable as it is.

like image 345
Mike O Avatar asked Dec 27 '13 16:12

Mike O


2 Answers

You cannot simply cast a char* to a wchar_t* (by casting to LPWSTR) and expect things to work. The two are fundamentally distinct types, and in Windows, they signify different encoding.

You obviously have WinAPI defines set such that GetEnvironmentVariable resolves to GetEnvironmentVariableW, which uses UTF-16 to encode the string. In practice, this means a 0 byte follows every ASCII character in memory.

You then construct a std::string out of this, so it takes the first 0 byte (at char index 1) as the string terminator, so you get just "c".

You have several options:

  • Use std::wstring and wchar_t EnvVarValue[1024];

  • Call GetEnvironmentVariableA() (which uses char and ASCII)

  • Use wchar_t EnvVarValue[1024]; and convert the returned value to a std::string using something like wcstombs.

like image 62
Angew is no longer proud of SO Avatar answered Oct 14 '22 21:10

Angew is no longer proud of SO


It seems you are building with wide-character functions (as indicated by your cast to LPWSTR). This means that the string in EnvVarValue is a wide-character string, and you you should be using wchar_t and std::wstring instead.

I would guess that the contents in the array array after the GetEnvironmentVariable call is actually the ASCII values 0x43 0x00 0x3a 0x00 0x5c 0x00 etc. (that is the wide-char representation of "C:\"). The first zero acts as the string terminator for a narrow-character string, which is why the narrow-character string PathRoot only contains the 'C'.

like image 29
Some programmer dude Avatar answered Oct 14 '22 19:10

Some programmer dude