I've created a simple code to allow cross-appdomain communication using WCF and NamedPipes. I'm testing the code on my Windows 8.1 and it is causing a EndpointNotFoundException.
Here is my code:
Service Contract
[ServiceContract(Namespace = "http://PoC.AppDomainWCF")]
public interface ICrossAppDomainSvc
{
[OperationContract]
bool HasPermission(String User, String Permission);
}
Program.cs (WinForms)
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread thread = new Thread(new ThreadStart(StartService));
thread.Start();
Application.Run(new Form1());
}
static void StartService()
{
using (ServiceHost host = new ServiceHost(typeof(CrossAppDomainSvc), new Uri[] {
new Uri("http://localhost:12000/AppDomainWCF/"),
new Uri("net.pipe://localhost/")
}))
{
var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
host.AddServiceEndpoint(typeof(ICrossAppDomainSvc), binding, "CrossAppDomainSvc");
// Add a MEX endpoint
//ServiceMetadataBehavior metadataBehavior = new ServiceMetadataBehavior();
//metadataBehavior.HttpGetEnabled = true;
//metadataBehavior.HttpGetUrl = new Uri("http://localhost:12001/AppDomainWCF");
//host.Description.Behaviors.Add(metadataBehavior);
host.Open();
}
}
}
Client code
NetNamedPipeBinding binding = new NetNamedPipeBinding();
ChannelFactory<ICrossAppDomainSvc> channelFactory = new ChannelFactory<ICrossAppDomainSvc>(binding);
EndpointAddress endpointAddress = new EndpointAddress("net.pipe://localhost/CrossAppDomainSvc");
ICrossAppDomainSvc service = channelFactory.CreateChannel(endpointAddress);
MessageBox.Show(service.HasPermission("Juliano", "XPTO").ToString());
The exception is thrown at the service.HasPermission call.
What is wrong with my code?
UPDATE
As the question has been answered and my proof-of-concept is working, I've created a repository on GitHub to help anyone who needs to allow cross appdomain communication.
CrossAppDomainWCF sample code
You open your ServiceHost and immediately close it. serviceHost.Open() method doesn't block. So your endpoint doesn't exist because host is already closed when you are connecting
Related
If I use this code for self Host a WCF service in a Console application it works. I run the host app and then from another app (which I call the client app,) I can add the service reference from visual studio > solution explorer > ADD SERVICE REFERENCE > http://10.131.131.14:8080/sendKioskMessage > click GO, add the service with no problems and consume it from the client app (which is a windows form)
But if I run the same code in a Windows Form, I run first the (SELF HOST WCF) windows form app, then from the other app (client app) in visual studio I try to add the service reference from ADD SERVICE REFERENCE in solution explorer (Just the same way that it works before but with the Console App self host) but it throws the following error:
*
An error (Details) occurred while attempting to find services at
http://10.131.131.14:8080/sendKioskMessage.
(If I click Details Link, says the following:)
There was an error downloading
'http://10.131.131.14:8080/sendKioskMessage/$metadata'. Unable to
connect to the remote server. Metadata contains a reference that
cannot be resolved: 'http://10.131.131.14:8080/sendKioskMessage'.
There was no endpoint listening at
http://10.131.131.14:8080/sendKioskMessage that could accept the
message. This is often caused by an incorrect address or SOAP action.
See InnerException, if present, for more details. Unable to connect to
the remote server. If the service is defined in the current solution,
try building the solution and adding the service reference again.
*
The IP that I use is the IP of my pc where both apps are running. I also used localhost instead of my actual IP with the same result.
Windows Form Code (can't add the service from another app):
public partial class KioskosServerForm : Form
{
[ServiceContract]
public interface IKioskMessageService
{
[OperationContract]
string SendKioskMessage(string message);
}
public class KioskMessageService : IKioskMessageService
{
public string SendKioskMessage(string message)
{
return string.Format("Message sent: {0}", message);
}
}
public KioskosServerForm()
{
InitializeComponent();
}
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
}
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
}
Console App Code (Works! I can add the service from other client app):
[ServiceContract]
public interface IKioskMessageService
{
[OperationContract]
string SendKioskMessage(string message);
}
public class KioskMessageService : IKioskMessageService
{
public string SendKioskMessage(string message)
{
return string.Format("Message sent: {0}", message);
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/sendKioskMessage");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService),baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
I don't know why I can consume the service if the service is self hosted in a console app, but I can't add it if the service is self hosted in a Windows Form.
I will appreciate a lot your help to achieve this from a Windows From, since I need to self host the WCF service from a windows form, no a console app.
I'm using Visual Studio 2017, .Net Framework 4.6.1
THANKS IN ADVANCE GUYS!!
TL;DR the console app works because you have a delay before shutting down the service; the WinForms host doesn't
The reason your console WCF host service works is that you start the hosting and continue until the Console.ReadLine() line:
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine(); // <-------- program waits here
// Close the ServiceHost.
host.Close();
...after which the service is torn down. Prior to that, your other clients can connect fine and add Service References.
The WinForms app has no such delay:
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open(); // <------ opened here
} // <------ shutdown here
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
...it is immediately shutdown when the code goes out of scope of the using block. The using will automatically call Dispose() on the host object which in turn calls Close().
Consider placing the host into a variable like so:
ServiceHost _host; // <---------- new!
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
_host = new ServiceHost(typeof(KioskMessageService), baseAddress))
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
_host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
_host.Open();
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
Later, you can close the _host instance with a call to Close.
Question: Is there a way how to quickly check whether particular pipename is being hosted in session 0 - preferabely during the ServiceHost.Open call?
Scenario:
Two processes PipeHost and PipeUser are trying to communicate on the system via pipe with name PeacePipe. They are not required to be started with special privileges.
PipeHost is started and hosts the PeacePipe without any problem.
PipeUser is started and connects to PeacePipe without any problem.
PipeUser tries to comunicate to PipeHost via PeacePipe, it sends messages but PipeHost doesn't see anything.
In fact PipeUser connected to DifferentPipeHostInSession0 that is hosting pipe with same name (but OS creates different pipe) in an elevated (or service) process.
Background:
ServiceHost.Open should throw AddressAlreadyInUseException when the selected pipename is already being hosted.
However it's not thrown if the pipe is hosted in session 0 and you are attempting to host the same pipe in different sessions. As windows named pipes are normally not te be used accross sessions. With the exception of pipe hosted in session 0. Any process can connect to such a pipe. This can lead to the above sceanrio.
Code:
[ServiceContract]
public interface IService
{
[OperationContract]
void Ping();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, IncludeExceptionDetailInFaults = true)]
public class Service: IService
{
public void Ping()
{
Console.WriteLine("Client Pinged me");
}
}
private static readonly Uri _pipeUri = new Uri("net.pipe://localhost/aaa");
private static readonly Binding _pipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
static void PipeHostTest()
{
ServiceHost serviceHost = new ServiceHost(new Service(), _pipeUri);
serviceHost.AddServiceEndpoint(typeof(IService), _pipeBinding, "");
try
{
//Fail here if same pipe already opened - even in Global space
serviceHost.Open();
Console.WriteLine("OPENED");
}
catch (AddressAlreadyInUseException e)
{
Console.WriteLine(e);
throw;
}
Console.ReadKey();
}
static void PipeClient()
{
ChannelFactory<IService> channelFactory =
new ChannelFactory<IService>(_pipeBinding, new EndpointAddress(_pipeUri));
var proxy = channelFactory.CreateChannel();
proxy.Ping();
}
static void Main(string[] args)
{
if (args.Any())
{
PipeClient();
}
else
{
PipeHostTest();
}
}
Run once without parameters elevated, once without parameters non-elevated. Both processes will host pipe with same name - but those are different pipes.
Then run once with any parameter. Client process will conect to the pipe hosted by elevated process.
Possible Solution:
Use a named mutex in global session new Mutex(true, "Global\\MutexForMyPipeName", out createdNew) to see if there is another process trying to do the same.
This however disqualifies even scenarios where the pipes are in 2 different sessions that do not colide.
Preferabely the ServiceHost.Open would take care about this for me as I'm using multiple bindings types (net.tcp, net.pipe, net.udp) and have single code for creating and hosting the ServiceHost. NamedPipes are the only ones that can allow creation of new host without AddressAlreadyInUseException exception while the address is actuall already in use.
I am working on an C# WCF project and I have got it pretty much working except for quite a big but hopefully simple problem.
The WCF service is hosted from within my Console application and my console application calls a function to a different class to open the connection for the WCF service.
However, if the last line of the function is host.open(); the function call then finishes to the connection gets closed and the service can no longer be used. However, if I put Console.ReadLine() after the host.open then the service stays open and I can use it but obviously the rest of the flow of the program no longer runs.
Below is the code I am using to open the host connection.
public void startSoapServer()
{
string methodInfo = classDetails + MethodInfo.GetCurrentMethod().Name;
if (String.IsNullOrEmpty(Configuration.soapServerSettings.soapServerUrl) ||
Configuration.soapServerSettings.soapPort == 0)
{
string message = "Not starting Soap Server: URL or Port number is not set in config file";
library.logging(methodInfo, message);
library.setAlarm(message, CommonTasks.AlarmStatus.Medium, methodInfo);
return;
}
//baseAddress = new Uri(string.Format("{0}:{1}", Configuration.soapServerSettings.soapServerUrl,
// Configuration.soapServerSettings.soapPort));
baseAddress = new Uri("http://localhost:6525/hello");
using (ServiceHost host = new ServiceHost(typeof(SoapServer), baseAddress))
{
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.Opened += new EventHandler(host_Opened);
host.Faulted += new EventHandler(host_Faulted);
host.Open();
Console.ReadLine();
}
Without the Console.ReadLine() there the function finishes so the connection closes. How can I keep the host open for the duration that the C# is app is running.
This function call is called from within the Main method halfway through initiliasing some stuff within the console stuff.
Thanks for any help you can provide.
You need to declare ServiceHost at class scope instead of function scope and do not use using.
using {} will automatically Dispose the object to which it pertains and Disposal means closing. Also, since your ServiceHost is defined at function scope, it will go out of scope as soon as the function finishes and will be cleaned up by the garbage collector.
The reason that your ReadLine call is preventing the closing is because it is inside the using statement and stops the program inside the function where the variable is declared keeping it in scope.
You need to do something like this:
private ServiceHost host;
public void startSoapServer()
{
// trimmed... for clarity
host = new ServiceHost(typeof(SoapServer), baseAddress));
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.Opened += new EventHandler(host_Opened);
host.Faulted += new EventHandler(host_Faulted);
host.Open();
// etc.
You will close host when you exit the application.
I have a WCF Service interface, a class that implements the contract and a hosting winforms application. This then starts worker processes who connect back to the WCF server and then the events should be getting triggered. The client worker process is not having any issues making the calls to the methods and followed by that I am expecting the attached event handlers be called within the Windows forms application too but this is not happening:
xWCFService xWCFService = new xWCFService();
xWCFService.eventWorkerProcessStart += new EventHandler<WorkerProcessProgressChangedEventArgs>(xWCFService_eventWorkerProcessStart);
xWCFService.eventWorkerProcessStop += new EventHandler<WorkerProcessProgressChangedEventArgs>(xWCFService_eventWorkerProcessStop);
xWCFService.eventWorkerProcessUpdateProgress += new EventHandler<WorkerProcessProgressChangedEventArgs>(xWCFService_eventWorkerProcessUpdateProgress);
xWCFService.eventWorkerProcessError += new EventHandler<WorkerProcessProgressChangedEventArgs>(xWCFService_eventWorkerProcessError);
ServiceHost xServiceHost = new ServiceHost(xWCFService, new Uri(serviceAddress));
xServiceHost.AddServiceEndpoint(typeof(IxWCFServiceContract), new NetTcpBinding(), address);
xServiceHost.Open();
I am passing the instance of the Service class to the servicehost and it is a singleton instance. I appreciate any help/insight that can be provided on why I'm not referencing the correct instance.
After much reading I have noticed my mistake in the client side code:
static xWCFService xwcfService = new xWCFService();
....
{
EndpointAddress endPoint = new EndpointAddress(new Uri(string.Format(xWCFServerBaseAddress, address) + address));
Binding binding = new NetTcpBinding();
xChannelFactory = new ChannelFactory<IxWCFServiceChannel>(binding, endPoint);
xChannelFactory.Open();
xServiceChannel = xChannelFactory.CreateChannel();
xServiceChannel.Open();
**xwcfService.WorkerProcessStartedParsing(strGuidClientIdentifier);**
This last line was my mistake, I was invoking the calls on the service through an instance of the service implementation class. When I used the xServiceChannel to invoke the methods on the service, all events were raised.
I have implemented a simple chat console app and it worked well. When i tried to apply the same concept on GUI app. the service side when hosting , there is any error but if i use CMD command netstat -ao to show all ports , it is not exists.So when i run client app , there is an Exception (No connection could be made because the target machine actively refused). How can i solve these problem ?
Server
ServiceHost host;
using (host = new ServiceHost(typeof(Service), new Uri("net.tcp://localhost:4111")))
{
host.AddServiceEndpoint(typeof(IService), new NetTcpBinding(), "IService");
try
{
host.Open();
}
catch
{
}
}
Client
public bool Connect()
{
DuplexChannelFactory<IService> pipeFactory = new DuplexChannelFactory<IService>(new InstanceContext(this),
new NetTcpBinding(),
new EndpointAddress(AppConfiguration.GetValue(net.tcp://localhost:4111/IService"));
try
{
pipeProxy = pipeFactory.CreateChannel();
if (pipeProxy.Register())
{
return true;
}
}
catch
{
}
return false;
}
Assuming you are showing all your code.
You need to add a line after host.Open();, you could add Console.ReadLine();
This would get the program to stop from existing. What happens now is that the host opens, then the program exists, and the host gets closed/garbage collected.
I have solve it.
In GUI remove the (using) at defined new ServiceHost. Why i don't know but it works!!!
ServiceHost host;
host = new ServiceHost(typeof(Service), new Uri("net.tcp://localhost:4111"));