Do I have to call TlsAlloc() in DLL_PROCESS_ATTACH? - winapi

Do I have to call TlsAlloc() in DLL_PROCESS_ATTACH once genrally since the DLL hasn't any addional calls with DLL_THREAD_ATTACH for the thread doing the LoadLibrary() ?
Or to make it even more complicated: Do I get a DLL_THREAD_ATTACH notification in addition to the DLL_PROCESS_ATTACH notification if I create a thread directly from DLL_PROCESS_ATTACH ?
EDIT: I've wrote a little test application that loads a library that spawns a thread:
#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
HMODULE hmLib = LoadLibraryA( "theDll.dll" );
HANDLE &hDllThread = *(HANDLE *)GetProcAddress( hmLib, "hDllThread" );
WaitForSingleObject( hDllThread, INFINITE );
cout << "thead terminated " << endl;
cout << "thread id: " << GetCurrentThreadId() << endl << endl;
}
Here's the DLL:
#include <Windows.h>
#include <iostream>
using namespace std;
extern "C"
__declspec(dllexport)
HANDLE hDllThread = NULL;
void printThreadId()
{
cout << "thread id: " << GetCurrentThreadId() << endl;
};
BOOL APIENTRY DllMain( HMODULE hModule, DWORD dwReason, LPVOID lpReserved )
{
switch( dwReason )
{
case DLL_PROCESS_ATTACH:
{
printThreadId();
cout << "process attach" << endl << endl;
::hDllThread = CreateThread( nullptr, 0,
[]( LPVOID ) -> DWORD
{
cout << "created thread id: " << GetCurrentThreadId() << endl;
cout << endl;
return 0;
}, nullptr, 0, nullptr );
// don't do that here
// WaitForSingleObject( ::hDllThread, INFINITE );
break;
}
case DLL_THREAD_ATTACH:
printThreadId();
cout << "thread attach" << endl << endl;
// don't do that here
// WaitForSingleObject( ::hDllThread, INFINITE );
break;
case DLL_THREAD_DETACH:
printThreadId();
cout << "thread detach" << endl << endl;
break;
case DLL_PROCESS_DETACH:
printThreadId();
cout << "process detach" << endl << endl;
break;
}
return TRUE;
}
static struct S
{
S()
{
printThreadId();
cout << "statics are initialized, but not thread locals" << endl << endl;
}
} s;
On my computer this prints:
thread id: 14692
statics are initialized, but not thread locals
thread id: 14692
process attach
thread id: 9396
thread attach
created thread id: 9396
thread id: 9396
thread detach
thead terminated
thread id: 14692
thread id: 14692
process detach
"thread attach" is printed before "created thread" in the context of the new thread. If I wait for the thread to terminate in DLL_PROCESS_ATTACH or DLL_THREAD_ATTACH I wait forever. So the entry point is called after both calls. Is this documented ?

Sorry, everything I told based on outdated informaton. Before Vista dynamially loaded DLLs with thread locals weren't reliable.
Everything is fine with thread_local / __declspec(thread) in DLLs for years.

1.Do I have to call TlsAlloc() in DLL_PROCESS_ATTACH once genrally since the DLL hasn't any addional calls with DLL_THREAD_ATTACH for the thread doing the LoadLibrary() ?
Call TlsAlloc() won't do anything for your current question.
Each time the process creates a new thread, the entry-point function
is called with the DLL_THREAD_ATTACH value.
2.So the entry point is called after both calls. Is this documented ?
Raymond Chen has explained thread creation and running process in his blog.
When you call CreateThread, a kernel thread object is created and
scheduled. Once the thread gets a chance to run, the kernel calls all
the DllMain functions with the DLL_THREAD_ATTACH code. Once that’s
done, the thread’s entry point is called.
In MSDN, You can refer to this official document.
DLL_THREAD_ATTACH, Note that a DLL's entry-point function is called with this value only by threads created after the DLL is loaded by the
process.

Related

Can't get segmentation fault exit code from boost child process

I am trying to get the exit code of a child process (using boost::process and boost::asio) when that child process is killed due to a segmentation violation or divide be zero or any other kill signal. The exit code and error code always return with 0 and success.
I am running this on CentOS 7 using g++ 4.8.5 and boost 1.66
If I run the same code with a child process that simply returns a non-zero exit code it successfully returns that exit code.
#include <iostream>
#include <boost/process.hpp>
#include <boost/asio/io_service.hpp>
namespace bp = boost::process;
using namespace std;
int main (int argc, char** argv)
{
string exe = "./crashes";
vector<string> data;
boost::asio::io_service ios;
int exit_code;
error_code ec;
future<string> ostr;
bp::child c(exe,
(bp::std_out & bp::std_err) > ostr,
ios,
bp::on_exit=[&exit_code, &ec](int exit, const error_code& ecin)
{exit_code = exit; ec = ecin;});
ios.run();
cout << "Exit Code = " << exit_code << endl;
cout << "Error Code = " << ec.message() << endl;
cout << "child stdin & stderr:\n";
cout << ostr.get() << endl;
return exit_code;
}
and the crashes code
int main (int argc, char** argv)
{
int* y = 0;
int c = *y;
}
The results show a 0 exit code and Success error_code
Exit Code = 0
Error Code = Success
child stdin & stderr:
running the crashes executable alone returns an exit code of 139
bash-4.2$ ./crashes
Segmentation fault (core dumped)
bash-4.2$ echo $?
139
The details of process termination and exit codes are platform dependent.
Boost process papers over the differences in the default interface: your on_exit handler is called with the result of boost::process::detail::posix::eval_exit_status() of the exit status, which means:
inline int eval_exit_status(int code)
{
if (WIFEXITED(code))
{
return WEXITSTATUS(code);
}
else if (WIFSIGNALED(code))
{
return WTERMSIG(code);
}
else
{
return code;
}
}
So, you get "exit-code 11" meaning segfault... If you want to actually know, you can look at native_exit_code()
bp::on_exit = [&result, &c](int /*ignored*/, const std::error_code &ec) {
auto exit_status = c.native_exit_code();
result.exit_code = boost::make_optional(WIFEXITED(exit_status), WEXITSTATUS(exit_status));
result.signal = boost::make_optional(WIFSIGNALED(exit_status), WTERMSIG(exit_status));
result.ec = ec;
}
Now this assumes some changes to the result variables. Full listing:
Listing
#include <boost/asio/io_service.hpp>
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
int main(int argc, char**) {
std::string exe = argc>1? "./ltua" : "./crashes";
boost::asio::io_service ios;
struct {
boost::optional<int> exit_code;
boost::optional<int> signal;
std::error_code ec{};
} result;
std::future<std::string> ostr;
bp::group g;
bp::child c(exe, g, (bp::std_out & bp::std_err) > ostr, ios,
bp::on_exit = [&result, &c](int /*ignored*/, const std::error_code &ec) {
auto exit_status = c.native_exit_code();
result.exit_code = boost::make_optional(WIFEXITED(exit_status), WEXITSTATUS(exit_status));
result.signal = boost::make_optional(WIFSIGNALED(exit_status), WTERMSIG(exit_status));
result.ec = ec;
});
//g.wait();
ios.run();
if (result.exit_code) {
std::cout << "Exited with " << *result.exit_code << std::endl;
}
if (result.signal) {
std::cout << "Signaled with sginal #" << *result.signal << ", aka " << ::strsignal(*result.signal) << std::endl;
}
std::cout << "Error Code = " << result.ec.message() << std::endl;
std::cout << "child stdin & stderr:\n";
std::cout << ostr.get() << std::endl;
return result.exit_code? *result.exit_code : 255;
}
Output
When run with ltua.cpp:
#include <iostream>
int main() {
std::cout << "so long" << std::end;
std::cerr << "and thanks" << std::end;
std::cout << "for all" << std::end;
std::cerr << "the fish" << std::end;
return 42;
}
Prints
Exited with 42
Error Code = Success
child stdin & stderr:
so long
and thanks
for all
the fish
And with crashes.cpp:
int main() {
int *y = 0;
int c = *y;
}
Prints
Signaled with sginal #11, aka Segmentation fault
Error Code = Success
child stdin & stderr:

How to test an instance counter by asynchronous run of a boost childprocess?

I have tried to use boost::childprocess with an async_pipe as shown in the code example below, while expecting since there is a wait method, that the call to run would not wait for the called executable to finish before continuing to the line where I call wait(). My aim is namely to start the same executable multiple times in order to test in GTest an instance counting method (implemented based on boost managed shared memory segment).
But here fore I need the call to io_service::run(), to not wait for the called executable to finish as it does right now. Can someone tell me where I am using it wrong please? Or if this is the wrong way to unit test my function? I have been trying to find the solution for quite some time!
Here is a sample of how I call one instance of the executable:
int CallChildProcess_Style9() {
std::string strCmdLine = "E:\\file.exe --Debug MainStartUps_Off --Lock 3";
boost::asio::io_service m_oIOS;
std::vector<char> m_oAsyncBuffer_Out;
bp::async_pipe m_oAsyncPipe_Out(m_oIOS);
std::error_code build_ec;
size_t nReadSize(0);
boost::scoped_ptr<boost::process::child> m_pChildProcess(nullptr);
m_pChildProcess.reset(new bp::child(strCmdLine.data(), bp::std_out > m_oAsyncPipe_Out, build_ec));
m_oAsyncBuffer_Out.resize(1024*8);
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) { nReadSize = size; });
size_t iii = m_oIOS.run();
m_pChildProcess->wait();
m_oAsyncBuffer_Out.resize(nReadSize);
std::string strBuf(m_oAsyncBuffer_Out.begin(), m_oAsyncBuffer_Out.begin() + nReadSize);
int result = m_pChildProcess->exit_code();
m_oAsyncPipe_Out.close();
m_oIOS.reset();
return result;
}
Using io_service
To be using async_pipe, you need to supply the io_service instance to the parameter keywords of bp::child:
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/scoped_ptr.hpp>
#include <iostream>
namespace bp = boost::process;
int CallChildProcess_Style9() {
std::string strCmdLine = "/bin/cat";
boost::asio::io_service m_oIOS;
std::vector<char> m_oAsyncBuffer_Out;
bp::async_pipe m_oAsyncPipe_Out(m_oIOS);
std::error_code build_ec;
size_t nReadSize(0);
boost::scoped_ptr<boost::process::child> m_pChildProcess(nullptr);
std::vector<std::string> const args = { "/home/sehe/Projects/stackoverflow/test.cpp" };
m_pChildProcess.reset(new bp::child(strCmdLine, args, bp::std_out > m_oAsyncPipe_Out, build_ec, m_oIOS));
std::cout << "Launched: " << build_ec.message() << std::endl;
m_oAsyncBuffer_Out.resize(1024 * 8);
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) {
std::cout << "read completion handler: size = " << size << " (" << ec.message() << ")" << std::endl;
nReadSize = size;
});
std::cout << "read started" << std::endl;
size_t iii = m_oIOS.run();
std::cout << "io_service stopped" << std::endl;
std::cout << "initiate child::wait" << std::endl;
m_pChildProcess->wait();
std::cout << "wait completed" << std::endl;
std::string const strBuf(m_oAsyncBuffer_Out.data(), nReadSize);
int result = m_pChildProcess->exit_code();
m_oAsyncPipe_Out.close();
m_oIOS.reset();
return result;
}
int main() {
CallChildProcess_Style9();
}
Prints
http://coliru.stacked-crooked.com/a/8a9bc6bed3dd5e0a
Launched: Success
read started
read completion handler: size = 1589 (End of file)
io_service stopped
initiate child::wait
wait completed
Hanging Up The Child
Even with that fixed, async_pipe::async_read only reads until the buffer is full or EOF is reached. If the child process outputs more than the buffer size (8k in your sample) then it will get stuck and never finish.
E.g.: replacing the command like this:
std::string strCmdLine = "/usr/bin/yes";
Results in
Live On Coliru
Launched: Success
read started
read completion handler: size = 8192 (Success)
io_service stopped
initiate child::wait
At which it will hang till infinity. This is not because yes has infinite output. Any command having large output will hang (e.g. /bin/cat /etc/dictionaries-common/words hangs in the same way). You can prove this by looking at the strace output:
$ sudo strace -p $(pgrep yes)
strace: Process 21056 attached
write(1, "/home/sehe/Projects/stackoverflo"..., 8170
The easiest way to "fix" this would be to close the output sink after you filled up your output buffer:
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) {
std::cout << "read completion handler: size = " << size << " (" << ec.message() << ")" << std::endl;
nReadSize = size;
m_oAsyncPipe_Out.close();
});
This requires you to anticipate that the child exited before you call wait() so wait() might fail:
Live On Coliru
Launched: Success
read started
read completion handler: size = 8192 (Success)
io_service stopped
initiate child::wait
wait completed (Success)
Taking A Step Back: What Do You Need?
It looks, though, that you might be complicating. If you're happy limiting the output to 8k, and all you need is to have multiple copies, why bother with async io?
Any child is already asynchronous, and you can just pass the buffer:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
using Args = std::vector<std::string>;
using Buffer8k = std::array<char, 8192>;
int main() {
auto first_out = std::make_unique<Buffer8k>(),
second_out = std::make_unique<Buffer8k>();
*first_out = {};
*second_out = {};
boost::asio::io_service svc;
bp::child first("/bin/echo", Args{"-n", "first"}, bp::std_out > boost::asio::buffer(*first_out), svc);
bp::child second("/bin/echo", Args{"-n", "second"}, bp::std_out >boost::asio::buffer(*second_out), svc);
std::cout << "Launched" << std::endl;
svc.run();
first.wait();
second.wait();
std::string const strFirst(first_out->data()); // uses NUL-termination (assumes text output)
std::string const strSecond(second_out->data()); // uses NUL-termination (assumes text output)
std::cout << strFirst << "\n";
std::cout << strSecond << "\n";
return first.exit_code();
}
Prints
Launched
first
second
More Examples
Because I can't really be sure about what you need, look at other examples that I wrote to actually show live async IO, where you might need to respond to particular output of one process.
Boost::process output blank lines
Read child process stdout in a separate thread with BOOST process
How to retrieve program output as soon as it printed?

Serializing multiple std::shared_ptr via boost::serialization and sending over boost::asio

I'd like to transmit a shared_ptr object via boost asio from a client to a server. Here is my code:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {
}
virtual ~Message() {
}
string text;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &text;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("localhost", "3000");
boost::archive::text_oarchive archive(stream);
for (int i = 0; i < 10; i++) {
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello " << i;
dl->text = ss.str();
archive << dl;
}
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
std::shared_ptr<Message> m;
try {
archive >> m;
cout << m->text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
void runServer() {
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3000);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
int main(int argc, char **argv) {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
return 0;
}
Here is the program output:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 3
Hello 3
Hello 3
Hello 3
Client shutdown
Hello 3
Hello 3
input stream error
eof
Server: shutdown client handling...
I am expecting the following output:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6
Hello 7
Client shutdown
Hello 8
Hello 9
input stream error
eof
Server: shutdown client handling...
When changing the shared_ptr to a simple object (std::shared_ptr<Message> m; to Message m) everything works as expected. I want to stick to the shared_ptr. What do I need to change?
Serialization alone seems to work:
stringstream stream;
{
boost::archive::text_oarchive archive(stream);
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello World!";
dl->text = ss.str();
archive << dl;
}
{
boost::archive::text_iarchive archive(stream);
std::shared_ptr<Message> m;
archive >> m;
cout << m->text << endl;
}
Output: Hello World!
The issues you're encountering are due to object tracking done by Boost.Serialization.
Depending on how the class is used and other factors, serialized
objects may be tracked by memory address. This prevents the same
object from being written to or read from an archive multiple times.
These stored addresses can also be used to delete objects created
during a loading process that has been interrupted by throwing of an
exception.
The documentation actually foreshadows this specific issue happening:
This could cause problems in progams[sic] where the copies of different
objects are saved from the same address.
Furthermore, the Class Serialization Traits documentation on object tracking tells us that in this particular situation, object tracking is enabled:
Default tracking traits are:
For primitive, track_never.
For pointers, track_never. That is, addresses of addresses are not tracked by default.
All current serialization wrappers such as boost::serialization::nvp, track_never.
For all other types, track_selectively. That is addresses of serialized objects are tracked if and only if one or more of the
following is true:
an object of this type is anywhere in the program serialized through a pointer.
the class is explicitly "exported" - see below.
the class is explicitly "registered" in the archive
Going back to your situation -- in the client, due to how your loop body is written, the 5th (and following) Message instance were allocated at the same address as the 4th Message instance. You can verify this by inspecting the values of dl.get() in each iteration. (In my tests on coliru, all of the instances were allocated at the same address, so YMMV).
Due to how object tracking works, all those shared_ptr instances were considered to point to the same Message instance (even though you changed the value meanwhile -- the library does not expect this happening), so the additional occurrences were just serialized as additional references. Upon deserialization... to be honest this smells of memory leaks and/or dangling reference issues (opinion, haven't investigated this in detail).
Summed up, the main issue with the code as shown is that it breaks a prerequisite of the serialization library, which is that you're serializing some constant state, and on deserialization you recreate that same state.
One way to address this would be to have an initialized std::vector of shared_ptr<Message> containing all the messages to transmit in this particular transaction. Similarly, you'd deserialize the whole vector on the other side. If you expect to have some persistent connection, then add framing to the protocol, with each frame containing an archive that contains one sequence of messages.
Minimal code modifications to make this work -- add include
#include <boost/serialization/vector.hpp>
Change runClient() as such:
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
std::vector<std::shared_ptr<Message>> messages;
for (int i = 0; i < 10; i++) {
std::shared_ptr<Message> dl = std::make_shared<Message>();
stringstream ss;
ss << "Hello " << i;
dl->text = ss.str();
messages.emplace_back(dl);
}
boost::archive::text_oarchive archive(stream);
archive << messages;
stream.close();
cout << "Client shutdown" << endl;
}
And change handleIncommingClientConnection(...) as such:
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
try {
std::vector<std::shared_ptr<Message>> messages;
archive >> messages;
for (auto const& m : messages) {
cout << m->text << endl;
}
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
NB: This doesn't add any support for multiple frames -- the client is expected to close the connection after it sent one vector of messages, otherwise the behaviour is undefined.
Sample on Coliru
Further resources:
boost serialization multiple objects

XINPUT_GAMEPAD_A does not work unless you hold down the button when program starts

So I am using xinput with my program, it is all set up and working so I can detect my xbox one controller. I want to be able to detect when a button on the controller is pressed. The procedure I use works if I hold down the button when the program starts. I have the if command setup inside a while so it constantly executes although for some reason the value does not change when I press A on my controller.
So basically, if I hold down A when the program is opening it works and returns the cout on the screen. Although if I want to press it a little after the program has started (which is what I want to work) it does not detect it.
Here is my code:
using namespace std;
XINPUT_STATE state;
bool A_button_pressed;
int online;
int test;
int main() {
if (XInputGetState(0, &state) == ERROR_SUCCESS)
{
online = 1;
cout << "I could find a controller, it is an Xbox Controller" << endl;
} else {
online = 2;
cout << "Unable to find controller, searching..." << endl;
}
cout << A_button_pressed << endl;
cout << "Active" << endl;
while (online == 1) {
bool A_button_pressed = ((state.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0);
cout << A_button_pressed << endl;
if (A_button_pressed = ((state.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0)) {
cout << "You pressed a button, congrats, game over..." << endl;
}
};
}
As far as I know I am including all of the correct libraries in the correct order:
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <Xinput.h>
#pragma comment(lib, "Xinput.lib")
#pragma comment(lib, "Xinput9_1_0.lib")
Your problem is that you are only calling XInputGetState once at startup. You must call XInputGetState every frame that your program runs so that your state info can be updated.

losing files when using ReadDirectoryChangesW

I'm using ReadDirectoryChangesW to monitor a directory.
Here's my simple code
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <string>
#include <cwctype>
using namespace std;
wstring getname(FILE_NOTIFY_INFORMATION *tmp)
{
wstring s = L"";
for (int i = 0;i < tmp->FileNameLength / 2;i++)
s += tmp->FileName[i];
return s;
}
void _tmain(int argc, TCHAR *argv[])
{
HANDLE hDir;
char notify[1024];
DWORD cbBytes,i;
char AnsiChar[3];
wchar_t UnicodeChar[2];
LPTSTR path;
FILE_NOTIFY_INFORMATION *pnotify=(FILE_NOTIFY_INFORMATION *)notify;
FILE_NOTIFY_INFORMATION *tmp ;
// GetCurrentDirectory(MAX_PATH,path.GetBuffer(MAX_PATH+1));
wcout.imbue(locale("chs"));
path = argv[1];
hDir = CreateFile( path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL);
wcout << L"===CreateFile complete===" << endl;
if (hDir == INVALID_HANDLE_VALUE)
{
wcout << L"invalid handle value" << endl;
return;
}
FILE_NOTIFY_INFORMATION buffer[1024];
FILE_NOTIFY_INFORMATION *pbuffer;
while (TRUE)
{
wcout << L"waiting..." << endl;
if(ReadDirectoryChangesW(hDir, &buffer, sizeof(buffer),
TRUE, FILE_NOTIFY_CHANGE_FILE_NAME| FILE_NOTIFY_CHANGE_LAST_WRITE,
&cbBytes, NULL, NULL))
{
pbuffer = buffer;
do{
tmp = pbuffer;
switch(tmp->Action)
{
case FILE_ACTION_ADDED:
wcout << L"Directory/File added - " << getname(tmp) << endl;
break;
case FILE_ACTION_REMOVED:
wcout << L"Directory/File removed - " << getname(tmp) << endl;
break;
case FILE_ACTION_MODIFIED:
wcout << L"Directory/File modfied - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_OLD_NAME:
wcout << L"Directory/File old name - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_NEW_NAME:
wcout << L"Directory/File new name - " << getname(tmp) << endl;
break;
default:
wcout << L"unknown action\n" << endl;
break;
}
pbuffer += pbuffer->NextEntryOffset;
}while(pbuffer->NextEntryOffset);
} else
{
wcout << "readChangesW failed now return" << endl;
return;
}
}
}
It looks fine, however, when I'm adding or deleting a large number of files in my directory, it will not report some of the changes, how can I fix this?
Try making your buffer bigger.
From the documentation for the ReadDirectoryChangesW function:
When you first call ReadDirectoryChangesW, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, the entire contents of the buffer are discarded and the lpBytesReturned parameter contains zero.
The buffer size that the system allocates is based on the size of the buffer you pass in. If you pass in a bigger size the system will allocate a bigger buffer to store changes that occur while you are processing the previous lot of changes, which means there's less chance of the buffer overflowing and those changes being lost.

Resources