0

Following code gives empty string and length = 0, but while debugging I can see the childDisplayName has correct name.

    CHAR fileSystemName[MAX_PATH + 1] = { 0 }; 
    DWORD serialNumber = 0; DWORD maxComponentLen = 0; 
    string childDisplayName = "";
    DWORD fileSystemFlags = 0; 
    if (GetVolumeInformationA("C:\\", // L"\\MyServer\MyShare\"
        (LPSTR)&childDisplayName, MAX_PATH+1,
        &serialNumber, &maxComponentLen,
        &fileSystemFlags, fileSystemName, sizeof(fileSystemName)) == true)
    {
        
        cout << childDisplayName << "length: "<<childDisplayName.length()<<endl;
    }


following code works fine. I am not getting why LPSTR works when I pass char array and does not work when I pass a string.

    CHAR fileSystemName[MAX_PATH + 1] = { 0 }; 
    DWORD serialNumber = 0; DWORD maxComponentLen = 0; 
    CHAR childDisplayName[MAX_PATH + 1] = { 0 };
    DWORD fileSystemFlags = 0; 
    if (GetVolumeInformationA("C:\\", // L"\\MyServer\MyShare\"
        childDisplayName, MAX_PATH+1,
        &serialNumber, &maxComponentLen,
        &fileSystemFlags, fileSystemName, sizeof(fileSystemName)) == true)
    {
        
        cout << childDisplayName << "length: "<<strlen(childDisplayName)<<endl;
    }
aromahola
  • 190
  • 1
  • 12
  • The adress of a string is _not_ the adress of the character, `std::string` only manages a dynamic array of characters. – Lukas-T Aug 06 '20 at 11:51
  • @churill thank you for your response. but in first snippet it prints empty output & length = 0. but I can see the correct Drive Label in variable childDisplayName when I debug the code. Could you please explain this. – aromahola Aug 06 '20 at 12:21
  • Every cast means, that you are side-stepping C++' type system. The consequence is, that the compiler will no longer verify correctness. Verifying correctness is on you, and you failed to do so. – IInspectable Aug 07 '20 at 09:23
  • Hi,if any answer did help to you, please feel free to mark it to help people with the same issue, and let me know if you have any problem.Thanks. – Zeus Sep 04 '20 at 06:37

2 Answers2

1

string childDisplayName = ""; creates an empty empty string (zero size and unspecified capacity). Using that as a data-buffer to write into is not likely to go well.

You can do this: string childDisplayName(MAX_PATH + 1, ' '); to create a string with the proper space allocated.

Secondly, as @churill wrote, the address of a string is not the address of the characters in it. Instead use childDisplayName.data() to get a char* to the internal storage of the string that you can write in - but make sure not to write outside the range [data(); data() + size()).

EDIT: A bit on how std::string and .data() works.

I made a small example program:

#include<iostream>
#include<string>

void print(const std::string& s)
{
    std::cout << "String size: " << s.size() << '\n';
    std::cout << "String contents: >>" << s << "<<\n";
    std::cout << "String as c-string: >>" << s.c_str() << "<<\n";
    std::cout << '\n';
}

int main() {
    std::string bla = "";
    auto bladata = bla.data();
    for (int i = 0;i < 5;++i) {
        bladata[i] = '!';
    }
    print(bla);

    std::string bla2(10, '\0');
    auto bla2data = bla2.data();
    for (int i = 0;i < 5;++i) {
        bla2data[i] = '!';
    }
    print(bla2);
}

When run this outputs:

String size: 0
String contents: >><<
String as c-string: >>!!!!!╠╠╠╠╠╠╠╠╠╠╠<<

String size: 10
String contents: >>!!!!!     <<
String as c-string: >>!!!!!<<

What is going on here? First thing to notice is that an empty std::string is created with zero size and unspecified capacity - looking in my debugger, I know that on my system that unspecified capacity is 15, so as long as I don't go beyond that nothing should crash. But this is obviously not something you should do in real code (writing here is strictly undefined behavior).

This means that the bla string is size 0, and contains a 15 character char buffer, where I set the first 5 characters to '!'. So when I try to print its size() or print it as a std::string it is identical to any regular empty string. However, if I use .c_str() to print the internal buffer directly, then it prints as any old char* and just prints whatever is in memory until it encounters a null-character.

On the other hand, bla2 is initialized to contain 10 null-characters. That means that its size is 10, and its capacity is at least 10 (in my case it happens to also be 15). This means that after the loop it still reports as size 10, regardless of how many '!'s I put into the buffer, and when I print it as a std::string it prints all the 10 characters it contains; both the 5 '!'s and the 5 '\0's. However, when I print it as a char* it prints the 5 '!'s and then stop as soon as it encounters a null-character.

Frodyne
  • 3,547
  • 6
  • 16
  • In first code snippet, I tried sending childDisplayName.c_str() it did not work(length = 0 in o utput). But in second snippet when I use childDisplayName (this is char array). It works fine. I am not getting how these 2 are different? Why LPSTR gives different behaviour for these 2? – aromahola Aug 06 '20 at 12:12
  • Also in first snippet, it prints empty output & length is also 0. but I can see the correct Drive Label in variable childDisplayName when I debug the code. Could you please explain this as well – aromahola Aug 06 '20 at 12:14
  • `c_str()` returns a const, so you can't pass that to an arg expecting `LPSTR`. Use `data()` as shown in the answer. – David Heffernan Aug 06 '20 at 12:21
  • I used `string childDisplayName (MAX_PATH + 1, ' ');` and `(LPSTR)childDisplayName.data()` and it worked. But I can see that .`data()` also returns `const char*`, then how come it worked here? I am confused.. – aromahola Aug 06 '20 at 12:33
  • At the same time `string childDisplayName (MAX_PATH + 1, ' ');` and `(LPSTR)childDisplayName.c_str()` does not work as this returns `const char*` – aromahola Aug 06 '20 at 12:34
  • @aromahola `.data()` can return both const and non-const, at least since C++17, since it is directly there to give you access to change the contents of the string. On the other hand `.c_str()` only returns `const char*` since it is only intended for read access. – Frodyne Aug 06 '20 at 12:40
  • 1
    @aromahola -- You should realize that quite possibly, the string is implemented using short string optimization, where short strings are stored in an array. Maybe you are viewing the internal array? Regardless, you are to use the `public` interface of `std::string` and not rely on how the string class is implemented. – PaulMcKenzie Aug 06 '20 at 13:12
  • @aromahola I added an explanation of some of the behavior you noticed with the "" initialized string. – Frodyne Aug 06 '20 at 13:20
0

In first code snippet, I cannot get the correct name of childDisplayName.

enter image description here

Although it seems to have several characters, but an exception is triggered when I want to output it.

enter image description here

So you passed a wrong address to the GetVolumeInformationA.Because the address of a string is not the address of the characters

You can test like the following code:

string s("test");
CHAR cs[] = "test";
cout << (void*)&s << endl;
cout << (void*)&s[0] << endl;

cout << (void*)&cs << endl;
cout << (void*)&cs[0] << endl;

Output: enter image description here

As @churill says, std::string only manages a dynamic array of characters.So you can not pass the address of string to the function.

And according to MSDN:

If you modify the contents of the string returned by the const overload of data, the behavior is undefined. You also get undefined behavior if the terminal null character is changed to any other value. The returned pointer may be invalidated if a non-const reference to the string is passed to a standard library function. It can also be invalidated by a call to a non-const member function.

So even if you really succeed in passing childDisplayName.data() into the function. This is also an undefined behavior.I recommend that you pass in the parameter types required by the function correctly, instead of trying to pass other undefined or untyped behaviors, which will cause you a lot of confusion.

Zeus
  • 3,703
  • 3
  • 7
  • 20