views:

107

answers:

5

I'm developing a DLL in C++ which needs to write some data via a (previously established) TCP/IP connection using the write() call. To be precise, the DLL should send a little 'Process 12345 is terminating at 2007-09-27 15:30:42, value of i is 131' message over the wire when the process goes down.

Unfortunately, all the ways I know for detecting that the process is ending are apparently too late for any network calls to succeed. In particular, I tried the following approaches and the write() call returned -1 in every case:

  1. Calling write() from the destructor of a global object.
  2. Calling write() from a callback function registered using atexit().
  3. Calling write() from DllMain (in case the reason argument is DLL_PROCESS_DETACH). I know that this is not a safe thing to do, but I'm getting a bit desperate. :-)

I'm aware that a DLL can't detect any process shutdown (it might have been unloaded long before the process terminates) but since the shutdown data which the DLL needs to send depends on other code in the DLL, that's acceptable. I'm basically looking for the latest moment at which I can safely perform network IO.

Does anybody know how to do this?

A: 

I suggest taking option 3. Just do your DLL loading/unloading properly and you're fine. Calling write() should work, I can't explain why it's not in your case. Is it possible that the call fails for a different reason that is unrelated?

Does it work if you call your DLL function manually from the host app?

http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx documents a number of things that should not be done (or are destined to fail) from `DllMain`. This is because when Windows is shutting down a process, DLLs can be unloaded in any order, so it may be the case where `Ws2_32.dll` was unloaded before your DLL.
rwong
If I read the `DllMain` documentation right, only (some) functions from `kernel32.dll` (those which don't load other DLLs) can be safely called from within `DllMain`.
Frerich Raabe
@user198397 - the problem with this approach is that there is no guarantee that WInsock has not already been shutdown and/or detached from the process. Order of DLL release is not in the control of OP. @frerich needs first to identify a reliable point at which to make his sockets calls.
Steve Townsend
+2  A: 

Consider monitoring the process from a separate watchdog process.

Determining If a Process Has Exited: http://msdn.microsoft.com/en-us/library/y111seb2(v=VS.71).aspx

Tutorial: Managing a Windows Process: http://msdn.microsoft.com/en-us/library/s9tkk4a3(v=VS.71).aspx

rwong
Using a watchdog process is a good idea. I'll think about it a bit. Unfortunately, the pages which your links point to only talk about process management of foreign processes (not the current one).
Frerich Raabe
A: 

Why? Just close the socket. If that's the only close in the program, which by your description it must be, that tells the other end that this end is exiting, and you can send the process ID information at the beginning instead of the end. You shouldn't do anything time-consuming or potentially blocking in an exit hook or static destructor.

EJP
You're right. However, I was unfortunately a bit imprecise: I need to log a bit more information than just the PID. I extended my question accordingly.
Frerich Raabe
A: 

Where is Winsock being shut down using WSACleanup? You need to make sure that your I/O completes before this happens.

You should be able to work out if this is happening by placing a breakpoint on the Win32 call in Winsock2.dll. Unload of DLLs is displayed in the output in the debug window.

Steve Townsend
I verified that at least none of *my* code calls WSACleanup() before the write() call is performed. My suspicion is that some other code (maybe in some other DLL which is unloaded before my own DLL is unloaded) tears down all of the socket API before I get to flush out my data. Maybe the WinSock DLL itself is being unloaded before I get unloaded, I don't know.
Frerich Raabe
@Frerich - see edit
Steve Townsend
+2  A: 

Consider to use Windows Job Objects.

You main program (monitoring program, which will use for example send()) can start child process suspended, place it into a Job and then resume. Then it will run in the job object. You can register notification via SetInformationJobObject with JobObjectAssociateCompletionPortInformation. Then you will be notified if in the job will be created some child process and if some process inside of job will be ended. So you will be able to send all what you need from the monitoring process. If you debug a program in Visual Studio it uses also job objects to have control under your process and all child processes which you start.

I successfully use the technique in C++ and in C#. So if you will have some problem with implementation I could post you a code example.

Oleg