Search code examples
c#wcfiisnamed-pipesself-hosting

WCF Service using named pipes causes blocking thread when hosted from shared directory


I'm building a webapplication that contains an internal self-hosted WCF service with Named Pipes endpoint. This works great, except whenever I want to host this webapplication on IIS, using a shared UNC virtualpath ('\server\share') in combination with physicalpathcredentials. Whenever I attempt to run this situation, I get an error:

Error: System.ServiceModel.EndpointNotFoundException: 'There was no endpoint listening at net.pipe://localhost/futurama/pid4988/instance2 that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.'
    AddressAccessDeniedException: The pipe name could not be obtained for net.pipe://localhost/futurama/pid4988/instance2.
    PipeException: The pipe name could not be obtained for the pipe URI: Unrecognized error 5 (0x5)

Is suspect this is a credentials/rights-issue, but I cannot figure out how to fix it. Does anyone have an idea?

I've simplified the problem in a sample, and whenever I start this sample, the thread simply blocks, and the process uses 25% of my processor.

The Global.asax file:

public class Global : System.Web.HttpApplication
{
    public static ClientProvider<ITestService> Provider { get; set; }

    protected void Application_Start(object sender, EventArgs e)
    {
        Provider = new ClientProvider<ITestService>(typeof(TestService));

        using (var client = Provider.GetClient())
        {
            string result = client.Proxy.SayHello("world");

            // this is never reached :(
        }
    }

    protected void Application_End(object sender, EventArgs e)
    {
        Provider.Dispose();
    }
}

The internal service:

[ServiceContract]
public interface ITestService
{
    [OperationContract]
    string SayHello(string name);
}

public class TestService : ITestService
{
    public string SayHello(string name)
    {
        return string.Format("Hello, {0}", name);
    }
}

The selfhosting class:

public class ClientProvider<T> : IDisposable
{
    protected ServiceHost ServiceHost { get; set; }

    public string Address { get; protected set; } = "net.pipe://localhost/test/instance";

    public ClientProvider(Type implementingType)
    {
        ServiceHost = new ServiceHost(implementingType);
        System.ServiceModel.Channels.Binding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
        ServiceHost.AddServiceEndpoint(typeof(T), binding, Address);
        ServiceHost.Open();
    }

    internal Client<T> GetClient()
    {
        var client = new Client<T>(this, Address.ToString());

        return client;
    }

    internal void Close()
    {
        ServiceHost.Close();
    }

    public void Dispose()
    {
        Close();
    }
}

The client that provides a proxy:

public class Client<T> : IDisposable
{
    public T Proxy { get; protected set; }

    protected ChannelFactory<T> Factory { get; set; }

    protected ClientProvider<T> Provider { get; set; }        

    internal Client(ClientProvider<T> provider, string address)
    {
        Provider = provider;

        System.ServiceModel.Channels.Binding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);

        EndpointAddress endpointAddress = new EndpointAddress(address);
        Factory = new ChannelFactory<T>(binding, endpointAddress);
        Proxy = Factory.CreateChannel();
    }

    public void Dispose()
    {
        IClientChannel channel = Proxy as IClientChannel;
        try { channel.Close(); }
        catch { channel.Abort(); }
        try { Factory.Close(); }
        catch { Factory.Abort(); }
    }
}

Does anyone know why IIS with a UNC virtual path messes this up?


Solution

  • In the end, we fixed this problem bij setting a Impersonationcontext to temporarily undo any impersonation:

    WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
    if (!currentIdentity.IsSystem && currentIdentity.ImpersonationLevel == TokenImpersonationLevel.Impersonation)
    {
        impersonationContext = WindowsIdentity.Impersonate(IntPtr.Zero);   //revert to previous identity (apppool identity)
    }
    

    This seemed to solve our problems.