http://craz.net/programs/ZeroconfNetServices/
A most generous soul named David Hammerton had created a "wrapper around Apple's mDNS Windows library (dnssd.dll) which provides two .NET classes, very similar to Cocoa's NSNetService and NSNetServiceBrowser". Since we were already familiar with Cocoa's classes, this was even easier for us.
And after many months of working with the library, and slowly testing it, and improving it, we're happily releasing an updated version of it today!
We've added several things, such as the ability to update TXT records after publishing, and the ability to monitor TXT records for updates. We also improved the speed of resolving a service, fixed a few bugs, and added some minor improvements. (The download also contains a detailed change log.) The result is a library that's almost a replica of what's been available on every Mac since 2002. :)
Download it HERE
The download comes as a zipped visual studio solution. Inside are 3 projects. 1 is the actual Zeroconf dll library. The other is a sample app to publish a service, and also includes code to later update the TXT record. The third is a sample app to browse for services, resolve services, and monitor TXT records for updates.
The solution will open just fine using the free Visual C# express. Also, I've left the compiled versions in their respective build\Release folders just in case you don't feel like compiling.
To use these, you will need to install Bonjour for Windows.
These changes have also been reported to the original author. If you have questions regarding the code, you can contact either the original author, or us.
PS - for those who don't know, Zeroconf (zero configuration networking) is a technology that allows you to automatically discover services on a local network. It's what iTunes uses to automatically find other iTunes shares. Apple's named it's particular implementation Bonjour.
-- UPDATE --
The code now has it's own Google Code Project. Check it for the latest updates.
-- UPDATE 2 --
The project now has it's own Google Groups Mailing List. Please use the mailing list for questions, comments and general troubleshooting.
46 comments:
Can you say what kind of license this code falls under?
The original code was released under public domain. This is also the case for this code. You're free to do whatever you want with it.
Great, thanks a bunch! Just today I was working on the same thing. But if this does the job, I shouldn't re-invent it.
Nice work, just what I was looking for. Many thanks!
not sure if im just being stupid or not,... this works great, but how do i get the socket connection so i can actually send data over the connection?
Hi action,
You're not being stupid, it's an honest question and I can see why you're asking it. Bonjour will only tell you the IP:port of the service. Sometimes this is all people need. Other times people want to connect - but of course the way they connect will depend on the protocol (eg. TCP, UDP, etc). In other words, Bonjour doesn't create the socket for you, it's just a way to discover services.
But don't worry, it's really easy to connect once you know the address. In the DidResolveService callback, just use the service.Addresses property to get a list of System.Net.IPEndPoint. There may be multiple items in here, including IPv4 and IPv6. Then just use the IPEndPoint of your choice to connect in whatever manner is appropriate for your service.
If you don't support IPv6, you can check for AddressFamily == AddressFamily.InterNetwork.
thanks for the quick response, i'll give this a shot!
Thank you, absolutely great work! I've been using the original library the last few days and it has some strange bugs in it which caused me some headaches. But your improved version works like a charm, very fast! Thanks :-)
Thanks a mint! Saved me a lot of time
what do you recommend as the best way to detect if bonjour/mDNSresponder is installed/running on the machine? in our app, the bonjour discovery is an 'extra' feature that is not required/necessary, so we dont want to fail if it is not available, but use it if it is.
so far, i have been using code like the code below, but it seems to result in sporadic CallbackOnCollectedDelegate MDA warnings being generated (i am assuming because i stop/destroy the NetServiceBrowser before the underlying unmanaged code makes the callbacks when bonjour is enabled):
try
{
NetServiceBrowser nsb = new NetServiceBrowser();
nsb.SearchForBrowseableDomains();
nsb.Stop();
nsb = null;
isSupported = true;
}
catch
{
Console.WriteLine("Bonjour is not supported");
isSupported = false;
}
Hi Brian,
It's been my experience that an exception would be thrown the very first time any method in mDNSImports with "[DllImport("dnssd.dll")]" is called. In the case of the code you posted, I would have expected it to barf on the call to "mDNSImports.DNSServiceEnumerateDomains", which happens in "SearchForBrowseableDomains".
Another possibility is to simply search for the dnssd.dll within the system folder to detect if bonjour is installed. I believe this is also the recommended way of detecting bonjour via installers.
you are right - when Bonjour is *not* installed, it fails right away, the exception is caught, and the code works as intended.
the problem is when Bonjour *is* available. at that point, the dnssd.dll is found and the SearchForBrowsableDomains methos completes successfully. however, i dont really want to search for any domains (at this point - it is just a detection feature), so when i .Stop() the browser and/or set it to null, the internal delegates go out of scope and that is when the runtime complains.
i think i will try just looking for the dnssd.dll, but i wasnt sure if it could be in varying locations. alternatively, i have implemented another dnssd.dll method (DNSServiceGetProperty) that simply tries to return the version of the service. it still invokes the dll, so i can check for failure, but avoids any delegate dependencies.
Ah, I see. Sorry I misunderstood. I will take a look at the bug and try to fix it soon.
"i have implemented another dnssd.dll method (DNSServiceGetProperty) that simply tries to return the version of the service."
Let me know if you'd be willing to submit this, as I'd be interested in adding this method to the project. Also, I'd be happy to add you as a developer to the project so you'd be able so commit such improvements and fixes.
well, i wouldnt say i am much of a interop guru, so i struggled getting the parameters just right, but here is what i came up with. it works, but i wouldnt plop it in there as-is until someone with more knowledge than i took a look at it:
in mDNSImports:
[DllImport("dnssd.dll")]
public static extern DNSServiceErrorType DNSServiceGetProperty(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]String name,
ref IntPtr result,
ref UInt32 size);
then in DNSService (or where ever):
public int GetVersion()
{
IntPtr result = IntPtr.Zero;
try
{
int version = 0;
int size = Marshal.SizeOf(typeof(UInt32));
result = Marshal.AllocCoTaskMem(size);
UInt32 rsize = (UInt32)size;
DNSServiceErrorType error = mDNSImports.DNSServiceGetProperty(ZeroconfService.DNSServiceProperty.DaemonVersion, ref result, ref rsize);
if (error != DNSServiceErrorType.kDNSServiceErr_NoError)
throw new DNSServiceException("DNSServiceGetProperty", error);
version = result.ToInt32();
return version;
}
finally
{
if (result != IntPtr.Zero) Marshal.FreeCoTaskMem(result);
}
}
there is more to do (the int returned from the function can be parsed apart to determine the major/minor version, etc, but it is a start.
Outstanding work guys! This has just made my work so much easier. Having started to struggle with Apple's antique managed C++ wrapper, I then came across your library, which works like a charm. Thanks a lot for releasing this.
Thanks for the good work.
I've been using the code from a WPF application. I created a "glue" class to attach the asynch callbacks to the GUI thread.
The class:
public class DispatcherPatcher : System.ComponentModel.ISynchronizeInvoke
{
Dispatcher dispatcher;
public DispatcherPatcher(Dispatcher dispatcher_)
{
dispatcher = dispatcher_;
}
#region ISynchronizeInvoke Members
public IAsyncResult BeginInvoke(Delegate method, object[] args)
{
throw new NotImplementedException();
}
public object EndInvoke(IAsyncResult result)
{
throw new NotImplementedException();
}
public object Invoke(Delegate method, object[] args)
{
return dispatcher.Invoke(method, args);
}
public bool InvokeRequired
{
get { throw new NotImplementedException(); }
}
#endregion
}
To use it:
nsBrowser.InvokeableObject = new DispatcherPatcher(this.Dispatcher);
Everything seems to work fine.
It works! Awesome - thanks.
One issue - I want to use this in a product for which Apple's license on the Bonjour executable isn't practical (it's an embedded app). If I uninstall Bonjour, but leave dnssd.dll on the machine or use a version of dnssd.dll built from the free source code, I get an "unknown" exception returned from mDNSImports.DNSServiceBrowse. Do I need something besides this DLL to use your library?
> If I uninstall Bonjour, but leave dnssd.dll on the
> machine or use a version of dnssd.dll built
> from the free source code
Is dnssd.dll the only thing that Bonjour installs? If you compile the library from apple's source code, isn't it still under the same terms?
I've published my service. Is there a callback that will let me know if a remote host has successfully resolved my service and is trying to connect?
I tried adding
publishService.DidResolveService += new NetService.ServiceResolved(publishService_DidResolveService);
publishService.DidNotResolveService += new NetService.ServiceNotResolved(publishService_DidNotResolveService);
But couldn't get control in the delegate methods.
Am I missing something?
Hi Mugunth,
There is no such callback in the Bonjour API. This is because it doesn't make much sense to have one. The act of resolving a service only tells someone the IP address of the service - nothing else.
Some applications that use Bonjour automatically resolve discovered services right away. The application may never actually connect though. Other applications only resolve services on demand, immediately before connecting.
In other words, knowing when another Bonjour client resolves your IP address from your published service doesn't really tell you anything useful. You may, however, want to know when another client connects to your published service. And for this, you would build such functionality into the server component.
"Is dnssd.dll the only thing that Bonjour installs?"
Actually, no - one also needs mDNSResponder. It took me a while to figure out that a "Host" in Bonjour is any resident that participates in the mDNS protocol, not just service providers, and that on a Windows Host, the mDNS "service" is (usually) implemented as a Windows service. I built the mDNSResponder and it works great. For the record, then, for those wanting to implement just service discovery, you only need to build dnssd.dll and mDNSResponder.exe. (There's a bunch of other stuff in the source distribution that's either obsolete or related to other platforms.)
Thanks again for your help.
Using the downloaded code, when I publish a service (using the publish app) I am seeing it 3 times in the browse app. Anyone else seeing this ? (and I don't have any other instances of it running).
Great sample app!. Can one send a file using this? When I try sending a pdf file.... I get an "invalid" error
When running the application as a service the application uses a lot of cpu cycles but when running directly as a standalone application it works just fine. I have created a bug report on the code site with more information about the problem.
Tope, Bonjour is simply for discovering services. Once you've discovered a service, such as an FTP server, printer, etc, then you can do something with that service.
Hi All,
I tried BrowseserviceSample in ipV6 network but it does not work (I never get a call back ns_DidResolveService). I tried the original code from author and it works. Please let me know what I need to do to get this work on IPv6 environment.
Thanks,
Phuong
I think I found the problem. The IPLookup function only scan for IPv4 address, I need to add another scan for IPv6.
Hello. I had same problem as Berndt Johansson. When running publish from service, my process where spending to much cpu time. Problem was because of the select loop. After select is unblocked client must call DNSServiceProcessResult. In sample app, this call was Invoked in async way thought Form. But in service you must set AllowMultithreadedCallbacks to true first( there is no form here :) ). If not, you will have endless select loop, and 50% of you CPU utilized :).
Someone said it above.
Bonjour will only tell you the IP:port of the service. Sometimes this is all people need. well this is all I need, does anyone know where I can get some sample code to do this?
Folks, first of all thanks for this library.
I'm unfortunately pretty bad at all that C interop stuff, and I'm getting an exception on the Marshal.FreeCoTaskMem(result) call in the GetVersion method. It only happens when running under x64 (the native call itself seems to work fine, it's just the dealloc that fails). Running this compiled specifically for x86 works. Anyone with more interop brains than me care to take a peek?
OK, so I figured out the x64 issue (I think). The external declaration should be:
[DllImport("dnssd.dll")]
public static extern DNSServiceErrorType DNSServiceGetProperty(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]String name,
IntPtr result,
ref UInt32 size);
and the relevant code in th e implementation around it should be:
DNSServiceErrorType error = mDNSImports.DNSServiceGetProperty(mDNSImports.DNSServiceProperty_DaemonVersion, result, ref size);
if (error != DNSServiceErrorType.kDNSServiceErr_NoError)
{
throw new DNSServiceException("DNSServiceGetProperty", error);
}
version = Marshal.ReadInt32(result);
The download link appears to be broken.
Please use the google code page to download the software.
David's solution above works on X64. Please add this code to the subversion trunk!
I'm running on x64 Windows 7 and I've come across all kinds of issues. I did encounter David's problem, but I had to prototype it like..
[DllImport("dnssd.dll")]
public static extern DNSServiceErrorType DNSServiceGetProperty(
[MarshalAs(UnmanagedType.LPStr)]String name,
ref UInt32 result,
ref UInt32 size);
to get around that. Passing the result as a referenced int instead of intptr, that worked fine.
Both test apps just quit without exceptions when I ran them. I swapped out all the [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))] with [MarshalAs(UnmanagedType.LPStr)] and that kept it from quit/crashing. However, I'm not getting any callbacks called by the dll. The DNSRegisterService call succeeds but the callback is never called. Any ideas? The interop prototypes look good to me.
Thanks for this code. Two comments:
It would be really cool if the changes that people have contributed in the comments here could be included in the SVN repo on google code. That would be reassuring for anyone wanting to rely on this project in production code.
Secondly, I checked out the current codebase from SVN and compiled it on Windows 7 64-bit. Bonjour is not installed. I do get the popups saying Bonjour is not installed, and I can see in the source that Application.Exit() is issued immedately afterwards. However, the code below this point is still run, resulting in an additional exception. Looking at the documentation for this Exit(), this actually makes sense. Adding a "return;" statement after Exit() fixes the problem.
I have to correct myself, and alleborate a bit.
I -did- have Bonjour for Windows installed, but it was the 32 bit version even though I have Windows 64 bit. This combination results in the DLL for DNSSD not being found, as described in the last post. If I go into the project properties for the sample projects and change the platform target from "any cpu" to "x86", I get no error and they work.
However, this is an unfortunate limitation to put on projects that want to use this, so I go ahead and install the 64-bit Bonjour. I change back to "any cpu", and now I get a hard crash with no exception. Stepping through the VS2008 debugger shows it to be in Marshal.FreeCoTaskMem(result) (DNSService.cs:98), which I guess is the same crash some other commenters got. I get the same behaviour if I change from "any cpu" to "x64", not surprisingly. The strange thing is that if I change to "x86" it now works again. Not sure why.
I guess there have been made some assumptions about pointer size in the actual ZeroconfService library, which makes it only work on 32-bit?
As of 2/24/2010 Apple now supports Bonjour for C# and .NET natively! It also supposedly works on both 32 and 64-bit Windows.
Check it out at:
http://developer.apple.com/networking/bonjour/download/
herbal viagra
viagra alternatives
Has anyone implemented then new .NET DLL?
I haev tried to import this into VS2010 C#, but withour succes as it will not accept the DLL and a compatible COM/DLL.
is it possible to use iPhone to talk to .NET, after Bonjour discovery of services?
the sockets or streams on different platforms are compatible?
sorry...noob here...
Thanks in advance~
and anybody uses the Bonjour SDK from Apple?
Thanks in advance~
Just tried Apple's bonjour. It crashed in my Console application, but worked in forms. Deusty's solution just works on my MSVC10, Win7 32 bit setup.
Hello,
I want to develop chat application for windows and mac using bonjour so please tell how it possible. plese send me sample code or hint.. thanks a lot
Great resources here guys, this is very helpful. For my service I am setting up I need to broadcast a "subtype", but right now looks like netservice only supports "type" and not specifying a "subtype", does this sound correct?
Sensational info. Your post is such a refreshing someone to read through. thanks to share.
Post a Comment