-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
From @poizan42 on June 13, 2018 4:6
When I run the following code I get a System.ArgumentException: ''handle' has already been bound to the thread pool, or was not opened for asynchronous I/O.'
This seems weird since it is a copy of the handle bound and it is definitely opened for async I/O. From a glance it looks like the error ultimately comes from CreateIoCompletionPort, but that is weird because the documentation implies that you can use DuplicateHandle to share a handle registered to an IO completion port:
It is best not to share a file handle associated with an I/O completion port by using either handle inheritance or a call to the DuplicateHandle function. Operations performed with such duplicate handles generate completion notifications. Careful consideration is advised.
Is the framework doing something funky here, or is the reality more complicated than the documentation for CreateIoCompletionPort would lead you to think?
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace NamedPipeDuplicateHandleTestCore
{
class Program
{
[DllImport("kernel32.dll", EntryPoint = "DuplicateHandle", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool _DuplicateHandleSU(IntPtr hSourceProcessHandle,
SafeHandle hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
public static IntPtr DuplicateHandle(SafeHandle hSourceHandle, IntPtr hTargetProcessHandle)
{
const int DUPLICATE_SAME_ACCESS = 0x00000002;
IntPtr targetHandle;
if (!_DuplicateHandleSU(Process.GetCurrentProcess().Handle, hSourceHandle, hTargetProcessHandle, out targetHandle,
0, false, DUPLICATE_SAME_ACCESS))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return targetHandle;
}
static void Main(string[] args)
{
string pipename = "foopipe" + Guid.NewGuid().ToString("N");
var pipeServer = new NamedPipeServerStream(pipename, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous,
4096, 4096);
pipeServer.BeginWaitForConnection(ar => { pipeServer.EndWaitForConnection(ar); }, null);
IntPtr handle2;
using (var clientStream1 = new NamedPipeClientStream(".", pipename, PipeDirection.InOut, PipeOptions.Asynchronous, TokenImpersonationLevel.None,
System.IO.HandleInheritability.None))
{
clientStream1.Connect();
handle2 = DuplicateHandle(clientStream1.SafePipeHandle, Process.GetCurrentProcess().Handle);
}
var clientStream2 = new NamedPipeClientStream(PipeDirection.InOut, true, true, new SafePipeHandle(handle2, true));
pipeServer.WriteAsync(new byte[] { 42 }, 0, 1);
Console.WriteLine(clientStream2.ReadByte());
}
}
}This is tested on dotnet core 2.1.30 and Windows 10.0.17686.1003.
Copied from original issue: dotnet/coreclr#18450