If I duplicate a handle, can I keep using the duplicate after closing the original?


A customer asked whether it was okay to use a duplicated handle even after the original handle was closed.

Yes. That's sort of why you would duplicate it.

Duplicating a handle creates a second handle which refers to the same underlying object as the original. Once that's done, the two handles are completely equivalent. There's no way to know which was the original and which is the duplicate. Either handle can be used to access the underlying object, and the underlying object is not torn down until all handles to it have been closed.

One tricky bit here is that since you have two ways to refer to the same thing, changes made to the object via one handle will be reflected when observed through the other handle. That's because the changes you're making are to the object itself, not to the handle. For example, if you duplicate the handle to an event, then you can set the event via either handle.

That may all sound obvious, but one thing to watch out for is the case of file handles: The current file position is a property of the file object, not the handle. Say you duplicate a file handle and give the original to one component and the duplicate to another. Now, when either component reads from or writes to the file, it's going to change the current position of the file object, and consequently may confuse the other component (who may not have expected the current position to be changing). Also, if the underlying file is a synchronous file handle, the file operations on the underlying file will be synchronized. If one component starts a read, the other component won't be able to access the file object until that read completes.

If you want to create a second handle to a file that has its own file pointer and is not synchronized against the first file handle, you can use the Re­Open­File function to create a second file object with its own synchronization and its own file position, but which refers to the same underlying file.

(Don't forget to get your sharing modes right! The second file object's access and sharing modes must be compatible with access and sharing modes of the original file object. Otherwise the call will fail with a sharing violation.)

Comments (10)
  1. Joshua says:

    I've only duplicated handles with the intent to keep the original with pipes.

  2. alegr1 says:

    One thing separate for duplicated handles is a file lock. At least when the duplication happens as a result of handle inheritance. The LockFile documentation doesn't say what happens for explicitly duplicated handles.

  3. Jeff says:

    I duplicate handles to make the object easier to hold…

    …better weight distribution and all that.

  4. dave says:

    >One thing separate for duplicated handles is a file lock.

    LockFile allegedly acquires the range lock 'for the calling process'.

  5. Joshua says:

    > LockFile allegedly acquires the range lock 'for the calling process'.

    Since when are duplicated handles guaranteed to be in the same process?

  6. John Doe says:

    @Joshua, I've done the same for the standard handles in the same process.

    Since there's no way to open the console for overlapped or asynchronously IO (the console is not actually a file-like kernel object), and since the standard handles, console or not, are synchronous for historical reasons, you have to delegate console reads to another thread to avoid blocking.  Duplicating means you can use _open_osfhandle, if you need to use some higher-level reader linked with the C runtime that will _close the C file descriptor when it finishes, or in fact any function of any framework on which you must provide file-like thing (based on the duplicated handle) that gets closed.

  7. Myria says:

    DuplicateHandle is why the PROCESS_DUP_HANDLE permission is like PROCESS_VM_WRITE in that it allows god access to a process: you can duplicate a process's GetCurrentProcess handle (reinterpret_cast<HANDLE>(-1)) to make a full-access handle to another process.

    Is WSADuplicateSocketW the proper way to duplicate a socket, even when it's known to be a kernel handle?  And does WSADuplicateSocketW work on the same process?

  8. alegr1 says:

    @Myria:

    WinSockets state that the handles cannot (should not) be duplicated to another process. I think it's because of using completion ports.

  9. Harry Johnston says:

    @alegr1: you can duplicate socket handles across processes using WSADuplicateSocket, you just can't use DuplicateHandle.  The documentation says "A socket handle should not be used with the DuplicateHandle function. The presence of layered service providers (LSPs) can cause this to fail and there is no way for the destination process to import the socket handle."

    As a side note, and to provide some context, socket handles are surprisingly complicated beasts – for a start, there are two completely different kinds.  From what I've gathered, IFS socket handles put the transport service provider directly in the driver stack, whereas with non-IFS socket handles the file system drivers have to call the provider on the user's behalf if you use non-winsock I/O functions such as ReadFile.  Apparently that requires a transition back to user mode, impacting performance, which is why "it is recommended that socket handles be used only with Winsock functions."

  10. KJK::Hyperion says:

    ReOpenFile doesn't work with pipes: a re-open counts as a different connection on the same pipe, it will not result in two file objects sharing the same connection. This means you can't do overlapped I/O on an anonymous pipe, because CreatePipe always opens both ends of the pipe in synchronized mode

    DuplicateHandle safely works with sockets IF you are absolutely sure that the socket's provider stack is entirely composed of kernel mode (IFS) components. I'm not sure what happens if you try to re-open a socket, though (never tried)

Comments are closed.