Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
397 views
in Technique[技术] by (71.8m points)

.net - 3rd party app breaks our WCF application

Our application uses WCF over named pipes to communicate between two processes (note: neither process is a Windows Service.) Our application has been running in the field without incident for a couple of years now.

We are now receiving reports that the presence of a third party application (specifically, Garmin Express) is breaking ours. I've installed Garmin Express in house and confirmed the behavior. Specifically the "Garmin Core Update Service", when running, causes our application to fail.

When the Garmin service is running, the "service" side of our application starts and has no problems creating the WCF endpoint. But when the client starts and attempts to connect to the service, it fails with EndpointNotFoundException, as if the service is not even running.

At this point, I can literally stop the Garmin service from the Services control panel, then re-run the client successfully without even restarting our own service. If I start the Garmin service again, further attempts to start the client fail. So this at least proves that our WCF service is up and running the whole time, and the Garmin software is somehow blocking our client's ability to connect to it.

We are using our own name for the endpoint address (e.g. something like "net.pipe://localhost/MyPrivateApplication"). I've tried changing this address to various other names, but that has no effect on the issue.

How can another application, simply by running, break our own application's ability to use WCF?

Update: by request, here is a code snippet from the service side. I've simplified it from our original code in an attempt to isolate the issue. So far, not a single change I've made has had any effect on the issue.

MyService service = new MyService();
ServiceHost host = new ServiceHost(service);
string hostAddress = new Uri("net.pipe://localhost/MyWCFConnection");
host.AddServiceEndpoint(typeof(IMyService), new NetNamedPipeBinding(), hostAddress);
host.Open();
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

One possible answer to your question "How can another application, simply by running, break our own application...":

  1. The other application also uses the WCF NetNamedPipeBinding.
  2. Both applications create service endpoints using base + relative URLs.
  3. The applications' choice of base address, and HostNameComparisonMode is such that there is a name collision between the applications on one of the URL variants used by the client-side WCF stack to locate the metadata for the service.

I have no idea whether the Garmin service actually does use WCF NetNamedPipeBinding, but this is one possibility you should investigate. The problem can be avoided by always using absolute URLs for NetNamedPipe endpoints.


OK, so following the updates to the question, we now know that the Garmin service is using WCF NetNamedPipeBinding, and we know that your application registers its service using an absolute address, so the above explanation is not the complete story.

Here is another hypothesis:

  1. Suppose the Garmin service runs in a process which has the security privilege SeCreateGlobalPrivilege (which a Windows service will have unless specially coded to disable the privilege).
  2. Suppose it also registers its WCF Named Pipe endpoint with a base address of net.pipe://localhost and relative endpoint addresses.
  3. Now its service metadata will be published using a shared memory mapping object with a name in the Global namespace.
  4. Your service application is not a Windows service. My hypothesis is that its process does not have the security privilege SeCreateGlobalPrivilege. If this is the case, its service metadata will be published using a shared memory mapping object in its Local session namespace only.
  5. Now your client process tries to initiate a connection when the Garmin service is running... the WCF client-side channel stack NetNamedPipeBinding element tries to locate the service metadata for your service based on your service URL net.pipe://localhost/MyWCFConnection. As explained in the link above, it will execute the search using various variants of the service URL in turn to derive a name for the shared memory object containing the metadata. It looks in the Global namespace first, for the full list of variants in turn, before looking into the Local namespace.
  6. In this case, the first attempt will be for the name derived from "net.pipe://+/MyWCFConnection", and presumably it fails to find an object with this name in the Global namespace.
  7. However, the second attempt will be based on the variant "net.pipe://+/", and this will match the name of the Garmin service's shared memory mapping published in the Global namespace. Because of the search order, it will never get to your service's metadata published in the Local session namespace.
  8. Your client attempts to connect to the Garmin service's pipe. Let's assume the Garmin service has some security implemented which results in your client being rebuffed with an Access Denied (e.g. it may set an ACL on its pipe). The result might well surface as an EndpointNotFoundException. [LATER EDIT: Actually, most likely what is happening is that your client is actually connecting to the Garmin service, initiating the framing protocol's preamble handshake, and receiving back a framing protocol fault (http://schemas.microsoft.com/ws/2006/05/framing/faults/EndpointNotFound) because the URL requested in the Via record won't match what the Garmin service is expecting. The binding is then dropping the connection and surfacing this fault to your client code as an EndpointNotFoundException.]

What can you do about it? I would suggest:

  • If the above hypothesis or something similar can be confirmed, and Garmin are using base+relative addressing with a base of just net.pipe://localhost, best would be to get them to own the problem: they could fix such a problem very easily by changing their base address to something more likely to be unique.
  • You could perhaps work around it by finding some way for your service application to run with the security privilege SeCreateGlobalPrivilege: this isn't easy without making it a Windows service or running As Administrator, but maybe not impossible. Then your metadata would also be published in the Global namespace and the client's search would find it before Garmin's.
  • [Later edit] Maybe there is a workaround involving setting the HostNameComparisonMode property of the binding to Exact, and using a synonym for localhost as the host part of the service URL (e.g. net.pipe://127.0.0.1/MyWCFConnection). This may steer the search around the Garmin variants so that your client has a chance to consider names in the Local session namespace. I don't know that it will work, but worth a try, I would have thought.
  • And a very long shot: Does your company have a product support relationship with Microsoft? Arguably this is a serious design flaw in WCF: if you make a fuss about it you might possibly get Microsoft to issue a QFE patch for it e.g. to provide a binding property to tell the client-side stack to only try the Local namespace.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...