WCF Manual Service Hosing on Windows Vista and Windows 7

With the new security features introduced in Windows Vista and now in Windows 7 there are some, not so obvious, problems that have crept in and cause weird errors in even simple code. I was building a simple web service for the first time a simple HelloWorld in a console application to make it easy to debug and output trace information. Nothing complicated, a little different because I was configuring everything manually in code as opposed to using configuration file. Here’s the code for my simple, stripped down HelloWorld service:

ConsoleServer.cs
  1.  
  2. using System;
  3. using System.ServiceModel;
  4.  
  5. namespace ConsoleServer
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. Console.Write("Server...");
  12.  
  13. ServiceHost serviceHost = new ServiceHost(typeof(HelloWorldService),
  14. new Uri("http://localhost:9000/TestService"));
  15.  
  16. serviceHost.AddServiceEndpoint(
  17. typeof(IHelloWorldService),
  18. new WSHttpBinding(),
  19. "");
  20.  
  21. serviceHost.Open();
  22.  
  23. Console.WriteLine("Started");
  24.  
  25. Console.ReadLine();
  26. }
  27. }
  28.  
  29. //operation contract
  30. [ServiceContract]
  31. public interface IHelloWorldService
  32. {
  33. [OperationContract]
  34. string HelloWorld();
  35. }
  36.  
  37. public class HelloWorldService : IHelloWorldService
  38. {
  39. public void HelloWorld()
  40. {
  41. System.Threading.Thread.Sleep(1000); //sleep for one second
  42. return "Hello World Service";
  43. }
  44. }
  45. }
  46.  

And the client:

ConsoleClient.cs
  1.  
  2. using System;
  3. using System.ServiceModel;
  4. using ConsoleServer;
  5.  
  6. namespace Client
  7. {
  8. class Program
  9. {
  10. static void Main(string[] args)
  11. {
  12. Console.WriteLine("Client");
  13.  
  14. ChannelFactory<IHelloWorldService> myChannelFactory =
  15. new ChannelFactory<IHelloWorldService>(
  16. new WSHttpBinding(),
  17. new EndpointAddress
  18. ("http://localhost:9000/TestService"));
  19. IHelloWorldService wcfClient = myChannelFactory.CreateChannel();
  20.  
  21.  
  22. System.Threading.Thread.Sleep(3000);
  23. Console.WriteLine("Calling Hello World");
  24. for (int i = 0; i < 5; i++)
  25. {
  26. Console.WriteLine(wcfClient.HelloWorld());
  27. Console.WriteLine("- Called HelloWorld");
  28. }
  29.  
  30. Console.WriteLine("Done");
  31.  
  32. Console.ReadLine();
  33.  
  34.  
  35. }
  36. }
  37. }
  38.  

The code is fairly straight forward as far as services go, running it however I began getting the following error:

HTTP could not register URL http://localhost:9000/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details).

Extremely unhelpful, and the link provided by Microsoft offers no solutions (insert eye roll here) so I went off to consult Google… After a bit of research I discovered that it was the security features in Window 7 and Windows Vista that had been causing the problem, mostly because I’m trying to do everything manually in code some of the permission requests or other behind the scenes magic that normally occurs when Visual Studio 2008 creates and hosts a service from an application configuration file don’t happen. (My guess is that when visual studio installs it sets up permissions for Cassini, it’s inbuilt web server, and that when you run your service the ‘normal’ way that it automatically wraps your service in Cassini, effectively bypassing the normal application security measures, but that’s another story). Long and the short of it is that if you want to do this yourself you need to add some permissions to your user (by default even administrator accounts in Windows 7 and I *think* Windows Vista run in a restricted mode) to allow you to register your service with windows. (The original article is here)

Run a Command Prompt with Administrator privileges (Actually right click and select it), type and run the following command (replace the port number with whatever port you are using, 9000 for the code above, and your domain(or computername if your not on a domain) and username).

netsh http add urlacl url=http://+:8000/ user=DOMAIN\UserName

And *BAM* everything magically works. Cool huh?

– Paul Rohde