0

I'm using GetPrivateProfileStringA to read some things from a .ini file. I have some other class where I save things along with a string array. I have to use it like this to get a proper string into the ControlAlt array:

char buffer[24];
GetPrivateProfileStringA("CONTROLS",
    "ShiftUpAlt",
    "LeftThumb",
    buffer,
    (DWORD)24,
    "./Gears.ini");
scriptControl->ControlAlt[ScriptControls::ControlType::ShiftUp] = buffer;

I've tried putting it in directly, like so:

GetPrivateProfileStringA("CONTROLS",
    "ShiftUpAlt",
    "LeftThumb",
    (LPSTR)scriptControl->ControlAlt[ScriptControls::ControlType::ShiftUp],
    (DWORD)24,
    "./Gears.ini");

But then the value in ControlAlt is an LPSTR, which gives complications later when comparing it against a proper string. Is there a way to not use a buffer for this?

ControlAlt is defined as std::string ControlAlt[SIZEOF_ControlType];

ikt
  • 3
  • 4

1 Answers1

1

GetPrivateProfileStringA requires a buffer to write a classic C-style '\0'-terminated string into, and a std::string is not such a buffer, although as you observe, a C-style string can be converted to a std::string.

More specifically, GetPrivateProfileStringA expects a char * (LPSTR in Windows API terms) pointing to a writable buffer and that buffer's length. std::string does not provide this - at best, it provides the c_str() accessor which returns const char * (LPCSTR in Windows API terms) - a pointer to a read-only buffer. The const-ness of the buffer data is a pretty good indication that modifying it is a bad idea and will more than likely lead to undefined behavior.

C++ '98 says: "A program shall not alter any of the characters in this sequence." However, implementations conforming to newer standards may well be more willing to put up with monkey business: resize() to make the buffer large enough, then use &foo[0] to get a char * that isn't const (or just const_cast away the protection on data()), let GetPrivateProfileStringA write to the buffer, then truncate the std::string at the '\0' wherever it landed. This still doesn't let you pass in a std::string directly to a function expecting a buffer pointer, though, because they are not the same thing - it just gives you a chance to avoid copying the string one extra time from the buffer.

Jeffrey Hantin
  • 35,734
  • 7
  • 75
  • 94
  • Ah, I understand why now, thank you for the explanation. I think I'd stick with the solution with the buffer then, since going the way I initially wanted to go appears to be pretty unsafe? At least, that's what I understood from [this](http://stackoverflow.com/questions/2880248/stdstring-resize-and-stdstring-length). I suppose I can just re-use that buffer without clearing it? Or should I memset the entire buffer to \0 to ensure it doesn't contain old data? – ikt Feb 26 '16 at 12:46
  • You can certainly re-use it. Old data or other garbage in the buffer should not in theory be a problem, since nothing beyond the trailing `'\0'` is considered part of the string's value when converting, and `GetPrivateProfileString` always writes a trailing `'\0'`. – Jeffrey Hantin Feb 26 '16 at 23:54
  • Clearing the buffer afterwards is advisable if it is handling sensitive information, but use [`SecureZeroMemory`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa366877(v=vs.85).aspx) instead of `memset`, `bzero` or your own loop. This is because if an optimizing compiler notices that the memory is freed and not read after being overwritten it is likely to decide that the overwrite is a pointless ["dead store"](https://en.wikipedia.org/wiki/Dead_store) operation and remove it. – Jeffrey Hantin Feb 27 '16 at 00:02
  • That's reassuring then. It doesn't contain sensitive data, so that's a few things more I can remove then. – ikt Feb 27 '16 at 13:21