]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
mDNSResponder-87.tar.gz
[apple/mdnsresponder.git] / mDNSWindows / DNSServiceBrowser / Windows / Sources / ChooserDialog.cpp
diff --git a/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp b/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
new file mode 100644 (file)
index 0000000..0435f61
--- /dev/null
@@ -0,0 +1,1498 @@
+/*
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+
+    Change History (most recent first):
+    
+$Log: ChooserDialog.cpp,v $
+Revision 1.2  2004/07/13 21:24:26  rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.1  2004/06/18 04:04:36  rpantos
+Move up one level
+
+Revision 1.10  2004/04/23 01:19:41  bradley
+Changed TXT record new line delimiter from \n to \r\n so it works now that it is an edit text.
+
+Revision 1.9  2004/03/07 05:51:04  bradley
+Updated service type list table to include all service types from dns-sd.org as of 2004-03-06.
+Added separate Service Type and Service Description columns so both are show in the window.
+
+Revision 1.8  2004/01/30 02:56:32  bradley
+Updated to support full Unicode display. Added support for all services on www.dns-sd.org.
+
+Revision 1.7  2003/12/25 03:42:04  bradley
+Added login dialog to get username/password when going to FTP sites. Added more services.
+
+Revision 1.6  2003/10/31 12:18:30  bradley
+Added display of the resolved host name. Show separate TXT record entries on separate lines.
+
+Revision 1.5  2003/10/16 09:21:56  bradley
+Ignore non-IPv4 resolves until mDNS on Windows supports IPv6.
+
+Revision 1.4  2003/10/10 03:41:29  bradley
+Changed HTTP double-click handling to work with or without the path= prefix in the TXT record.
+
+Revision 1.3  2003/10/09 19:50:40  bradley
+Sort service type list by description.
+
+Revision 1.2  2003/10/09 19:41:29  bradley
+Changed quit handling to go through normal close path so dialog is freed on quit. Integrated changes
+from Andrew van der Stock for the addition of an _rfb._tcp service type for a VNC Remote Framebuffer
+Server for KDE support. Widened service type list to handle larger service type descriptions.
+
+Revision 1.1  2003/08/21 02:06:47  bradley
+Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder.
+
+Revision 1.7  2003/08/20 06:45:56  bradley
+Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID.
+
+Revision 1.6  2003/08/12 19:56:28  cheshire
+Update to APSL 2.0
+
+Revision 1.5  2003/07/13 01:03:55  cheshire
+Diffs provided by Bob Bradley to provide provide proper display of Unicode names
+
+Revision 1.4  2003/07/02 21:20:06  cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.3  2002/09/21 20:44:55  zarzycki
+Added APSL info
+
+Revision 1.2  2002/09/20 08:39:21  bradley
+Make sure each resolved item matches the selected service type to handle resolved that may have
+been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present.
+
+Revision 1.1  2002/09/20 06:12:52  bradley
+DNSServiceBrowser for Windows
+
+*/
+
+#include       <assert.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <time.h>
+
+#include       <algorithm>
+#include       <memory>
+
+#include       "stdafx.h"
+
+#include       "DNSServices.h"
+
+#include       "Application.h"
+#include       "AboutDialog.h"
+#include       "LoginDialog.h"
+#include       "Resource.h"
+
+#include       "ChooserDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+//     Constants
+//===========================================================================================================================
+
+// Menus
+
+enum
+{
+       kChooserMenuIndexFile   = 0, 
+       kChooserMenuIndexHelp   = 1 
+};
+
+// Domain List
+       
+#define kDomainListDefaultDomainColumnWidth                            164 
+       
+// Service List
+
+#define kServiceListDefaultServiceColumnTypeWidth              146
+#define kServiceListDefaultServiceColumnDescWidth              230
+       
+// Chooser List
+       
+#define kChooserListDefaultNameColumnWidth                             190
+#define kChooserListDefaultIPColumnWidth                               120
+
+// Windows User Messages
+
+#define        WM_USER_DOMAIN_ADD                                                              ( WM_USER + 0x100 )
+#define        WM_USER_DOMAIN_REMOVE                                                   ( WM_USER + 0x101 )
+#define        WM_USER_SERVICE_ADD                                                             ( WM_USER + 0x102 )
+#define        WM_USER_SERVICE_REMOVE                                                  ( WM_USER + 0x103 )
+#define        WM_USER_RESOLVE                                                                 ( WM_USER + 0x104 )
+
+#if 0
+#pragma mark == Constants - Service Table ==
+#endif
+
+//===========================================================================================================================
+//     Constants - Service Table
+//===========================================================================================================================
+
+struct KnownServiceEntry
+{
+       const char *            serviceType;
+       const char *            description;
+       const char *            urlScheme;
+       bool                            useText;
+};
+
+static const KnownServiceEntry         kKnownServiceTable[] =
+{
+       { "_accountedge._tcp.",                 "MYOB AccountEdge",                                                                             "",                     false },
+       { "_aecoretech._tcp.",                  "Apple Application Engineering Services",                                       "",                     false },
+       { "_afpovertcp._tcp.",                  "Apple File Sharing (AFP)",                                                             "afp://",               false },
+       { "_airport._tcp.",                     "AirPort Base Station",                                                                         "",                     false }, 
+       { "_apple-sasl._tcp.",                  "Apple Password Server",                                                                        "",                     false },
+       { "_aquamon._tcp.",                     "AquaMon",                                                                                                      "",                     false },
+       { "_async._tcp",                                "address-o-sync",                                                                                       "",                     false },
+       { "_auth._tcp.",                                "Authentication Service",                                                                       "",                     false },
+       { "_bootps._tcp.",                              "Bootstrap Protocol Server",                                                            "",                     false },
+       { "_bousg._tcp.",                               "Bag Of Unusual Strategy Games",                                                        "",                     false },
+       { "_browse._udp.",                              "DNS Service Discovery",                                                                        "",                     false },
+       { "_cheat._tcp.",                               "The Cheat",                                                                                            "",                     false },
+       { "_chess._tcp",                                "Project Gridlock",                                                                             "",                     false },
+       { "_chfts._tcp",                                "Fluid Theme Server",                                                                           "",                     false },
+       { "_clipboard._tcp",                    "Clipboard Sharing",                                                                            "",                     false },
+       { "_contactserver._tcp.",               "Now Up-to-Date & Contact",                                                                     "",                     false },
+       { "_cvspserver._tcp",                   "CVS PServer",                                                                                          "",                     false },
+       { "_cytv._tcp.",                                "CyTV Network streaming for Elgato EyeTV",                                      "",                     false },
+       { "_daap._tcp.",                                "Digital Audio Access Protocol (iTunes)",                                       "daap://",              false }, 
+       { "_distcc._tcp",                               "Distributed Compiler",                                                                         "",                     false },
+       { "_dns-sd._udp",                               "DNS Service Discovery",                                                                        "",                     false },
+       { "_dpap._tcp.",                                "Digital Picture Access Protocol (iPhoto)",                                     "",                     false },
+       { "_earphoria._tcp.",                   "Earphoria",                                                                                            "",                     false },
+       { "_ecbyesfsgksc._tcp.",                "Net Monitor Anti-Piracy Service",                                                      "",                             false },
+       { "_eheap._tcp.",                               "Interactive Room Software",                                                            "",                             false },
+       { "_embrace._tcp.",                     "DataEnvoy",                                                                                            "",                             false },
+       { "_eppc._tcp.",                                "Remote AppleEvents",                                                                           "eppc://",              false }, 
+       { "_exec._tcp.",                                "Remote Process Execution",                                                                     "",                             false },
+       { "_facespan._tcp.",                    "FaceSpan",                                                                                                     "",                             false },
+       { "_fjork._tcp.",                               "Fjork",                                                                                                        "",                             false },
+       { "_ftp._tcp.",                                 "File Transfer (FTP)",                                                                          "ftp://",               false }, 
+       { "_ftpcroco._tcp.",                    "Crocodile FTP Server",                                                                         "",                             false },
+       { "_gbs-smp._tcp.",                     "SnapMail",                                                                                                     "",                             false },
+       { "_gbs-stp._tcp.",                     "SnapTalk",                                                                                                     "",                             false },
+       { "_grillezvous._tcp.",                 "Roxio ToastAnywhere(tm) Recorder Sharing",                                     "",                             false },
+       { "_h323._tcp.",                                "H.323",                                                                                                        "",                             false },
+       { "_hotwayd._tcp",                              "Hotwayd",                                                                                                      "",                     false },
+       { "_http._tcp.",                                "Web Server (HTTP)",                                                                            "http://",              true  }, 
+       { "_hydra._tcp",                                "SubEthaEdit",                                                                                          "",                     false },
+       { "_ica-networking._tcp.",              "Image Capture Networking",                                                                     "",                     false }, 
+       { "_ichalkboard._tcp.",                 "iChalk",                                                                                                       "",                     false }, 
+       { "_ichat._tcp.",                               "iChat",                                                                                                        "ichat://",             false }, 
+       { "_iconquer._tcp.",                    "iConquer",                                                                                                     "",                     false }, 
+       { "_imap._tcp.",                                "Internet Message Access Protocol",                                                     "",                             false },
+       { "_imidi._tcp.",                               "iMidi",                                                                                                        "",                             false },
+       { "_ipp._tcp.",                                 "Printer (IPP)",                                                                                        "ipp://",               false },
+       { "_ishare._tcp.",                              "iShare",                                                                                                       "",                             false },
+       { "_isparx._tcp.",                              "iSparx",                                                                                                       "",                             false },
+       { "_istorm._tcp",                               "iStorm",                                                                                                       "",                     false },
+       { "_iwork._tcp.",                               "iWork Server",                                                                                         "",                             false },
+       { "_liaison._tcp.",                     "Liaison",                                                                                                      "",                             false },
+       { "_login._tcp.",                               "Remote Login a la Telnet",                                                                     "",                             false },
+       { "_lontalk._tcp.",                     "LonTalk over IP (ANSI 852)",                                                           "",                             false },
+       { "_lonworks._tcp.",                    "Echelon LNS Remote Client",                                                            "",                             false },
+       { "_macfoh-remote._tcp.",               "MacFOH Remote",                                                                                        "",                             false },
+       { "_moneyworks._tcp.",                  "MoneyWorks",                                                                                           "",                             false },
+       { "_mp3sushi._tcp",                     "MP3 Sushi",                                                                                            "",                     false },
+       { "_mttp._tcp.",                                "MenuTunes Sharing",                                                                            "",                             false },
+       { "_ncbroadcast._tcp.",                 "Network Clipboard Broadcasts",                                                         "",                             false },
+       { "_ncdirect._tcp.",                    "Network Clipboard Direct Transfers",                                           "",                             false },
+       { "_ncsyncserver._tcp.",                "Network Clipboard Sync Server",                                                        "",                             false },
+       { "_newton-dock._tcp.",                 "Escale",                                                                                                       "",                             false },
+       { "_nfs._tcp",                                  "NFS",                                                                                                          "",                     false },
+       { "_nssocketport._tcp.",                "DO over NSSocketPort",                                                                         "",                             false },
+       { "_omni-bookmark._tcp.",               "OmniWeb",                                                                                                      "",                             false },
+       { "_openbase._tcp.",                    "OpenBase SQL",                                                                                         "",                             false },
+       { "_p2pchat._tcp.",                     "Peer-to-Peer Chat",                                                                            "",                             false },
+       { "_pdl-datastream._tcp.",              "Printer (PDL)",                                                                                        "pdl://",               false }, 
+       { "_poch._tcp.",                                "Parallel OperatiOn and Control Heuristic",                                     "",                             false },
+       { "_pop_2_ambrosia._tcp.",              "Pop-Pop",                                                                                                      "",                             false },
+       { "_pop3._tcp",                                 "POP3 Server",                                                                                          "",                     false },
+       { "_postgresql._tcp",                   "PostgreSQL Server",                                                                            "",                     false },
+       { "_presence._tcp",                     "iChat AV",                                                                                             "",                     false },
+       { "_printer._tcp.",                     "Printer (LPR)",                                                                                        "lpr://",               false }, 
+       { "_ptp._tcp.",                                 "Picture Transfer (PTP)",                                                                       "ptp://",               false },
+       { "_register._tcp",                     "DNS Service Discovery",                                                                        "",                     false },
+       { "_rendezvouspong._tcp",               "RendezvousPong",                                                                                       "",                     false },
+       { "_rfb._tcp.",                                 "Remote Frame Buffer",                                                                          "",                             false },
+       { "_riousbprint._tcp.",                 "Remote I/O USB Printer Protocol",                                                      "",                             false },
+       { "_rtsp._tcp.",                                "Real Time Stream Control Protocol",                                            "",                             false },
+       { "_safarimenu._tcp",                   "Safari Menu",                                                                                          "",                     false },
+       { "_scone._tcp",                                "Scone",                                                                                                        "",                     false },
+       { "_sdsharing._tcp.",                   "Speed Download",                                                                                       "",                             false },
+       { "_seeCard._tcp.",                     "seeCard",                                                                                                      "",                             false },
+       { "_services._udp.",                    "DNS Service Discovery",                                                                        "",                             false },
+       { "_shell._tcp.",                               "like exec, but automatic authentication",                                      "",                             false },
+       { "_shout._tcp.",                               "Shout",                                                                                                        "",                             false },
+       { "_shoutcast._tcp",                    "Nicecast",                                                                                             "",                     false },
+       { "_smb._tcp.",                                 "Windows File Sharing (SMB)",                                                           "smb://",               false }, 
+       { "_soap._tcp.",                                "Simple Object Access Protocol",                                                        "",                     false }, 
+       { "_spincrisis._tcp.",                  "Spin Crisis",                                                                                          "",                             false },
+       { "_spl-itunes._tcp.",                  "launchTunes",                                                                                          "",                             false },
+       { "_spr-itunes._tcp.",                  "netTunes",                                                                                                     "",                             false },
+       { "_ssh._tcp.",                                 "Secure Shell (SSH)",                                                                           "ssh://",               false }, 
+       { "_ssscreenshare._tcp",                "Screen Sharing",                                                                                       "",                     false },
+       { "_sge-exec._tcp",                     "Sun Grid Engine (Execution Host)",                                             "",                     false },
+       { "_sge-qmaster._tcp",                  "Sun Grid Engine (Master)",                                                             "",                     false },
+       { "_stickynotes._tcp",                  "Sticky Notes",                                                                                         "",                     false },
+       { "_strateges._tcp",                    "Strateges",                                                                                            "",                     false },
+       { "_sxqdea._tcp",                               "Synchronize! Pro X",                                                                           "",                     false },
+       { "_sybase-tds._tcp",                   "Sybase Server",                                                                                        "",                     false },
+       { "_tce._tcp",                                  "Power Card",                                                                                           "",                     false },
+       { "_teamlist._tcp",                     "ARTIS Team Task",                                                                                      "",                     false },
+       { "_teleport._tcp",                     "teleport",                                                                                                     "",                     false },
+       { "_telnet._tcp.",                              "Telnet",                                                                                                       "telnet://",    false }, 
+       { "_tftp._tcp.",                                "Trivial File Transfer (TFTP)",                                                         "tftp://",              false }, 
+       { "_tinavigator._tcp.",                 "TI Navigator",                                                                                         "",                     false }, 
+       { "_tivo_servemedia._tcp",              "TiVo",                                                                                                         "",                     false },
+       { "_upnp._tcp.",                                "Universal Plug and Play",                                                                      "",                     false }, 
+       { "_utest._tcp.",                               "uTest",                                                                                                        "",                     false }, 
+       { "_vue4rendercow._tcp",                "VueProRenderCow",                                                                                      "",                     false },
+       { "_webdav._tcp.",                              "WebDAV",                                                                                                       "webdav://",    false }, 
+       { "_whamb._tcp.",                               "Whamb",                                                                                                        "",                             false }, 
+       { "_workstation._tcp",                  "Macintosh Manager",                                                                            "",                     false },
+       { "_ws._tcp",                                   "Web Services",                                                                                         "",                     false },
+       { "_xserveraid._tcp.",                  "Xserve RAID",                                                                                          "xsr://",               false }, 
+       { "_xsync._tcp.",                               "Xserve RAID Synchronization",                                                          "",                             false }, 
+       
+       { "",                                                   "",                                                                                                                     "",                             false }, 
+       
+       // Unofficial and invalid service types that will be phased out:
+       
+       { "_clipboardsharing._tcp.",                    "ClipboardSharing",                                                                     "",                             false }, 
+       { "_MacOSXDupSuppress._tcp.",                   "Mac OS X Duplicate Suppression",                                       "",                             false }, 
+       { "_netmonitorserver._tcp.",                    "Net Monitor Server",                                                           "",                             false }, 
+       { "_networkclipboard._tcp.",                    "Network Clipboard",                                                            "",                             false }, 
+       { "_slimdevices_slimp3_cli._tcp.",              "SliMP3 Server Command-Line Interface",                         "",                             false }, 
+       { "_slimdevices_slimp3_http._tcp.",             "SliMP3 Server Web Interface",                                          "",                             false }, 
+       { "_tieducationalhandhelddevice._tcp.", "TI Connect Manager",                                                           "",                             false }, 
+       { "_tivo_servemedia._tcp.",                             "TiVo",                                                                                         "",                             false }, 
+       
+       { NULL,                                                 NULL,                                                                                                           NULL,                   false }, 
+};
+
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+//     Structures
+//===========================================================================================================================
+
+struct DomainEventInfo
+{
+       DNSBrowserEventType             eventType;
+       CString                                 domain;
+       DNSNetworkAddress               ifIP;
+};
+
+struct ServiceEventInfo
+{
+       DNSBrowserEventType             eventType;
+       std::string                             name;
+       std::string                             type;
+       std::string                             domain;
+       DNSNetworkAddress               ifIP;
+};
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+//     Prototypes
+//===========================================================================================================================
+
+static void
+       BrowserCallBack( 
+               void *                                  inContext, 
+               DNSBrowserRef                   inRef, 
+               DNSStatus                               inStatusCode,
+               const DNSBrowserEvent * inEvent );
+
+static char *  DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString );
+
+static DWORD   UTF8StringToStringObject( const char *inUTF8, CString &inObject );
+static DWORD   StringObjectToUTF8String( CString &inObject, std::string &outUTF8 );
+
+#if 0
+#pragma mark == Message Map ==
+#endif
+
+//===========================================================================================================================
+//     Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(ChooserDialog, CDialog)
+       //{{AFX_MSG_MAP(ChooserDialog)
+       ON_WM_SYSCOMMAND()
+       ON_NOTIFY(LVN_ITEMCHANGED, IDC_DOMAIN_LIST, OnDomainListChanged)
+       ON_NOTIFY(LVN_ITEMCHANGED, IDC_SERVICE_LIST, OnServiceListChanged)
+       ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHOOSER_LIST, OnChooserListChanged)
+       ON_NOTIFY(NM_DBLCLK, IDC_CHOOSER_LIST, OnChooserListDoubleClick)
+       ON_COMMAND(ID_HELP_ABOUT, OnAbout)
+       ON_WM_INITMENUPOPUP()
+       ON_WM_ACTIVATE()
+       ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
+       ON_COMMAND(ID_FILE_EXIT, OnExit)
+       ON_WM_CLOSE()
+       ON_WM_NCDESTROY()
+       //}}AFX_MSG_MAP
+       ON_MESSAGE( WM_USER_DOMAIN_ADD, OnDomainAdd )
+       ON_MESSAGE( WM_USER_DOMAIN_REMOVE, OnDomainRemove )
+       ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
+       ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
+       ON_MESSAGE( WM_USER_RESOLVE, OnResolve )
+END_MESSAGE_MAP()
+
+#if 0
+#pragma mark == Routines ==
+#endif
+
+//===========================================================================================================================
+//     ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::ChooserDialog( CWnd *inParent )
+       : CDialog( ChooserDialog::IDD, inParent)
+{
+       //{{AFX_DATA_INIT(ChooserDialog)
+               // NOTE: the ClassWizard will add member initialization here
+       //}}AFX_DATA_INIT
+       
+       // Load menu accelerator table.
+
+       mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) );
+       assert( mMenuAcceleratorTable );
+       
+       mBrowser                        = NULL;
+       mIsServiceBrowsing      = false;
+}
+
+//===========================================================================================================================
+//     ~ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::~ChooserDialog( void )
+{
+       if( mBrowser )
+       {
+               DNSStatus               err;
+               
+               err = DNSBrowserRelease( mBrowser, 0 );
+               assert( err == kDNSNoErr );
+       }
+}
+
+//===========================================================================================================================
+//     DoDataExchange
+//===========================================================================================================================
+
+void ChooserDialog::DoDataExchange( CDataExchange *pDX )
+{
+       CDialog::DoDataExchange(pDX);
+
+       //{{AFX_DATA_MAP(ChooserDialog)
+       DDX_Control(pDX, IDC_SERVICE_LIST, mServiceList);
+       DDX_Control(pDX, IDC_DOMAIN_LIST, mDomainList);
+       DDX_Control(pDX, IDC_CHOOSER_LIST, mChooserList);
+       //}}AFX_DATA_MAP
+}
+
+//===========================================================================================================================
+//     OnInitDialog
+//===========================================================================================================================
+
+BOOL   ChooserDialog::OnInitDialog( void )
+{
+       HICON                   icon;
+       BOOL                    result;
+       CString                 tempString;
+       DNSStatus               err;
+       
+       // Initialize our parent.
+
+       CDialog::OnInitDialog();
+       
+       // Set up the window icon.
+       
+       icon = AfxGetApp()->LoadIcon( IDR_MAIN_ICON );
+       assert( icon );
+       if( icon )
+       {
+               SetIcon( icon, TRUE );          // Set big icon
+               SetIcon( icon, FALSE );         // Set small icon
+       }
+       
+       // Set up the Domain List.
+       
+       result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME );
+       assert( result );
+       mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth );
+       
+       // Set up the Service List.
+       
+       result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE );
+       assert( result );
+       mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnTypeWidth );
+       
+       result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC );
+       assert( result );
+       mServiceList.InsertColumn( 1, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnDescWidth );
+       
+       PopulateServicesList();
+       
+       // Set up the Chooser List.
+       
+       result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME );
+       assert( result );
+       mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth );
+       
+       result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME );
+       assert( result );
+       mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth );
+       
+       // Set up the other controls.
+       
+       UpdateInfoDisplay();
+       
+       // Start browsing for domains.
+       
+       err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
+       assert( err == kDNSNoErr );
+       
+       err = DNSBrowserStartDomainSearch( mBrowser, 0 );
+       assert( err == kDNSNoErr );
+       
+       return( true );
+}
+
+//===========================================================================================================================
+//     OnFileClose
+//===========================================================================================================================
+
+void ChooserDialog::OnFileClose() 
+{
+       OnClose();
+}
+
+//===========================================================================================================================
+//     OnActivate
+//===========================================================================================================================
+
+void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
+{
+       // Always make the active window the "main" window so modal dialogs work better and the app quits after closing 
+       // the last window.
+
+       gApp.m_pMainWnd = this;
+
+       CDialog::OnActivate(nState, pWndOther, bMinimized);
+}
+
+//===========================================================================================================================
+//     PostNcDestroy
+//===========================================================================================================================
+
+void   ChooserDialog::PostNcDestroy() 
+{
+       // Call the base class to do the normal cleanup.
+
+       delete this;
+}
+
+//===========================================================================================================================
+//     PreTranslateMessage
+//===========================================================================================================================
+
+BOOL   ChooserDialog::PreTranslateMessage(MSG* pMsg) 
+{
+       BOOL            result;
+       
+       result = false;
+       assert( mMenuAcceleratorTable );
+       if( mMenuAcceleratorTable )
+       {
+               result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg );
+       }
+       if( !result )
+       {
+               result = CDialog::PreTranslateMessage( pMsg );
+       }
+       return( result );
+}
+
+//===========================================================================================================================
+//     OnInitMenuPopup
+//===========================================================================================================================
+
+void   ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu ) 
+{
+       CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
+
+       switch( nIndex )
+       {
+               case kChooserMenuIndexFile:
+                       break;
+
+               case kChooserMenuIndexHelp:
+                       break;
+
+               default:
+                       break;
+       }
+}
+
+//===========================================================================================================================
+//     OnExit
+//===========================================================================================================================
+
+void ChooserDialog::OnExit() 
+{
+       OnClose();
+}
+
+//===========================================================================================================================
+//     OnAbout
+//===========================================================================================================================
+
+void   ChooserDialog::OnAbout() 
+{
+       AboutDialog             dialog;
+       
+       dialog.DoModal();
+}
+
+//===========================================================================================================================
+//     OnSysCommand
+//===========================================================================================================================
+
+void   ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam ) 
+{
+       CDialog::OnSysCommand( inID, inParam );
+}
+
+//===========================================================================================================================
+//     OnClose
+//===========================================================================================================================
+
+void ChooserDialog::OnClose() 
+{
+       StopBrowsing();
+       
+       gApp.m_pMainWnd = this;
+       DestroyWindow();
+}
+
+//===========================================================================================================================
+//     OnNcDestroy
+//===========================================================================================================================
+
+void ChooserDialog::OnNcDestroy() 
+{
+       gApp.m_pMainWnd = this;
+
+       CDialog::OnNcDestroy();
+}
+
+//===========================================================================================================================
+//     OnDomainListChanged
+//===========================================================================================================================
+
+void   ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult ) 
+{
+       UNUSED_ALWAYS( pNMHDR );
+       
+       // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
+       
+       OnServiceListChanged( NULL, NULL );
+       
+       *pResult = 0;
+}
+
+//===========================================================================================================================
+//     OnServiceListChanged
+//===========================================================================================================================
+
+void   ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult ) 
+{
+       int                             selectedType;
+       int                             selectedDomain;
+       
+       UNUSED_ALWAYS( pNMHDR );
+       
+       // Stop any existing service search.
+       
+       StopBrowsing();
+       
+       // If a domain and service type are selected, start searching for the service type on the domain.
+       
+       selectedType    = mServiceList.GetNextItem( -1, LVNI_SELECTED );
+       selectedDomain  = mDomainList.GetNextItem( -1, LVNI_SELECTED );
+       
+       if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) )
+       {
+               CString                         s;
+               std::string                     utf8;
+               const char *            type;
+               
+               s = mDomainList.GetItemText( selectedDomain, 0 );
+               StringObjectToUTF8String( s, utf8 );
+               type = mServiceTypes[ selectedType ].serviceType.c_str();
+               if( *type != '\0' )
+               {
+                       StartBrowsing( type, utf8.c_str() );
+               }
+       }
+       
+       if( pResult )
+       {
+               *pResult = 0;
+       }
+}
+
+//===========================================================================================================================
+//     OnChooserListChanged
+//===========================================================================================================================
+
+void   ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult ) 
+{
+       UNUSED_ALWAYS( pNMHDR );
+       
+       UpdateInfoDisplay();
+       *pResult = 0;
+}
+
+//===========================================================================================================================
+//     OnChooserListDoubleClick
+//===========================================================================================================================
+
+void   ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
+{
+       int             selectedItem;
+       
+       UNUSED_ALWAYS( pNMHDR );
+       
+       // Display the service instance if it is selected. Otherwise, clear all the info.
+       
+       selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+       if( selectedItem >= 0 )
+       {
+               ServiceInstanceInfo *                   p;
+               CString                                                 url;
+               const KnownServiceEntry *               service;
+               
+               assert( selectedItem < (int) mServiceInstances.size() );
+               p = &mServiceInstances[ selectedItem ];
+               
+               // Search for a known service type entry that matches.
+               
+               for( service = kKnownServiceTable; service->serviceType; ++service )
+               {
+                       if( p->type == service->serviceType )
+                       {
+                               break;
+                       }
+               }
+               if( service->serviceType )
+               {
+                       const char *            text;
+                       
+                       // Create a URL representing the service instance.
+                       
+                       if( strcmp( service->serviceType, "_smb._tcp." ) == 0 )
+                       {
+                               // Special case for SMB (no port number).
+                               
+                               url.Format( TEXT( "%s%s/" ), service->urlScheme, (const char *) p->ip.c_str() ); 
+                       }
+                       else if( strcmp( service->serviceType, "_ftp._tcp." ) == 0 )
+                       {
+                               // Special case for FTP to get login info.
+
+                               LoginDialog             dialog;
+                               CString                 username;
+                               CString                 password;
+                               
+                               if( !dialog.GetLogin( username, password ) )
+                               {
+                                       goto exit;
+                               }
+                               
+                               // Build URL in the following format:
+                               //
+                               // ftp://[username[:password]@]<ip>
+                               
+                               url += service->urlScheme;
+                               if( username.GetLength() > 0 )
+                               {
+                                       url += username;
+                                       if( password.GetLength() > 0 )
+                                       {
+                                               url += ':';
+                                               url += password;
+                                       }
+                                       url += '@';
+                               }
+                               url += p->ip.c_str();
+                       }
+                       else if( strcmp( service->serviceType, "_http._tcp." ) == 0 )
+                       {
+                               // Special case for HTTP to exclude "path=" if present.
+                               
+                               text = service->useText ? p->text.c_str() : "";
+                               if( strncmp( text, "path=", 5 ) == 0 )
+                               {
+                                       text += 5;
+                               }
+                               if( *text != '/' )
+                               {
+                                       url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
+                               }
+                               else
+                               {
+                                       url.Format( TEXT( "%s%s%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
+                               }
+                       }
+                       else
+                       {
+                               text = service->useText ? p->text.c_str() : "";
+                               url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text ); 
+                       }
+                       
+                       // Let the system open the URL in the correct app.
+                       
+                       {
+                               CWaitCursor             waitCursor;
+                               
+                               ShellExecute( NULL, TEXT( "open" ), url, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL );
+                       }
+               }
+       }
+
+exit:
+       *pResult = 0;
+}
+
+//===========================================================================================================================
+//     OnCancel
+//===========================================================================================================================
+
+void ChooserDialog::OnCancel() 
+{
+       // Do nothing.
+}
+
+//===========================================================================================================================
+//     PopulateServicesList
+//===========================================================================================================================
+
+void   ChooserDialog::PopulateServicesList( void )
+{
+       ServiceTypeVector::iterator             i;
+       CString                                                 type;
+       CString                                                 desc;
+       std::string                                             tmp;
+       
+       // Add a fixed list of known services.
+       
+       if( mServiceTypes.empty() )
+       {
+               const KnownServiceEntry *               service;
+               
+               for( service = kKnownServiceTable; service->serviceType; ++service )
+               {
+                       ServiceTypeInfo         info;
+                       
+                       info.serviceType        = service->serviceType;
+                       info.description        = service->description;
+                       info.urlScheme          = service->urlScheme;
+                       mServiceTypes.push_back( info );
+               }
+       }
+       
+       // Add each service to the list.
+       
+       for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i )
+       {
+               const char *            p;
+               const char *            q;
+               
+               p  = ( *i ).serviceType.c_str();
+               if( *p == '_' ) ++p;                                                    // Skip leading '_'.
+               q  = strchr( p, '.' );                                                  // Find first '.'.
+               if( q ) tmp.assign( p, (size_t)( q - p ) );             // Use only up to the first '.'.
+               else    tmp.assign( p );                                                // No '.' so use the entire string.
+               UTF8StringToStringObject( tmp.c_str(), type );
+               UTF8StringToStringObject( ( *i ).description.c_str(), desc );
+               
+               int             n;
+               
+               n = mServiceList.GetItemCount();
+               mServiceList.InsertItem( n, type );
+               mServiceList.SetItemText( n, 1, desc );
+       }
+       
+       // Select the first service type by default.
+       
+       if( !mServiceTypes.empty() )
+       {
+               mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+       }
+}
+
+//===========================================================================================================================
+//     UpdateInfoDisplay
+//===========================================================================================================================
+
+void   ChooserDialog::UpdateInfoDisplay( void )
+{
+       int                                                     selectedItem;
+       std::string                                     name;
+       CString                                         s;
+       std::string                                     ip;
+       std::string                                     ifIP;
+       std::string                                     text;
+       std::string                                     textNewLines;
+       std::string                                     hostName;
+       CWnd *                                          item;
+       std::string::iterator           i;
+       
+       // Display the service instance if it is selected. Otherwise, clear all the info.
+       
+       selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+       if( selectedItem >= 0 )
+       {
+               ServiceInstanceInfo *           p;
+               
+               assert( selectedItem < (int) mServiceInstances.size() );
+               p = &mServiceInstances[ selectedItem ];
+               
+               name            = p->name;
+               ip                      = p->ip;
+               ifIP            = p->ifIP;
+               text            = p->text;
+               hostName        = p->hostName;
+
+               // Sync up the list items with the actual data (IP address may change).
+               
+               UTF8StringToStringObject( ip.c_str(), s );
+               mChooserList.SetItemText( selectedItem, 1, s );
+       }
+       
+       // Name
+       
+       item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT );
+       assert( item );
+       UTF8StringToStringObject( name.c_str(), s );
+       item->SetWindowText( s );
+       
+       // IP
+       
+       item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT );
+       assert( item );
+       UTF8StringToStringObject( ip.c_str(), s );
+       item->SetWindowText( s );
+       
+       // Interface
+       
+       item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT );
+       assert( item );
+       UTF8StringToStringObject( ifIP.c_str(), s );
+       item->SetWindowText( s );
+       
+
+       item = (CWnd *) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT );
+       assert( item );
+       UTF8StringToStringObject( hostName.c_str(), s );
+       item->SetWindowText( s );
+
+       // Text
+       
+       item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT );
+       assert( item );
+       for( i = text.begin(); i != text.end(); ++i )
+       {
+               if( *i == '\1' )
+               {
+                       textNewLines += "\r\n";
+               }
+               else
+               {
+                       textNewLines += *i;
+               }
+       }
+       UTF8StringToStringObject( textNewLines.c_str(), s );
+       item->SetWindowText( s );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     OnDomainAdd
+//===========================================================================================================================
+
+LONG   ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam )
+{
+       DomainEventInfo *                                               p;
+       std::auto_ptr < DomainEventInfo >               pAutoPtr;
+       int                                                                             n;
+       int                                                                             i;
+       CString                                                                 domain;
+       CString                                                                 s;
+       bool                                                                    found;
+       
+       UNUSED_ALWAYS( inWParam );
+       
+       assert( inLParam );
+       p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+       pAutoPtr.reset( p );
+       
+       // Search to see if we already know about this domain. If not, add it to the list.
+       
+       found = false;
+       domain = p->domain;
+       n = mDomainList.GetItemCount();
+       for( i = 0; i < n; ++i )
+       {
+               s = mDomainList.GetItemText( i, 0 );
+               if( s == domain )
+               {
+                       found = true;
+                       break;
+               }
+       }
+       if( !found )
+       {
+               int             selectedItem;
+               
+               mDomainList.InsertItem( n, domain );
+               
+               // If no domains are selected and the domain being added is a default domain, select it.
+               
+               selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED );
+               if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) )
+               {
+                       mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+               }
+       }
+       return( 0 );
+}
+
+//===========================================================================================================================
+//     OnDomainRemove
+//===========================================================================================================================
+
+LONG   ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam )
+{
+       DomainEventInfo *                                               p;
+       std::auto_ptr < DomainEventInfo >               pAutoPtr;
+       int                                                                             n;
+       int                                                                             i;
+       CString                                                                 domain;
+       CString                                                                 s;
+       bool                                                                    found;
+       
+       UNUSED_ALWAYS( inWParam );
+       
+       assert( inLParam );
+       p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+       pAutoPtr.reset( p );
+       
+       // Search to see if we know about this domain. If so, remove it from the list.
+       
+       found = false;
+       domain = p->domain;
+       n = mDomainList.GetItemCount();
+       for( i = 0; i < n; ++i )
+       {
+               s = mDomainList.GetItemText( i, 0 );
+               if( s == domain )
+               {
+                       found = true;
+                       break;
+               }
+       }
+       if( found )
+       {
+               mDomainList.DeleteItem( i );
+       }
+       return( 0 );
+}
+
+//===========================================================================================================================
+//     OnServiceAdd
+//===========================================================================================================================
+
+LONG   ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
+{
+       ServiceEventInfo *                                              p;
+       std::auto_ptr < ServiceEventInfo >              pAutoPtr;
+       
+       UNUSED_ALWAYS( inWParam );
+       
+       assert( inLParam );
+       p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+       pAutoPtr.reset( p );
+       
+       return( 0 );
+}
+
+//===========================================================================================================================
+//     OnServiceRemove
+//===========================================================================================================================
+
+LONG   ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
+{
+       ServiceEventInfo *                                              p;
+       std::auto_ptr < ServiceEventInfo >              pAutoPtr;
+       bool                                                                    found;
+       int                                                                             n;
+       int                                                                             i;
+       
+       UNUSED_ALWAYS( inWParam );
+       
+       assert( inLParam );
+       p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+       pAutoPtr.reset( p );
+       
+       // Search to see if we know about this service instance. If so, remove it from the list.
+       
+       found = false;
+       n = (int) mServiceInstances.size();
+       for( i = 0; i < n; ++i )
+       {
+               ServiceInstanceInfo *           q;
+               
+               // If the name, type, domain, and interface match, treat it as the same service instance.
+               
+               q = &mServiceInstances[ i ];
+               if( ( p->name   == q->name )    && 
+                       ( p->type       == q->type )    && 
+                       ( p->domain     == q->domain ) )
+               {
+                       found = true;
+                       break;
+               }
+       }
+       if( found )
+       {
+               mChooserList.DeleteItem( i );
+               assert( i < (int) mServiceInstances.size() );
+               mServiceInstances.erase( mServiceInstances.begin() + i );
+       }
+       return( 0 );
+}
+
+//===========================================================================================================================
+//     OnResolve
+//===========================================================================================================================
+
+LONG   ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam )
+{
+       ServiceInstanceInfo *                                           p;
+       std::auto_ptr < ServiceInstanceInfo >           pAutoPtr;
+       int                                                                                     selectedType;
+       int                                                                                     n;
+       int                                                                                     i;
+       bool                                                                            found;
+       
+       UNUSED_ALWAYS( inWParam );
+       
+       assert( inLParam );
+       p = reinterpret_cast <ServiceInstanceInfo *> ( inLParam );
+       pAutoPtr.reset( p );
+       
+       // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
+       
+       selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
+       assert( selectedType >= 0 );
+       if( selectedType >= 0 )
+       {
+               assert( selectedType <= (int) mServiceTypes.size() );
+               if( p->type != mServiceTypes[ selectedType ].serviceType )
+               {
+                       goto exit;
+               }
+       }
+       
+       // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
+       
+       found = false;
+       n = (int) mServiceInstances.size();
+       for( i = 0; i < n; ++i )
+       {
+               ServiceInstanceInfo *           q;
+               
+               // If the name, type, domain, and interface matches, treat it as the same service instance.
+                               
+               q = &mServiceInstances[ i ];
+               if( ( p->name   == q->name )    && 
+                       ( p->type       == q->type )    && 
+                       ( p->domain     == q->domain )  && 
+                       ( p->ifIP       == q->ifIP ) )
+               {
+                       found = true;
+                       break;
+               }
+       }
+       if( found )
+       {
+               mServiceInstances[ i ] = *p;
+       }
+       else
+       {
+               CString         s;
+               
+               mServiceInstances.push_back( *p );
+               UTF8StringToStringObject( p->name.c_str(), s );
+               mChooserList.InsertItem( n, s );
+               
+               UTF8StringToStringObject( p->ip.c_str(), s );
+               mChooserList.SetItemText( n, 1, s );
+               
+               // If this is the only item, select it.
+               
+               if( n == 0 )
+               {
+                       mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+               }
+       }
+       UpdateInfoDisplay();
+
+exit:
+       return( 0 );
+}
+
+//===========================================================================================================================
+//     StartBrowsing
+//===========================================================================================================================
+
+void   ChooserDialog::StartBrowsing( const char *inType, const char *inDomain )
+{
+       DNSStatus               err;
+       
+       assert( mServiceInstances.empty() );
+       assert( mChooserList.GetItemCount() == 0 );
+       assert( !mIsServiceBrowsing );
+       
+       mChooserList.DeleteAllItems();
+       mServiceInstances.clear();
+       
+       mIsServiceBrowsing = true;
+       err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain );
+       assert( err == kDNSNoErr );
+}
+
+//===========================================================================================================================
+//     StopBrowsing
+//===========================================================================================================================
+
+void   ChooserDialog::StopBrowsing( void )
+{
+       // If searching, stop.
+       
+       if( mIsServiceBrowsing )
+       {
+               DNSStatus               err;
+               
+               mIsServiceBrowsing = false;
+               err = DNSBrowserStopServiceSearch( mBrowser, 0 );
+               assert( err == kDNSNoErr );
+       }
+       
+       // Remove all service instances.
+       
+       mChooserList.DeleteAllItems();
+       assert( mChooserList.GetItemCount() == 0 );
+       mServiceInstances.clear();
+       assert( mServiceInstances.empty() );
+       UpdateInfoDisplay();
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     BrowserCallBack
+//===========================================================================================================================
+
+static void
+       BrowserCallBack( 
+               void *                                  inContext, 
+               DNSBrowserRef                   inRef, 
+               DNSStatus                               inStatusCode,
+               const DNSBrowserEvent * inEvent )
+{
+       ChooserDialog *         dialog;
+       UINT                            message;
+       BOOL                            posted;
+       
+       UNUSED_ALWAYS( inStatusCode );
+       UNUSED_ALWAYS( inRef );
+       
+       // Check parameters.
+       
+       assert( inContext );
+       dialog = reinterpret_cast <ChooserDialog *> ( inContext );
+       
+       try
+       {
+               switch( inEvent->type )
+               {
+                       case kDNSBrowserEventTypeRelease:
+                               break;
+                       
+                       // Domains
+                       
+                       case kDNSBrowserEventTypeAddDomain:
+                       case kDNSBrowserEventTypeAddDefaultDomain:
+                       case kDNSBrowserEventTypeRemoveDomain:
+                       {
+                               DomainEventInfo *                                               domain;
+                               std::auto_ptr < DomainEventInfo >               domainAutoPtr;
+                               
+                               domain = new DomainEventInfo;
+                               domainAutoPtr.reset( domain );
+                               
+                               domain->eventType       = inEvent->type;
+                               domain->domain          = inEvent->data.addDomain.domain;
+                               domain->ifIP            = inEvent->data.addDomain.interfaceIP;
+                               
+                               message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD;
+                               posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain );
+                               assert( posted );
+                               if( posted )
+                               {
+                                       domainAutoPtr.release();
+                               }
+                               break;
+                       }
+                       
+                       // Services
+                       
+                       case kDNSBrowserEventTypeAddService:
+                       case kDNSBrowserEventTypeRemoveService:
+                       {
+                               ServiceEventInfo *                                              service;
+                               std::auto_ptr < ServiceEventInfo >              serviceAutoPtr;
+                               
+                               service = new ServiceEventInfo;
+                               serviceAutoPtr.reset( service );
+                               
+                               service->eventType      = inEvent->type;
+                               service->name           = inEvent->data.addService.name;
+                               service->type           = inEvent->data.addService.type;
+                               service->domain         = inEvent->data.addService.domain;
+                               service->ifIP           = inEvent->data.addService.interfaceIP;
+                               
+                               message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE;
+                               posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service );
+                               assert( posted );
+                               if( posted )
+                               {
+                                       serviceAutoPtr.release();
+                               }
+                               break;
+                       }
+                       
+                       // Resolves
+                       
+                       case kDNSBrowserEventTypeResolved:
+                               if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4  )
+                               {
+                                       ServiceInstanceInfo *                                           serviceInstance;
+                                       std::auto_ptr < ServiceInstanceInfo >           serviceInstanceAutoPtr;
+                                       char                                                                            s[ 32 ];
+                                       
+                                       serviceInstance = new ServiceInstanceInfo;
+                                       serviceInstanceAutoPtr.reset( serviceInstance );
+                                       
+                                       serviceInstance->name           = inEvent->data.resolved->name;
+                                       serviceInstance->type           = inEvent->data.resolved->type;
+                                       serviceInstance->domain         = inEvent->data.resolved->domain;
+                                       serviceInstance->ip                     = DNSNetworkAddressToString( &inEvent->data.resolved->address, s );
+                                       serviceInstance->ifIP           = DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s );
+                                       serviceInstance->text           = inEvent->data.resolved->textRecord;
+                                       serviceInstance->hostName       = inEvent->data.resolved->hostName;
+
+                                       posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance );
+                                       assert( posted );
+                                       if( posted )
+                                       {
+                                               serviceInstanceAutoPtr.release();
+                                       }
+                               }
+                               break;
+                       
+                       default:
+                               break;
+               }
+       }
+       catch( ... )
+       {
+               // Don't let exceptions escape.
+       }
+}
+
+//===========================================================================================================================
+//     DNSNetworkAddressToString
+//
+//     Note: Currently only supports IPv4 network addresses.
+//===========================================================================================================================
+
+static char *  DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString )
+{
+       const DNSUInt8 *                p;
+       DNSUInt16                               port;
+       
+       p = inAddr->u.ipv4.addr.v8;
+       port = ntohs( inAddr->u.ipv4.port.v16 );
+       if( port != kDNSPortInvalid )
+       {
+               sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port );
+       }
+       else
+       {
+               sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] );
+       }
+       return( outString );
+}
+
+//===========================================================================================================================
+//     UTF8StringToStringObject
+//===========================================================================================================================
+
+static DWORD   UTF8StringToStringObject( const char *inUTF8, CString &inObject )
+{
+       DWORD           err;
+       int                     n;
+       BSTR            unicode;
+       
+       unicode = NULL;
+       
+       n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
+       if( n > 0 )
+       {
+               unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
+               if( !unicode )
+               {
+                       err = ERROR_INSUFFICIENT_BUFFER;
+                       goto exit;
+               }
+
+               n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
+               try
+               {
+                       inObject = unicode;
+               }
+               catch( ... )
+               {
+                       err = ERROR_NO_UNICODE_TRANSLATION;
+                       goto exit;
+               }
+       }
+       else
+       {
+               inObject = "";
+       }
+       err = 0;
+       
+exit:
+       if( unicode )
+       {
+               free( unicode );
+       }
+       return( err );
+}
+
+//===========================================================================================================================
+//     StringObjectToUTF8String
+//===========================================================================================================================
+
+static DWORD   StringObjectToUTF8String( CString &inObject, std::string &outUTF8 )
+{
+       DWORD           err;
+       BSTR            unicode;
+       int                     nUnicode;
+       int                     n;
+       char *          utf8;
+       
+       unicode = NULL;
+       utf8    = NULL;
+       
+       nUnicode = inObject.GetLength();
+       if( nUnicode > 0 )
+       {
+               unicode = inObject.AllocSysString();
+               n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, NULL, 0, NULL, NULL );
+               assert( n > 0 );
+               
+               utf8 = (char *) malloc( (size_t) n );
+               assert( utf8 );
+               if( !utf8 ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; }
+               
+               n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, utf8, n, NULL, NULL );
+               assert( n > 0 );
+               
+               try
+               {
+                       outUTF8.assign( utf8, n );
+               }
+               catch( ... )
+               {
+                       err = ERROR_NO_UNICODE_TRANSLATION;
+                       goto exit;
+               }
+       }
+       else
+       {
+               outUTF8.clear();
+       }
+       err = 0;
+       
+exit:
+       if( unicode )
+       {
+               SysFreeString( unicode );
+       }
+       if( utf8 )
+       {
+               free( utf8 );
+       }
+       return( err );
+}