0

I have an FILE_NOTIFY_INFORMATION struct which i have filled like this:

FILE_NOTIFY_INFORMATION* fni = new FILE_NOTIFY_INFORMATION;
fni->Action = 1;
wcscpy_s(fni->FileName,12, L"example.txt");
fni->FileNameLength = 12;
fni->NextEntryOffset = 0;

I have then castet this Struct to an std::byte*.

auto fni_as_byte = reinterpret_cast<std::byte*>(fni);

Now i want to put this fni_as_byte into an vector of std::vector<std::byte>. Because i need this for testing purpose.

Normally you receive the FILE_NOTIFY_INFORMATION for example from the ReadDirectoryChangesW function. And it's called like this:

std::vector<std::byte> notifyBuffer(1024);
res = ReadDirectoryChangesW(handle, notifyBuffer.data(), static_cast<DWORD>(notifyBuffer.size()), false, FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &overlapped, nullptr);

So how can i manually copy the castet FILE_NOTIFY_INFORMATION into the std::vector<std::byte>?

7
  • 2
    memcpy, std::copy, a for loop. All the usual options apply. Commented Dec 17, 2020 at 6:05
  • 2
    std::vector<std::byte> notifyBuffer{1024}; -- This creates a vector of one element, with that value being 1024. Is this what you want? Commented Dec 17, 2020 at 6:08
  • @PaulMcKenzie Have I missed something? Did uniform initalisation change? There's a constructor accepting a size parameter which should be called then – unless I'm not up to date... In any case, this unclearness is the reason I consider uniform initialisation flawed and it should not be used. notifyBuffer(1024) or notifyBuffer({1024}) are so much more obvious... Commented Dec 17, 2020 at 6:30
  • @Aconcagua std::vector also has a constructor that takes a std::initializer_list. That is the constructor that notifyBuffer{1024} will call, not the size constructor you are thinking of. If that initializer_list constructor didn’t exist, then yes, the size constructor would be called instead. Commented Dec 17, 2020 at 6:43
  • @RemyLebeau OK, then I have missed a change in the course of the standards. I still recall UI preferring the non-initialiser list constructors, if there are. Another breaking change then. Can you tell which standard changed that? Still even more convinced not to use UI... Commented Dec 17, 2020 at 6:52

1 Answer 1

2

Your code is corrupting memory, as the actual size of FILE_NOTIFY_INFORMATION is variable length. See Why do some structures end with an array of size 1?. You are not allocating enough memory to hold the string you are copying into the FileName. To allocate your FILE_NOTIFY_INFORMATION correctly, you would need to do something more like this instead:

FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*) new std::byte[offsetof(FILE_NOTIFY_INFORMATION, FileName[12])];
fni->Action = 1;
wcscpy_s(fni->FileName, 12, L"example.txt");
fni->FileNameLength = 11 * sizeof(WCHAR); // <— expressed in BYTEs, not including the null terminator!
fni->NextEntryOffset = 0;
...
delete (std::byte*) fni;

That being said, to copy the FILE_NOTIFY_INFORMATION into a std::vector, you can do it like this:

FILE_NOTIFY_INFORMATION* fni = ...;
// fill fni as needed...
std::vector<std::byte> buffer(offsetof(FILE_NOTIFY_INFORMATION, FileName) + fni->FileNameLength);
std::memcpy(buffer.data(), fni, buffer.size());
// use buffer as needed...

Alternatively, just use the std::vector up front as the memory for the original FILE_NOTIFY_INFORMATION:

std::vector<std::byte> buffer(offsetof(FILE_NOTIFY_INFORMATION, FileName[12]));
auto fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer.data());
// fill fni as needed....
// use buffer as needed...
Sign up to request clarification or add additional context in comments.

11 Comments

Note that the last one will break when buffer reaches end of scope, as fni doesn't own the data.
@JHBonarius in the 2nd example, fni is declared after buffer, so fni will go out of scope first. The only way that code would break is if that pointer is saved somewhere else, and then used after buffer is destroyed.
@Kevin Read the docs. FileNameLength is in bytes and does not count the trailing null. And FileName is Unicode and does not contain a null character. Now do the math.
@Kevin the FileName is 11 characters (not counting the null terminator) that take up 22 bytes. Per the documentation: “FileNameLength -The size of the file name portion of the record, in bytes. Note that this value does not include the terminating null character. FileName - A variable-length field ... The file name is in the Unicode character format and is not null-terminated.” Windows uses UTF-16 for Unicode strings. If you are having trouble getting the FILE_NOTIFY_INFORMATION out of the vector then you are likely not taking these facts into account properly.
@Kevin sizeof(notIf.FileName) should be sizeof(WCHAR). They happen to be the same byte size in this situation, but you should write your code to be easier to read and understand. In that same vein, I would also suggest use the wstring constructor that takes a size parameter, rather than iterators: std::wstring wPathName(notIf.FileName, notIf.FileNameLength / sizeof(WCHAR));
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.