]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - Clients/ExplorerPlugin/ExplorerBarWindow.cpp
mDNSResponder-87.tar.gz
[apple/mdnsresponder.git] / Clients / ExplorerPlugin / ExplorerBarWindow.cpp
diff --git a/Clients/ExplorerPlugin/ExplorerBarWindow.cpp b/Clients/ExplorerPlugin/ExplorerBarWindow.cpp
new file mode 100644 (file)
index 0000000..beb0b35
--- /dev/null
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 2003-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: ExplorerBarWindow.cpp,v $
+Revision 1.12  2004/10/26 00:56:03  cheshire
+Use "#if 0" instead of commenting out code
+
+Revision 1.11  2004/10/18 23:49:17  shersche
+<rdar://problem/3841564> Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot.
+Bug #: 3841564
+
+Revision 1.10  2004/09/02 02:18:58  cheshire
+Minor textual cleanup to improve readability
+
+Revision 1.9  2004/09/02 02:11:56  cheshire
+<rdar://problem/3783611> Fix incorrect testing of MoreComing flag
+
+Revision 1.8  2004/07/26 05:47:31  shersche
+use TXTRecord APIs, fix bug in locating service to be removed
+
+Revision 1.7  2004/07/22 16:08:20  shersche
+clean up debug print statements, re-enable code inadvertently commented out
+
+Revision 1.6  2004/07/22 05:27:20  shersche
+<rdar://problem/3735827> Check to make sure error isn't WSAEWOULDBLOCK when canceling browse
+Bug #: 3735827
+
+Revision 1.5  2004/07/20 06:49:18  shersche
+clean up socket handling code
+
+Revision 1.4  2004/07/13 21:24:21  rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.3  2004/06/27 14:59:59  shersche
+reference count service info to handle multi-homed hosts
+
+Revision 1.2  2004/06/23 16:09:34  shersche
+Add the resolve DNSServiceRef to list of extant refs.  This fixes the "doesn't resolve when double clicking" problem
+
+Submitted by: Scott Herscher
+
+Revision 1.1  2004/06/18 04:34:59  rpantos
+Move to Clients from mDNSWindows
+
+Revision 1.5  2004/04/15 01:00:05  bradley
+Removed support for automatically querying for A/AAAA records when resolving names. Platforms
+without .local name resolving support will need to manually query for A/AAAA records as needed.
+
+Revision 1.4  2004/04/09 21:03:15  bradley
+Changed port numbers to use network byte order for consistency with other platforms.
+
+Revision 1.3  2004/04/08 09:43:43  bradley
+Changed callback calling conventions to __stdcall so they can be used with C# delegates.
+
+Revision 1.2  2004/02/21 04:36:19  bradley
+Enable dot local name lookups now that the NSP is being installed.
+
+Revision 1.1  2004/01/30 03:01:56  bradley
+Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer.
+
+*/
+
+#include       "StdAfx.h"
+
+#include       "CommonServices.h"
+#include       "DebugServices.h"
+#include       "WinServices.h"
+#include       "dns_sd.h"
+
+#include       "ExplorerBar.h"
+#include       "LoginDialog.h"
+#include       "Resource.h"
+
+#include       "ExplorerBarWindow.h"
+
+// MFC Debugging
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+//     Constants
+//===========================================================================================================================
+
+// Control IDs
+
+#define        IDC_EXPLORER_TREE                               1234
+
+// Private Messages
+
+#define WM_PRIVATE_SERVICE_EVENT                               ( WM_USER + 0x100 )
+
+// TXT records
+
+#define        kTXTRecordKeyPath                               "path"
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+//     Prototypes
+//===========================================================================================================================
+
+DEBUG_LOCAL int                        FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex );
+
+#if 0
+#pragma mark == Message Map ==
+#endif
+
+//===========================================================================================================================
+//     Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd )
+       ON_WM_CREATE()
+       ON_WM_DESTROY()
+       ON_WM_SIZE()
+       ON_NOTIFY( NM_DBLCLK, IDC_EXPLORER_TREE, OnDoubleClick )
+       ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT, OnServiceEvent )
+END_MESSAGE_MAP()
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     ExplorerBarWindow
+//===========================================================================================================================
+
+ExplorerBarWindow::ExplorerBarWindow( void )
+{
+       mOwner                          = NULL;
+       mResolveServiceRef      = NULL;
+}
+
+//===========================================================================================================================
+//     ~ExplorerBarWindow
+//===========================================================================================================================
+
+ExplorerBarWindow::~ExplorerBarWindow( void )
+{
+       //
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     OnCreate
+//===========================================================================================================================
+
+int    ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct )
+{
+       AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+       
+       OSStatus                err;
+       CRect                   rect;
+       CBitmap                 bitmap;
+       CString                 s;
+       
+       err = CWnd::OnCreate( inCreateStruct );
+       require_noerr( err, exit );
+       
+       GetClientRect( rect );
+       mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_NOHSCROLL , rect, this, 
+               IDC_EXPLORER_TREE );
+       
+       
+       ServiceHandlerEntry *           e;
+       
+       // Web Site Handler
+       
+       e = new ServiceHandlerEntry;
+       check( e );
+       e->type                         = "_http._tcp";
+       e->urlScheme            = "http://";
+       e->ref                          = NULL;
+       e->treeItem                     = NULL;
+       e->treeFirst            = true;
+       e->obj                          = this;
+       e->needsLogin           = false;
+       mServiceHandlers.Add( e );
+       
+       s.LoadString( IDS_WEB_SITES );
+       e->treeItem = mTree.InsertItem( s, 0, 0 );
+       mTree.Expand( e->treeItem, TVE_EXPAND );
+       
+       err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
+       require_noerr( err, exit );
+
+       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
+       require_noerr( err, exit );
+
+       m_serviceRefs.push_back(e->ref); 
+
+       // FTP Site Handler
+       
+       e = new ServiceHandlerEntry;
+       check( e );
+       e->type                         = "_ftp._tcp";
+       e->urlScheme            = "ftp://";
+       e->ref                          = NULL;
+       e->treeItem                     = NULL;
+       e->treeFirst            = true;
+       e->obj                          = this;
+       e->needsLogin           = true;
+       mServiceHandlers.Add( e );
+       
+       s.LoadString( IDS_FTP_SITES );
+       e->treeItem = mTree.InsertItem( s, 0, 0 );
+       mTree.Expand( e->treeItem, TVE_EXPAND );
+       
+       err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e );
+       require_noerr( err, exit );
+
+       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
+       require_noerr( err, exit );
+
+       m_serviceRefs.push_back(e->ref); 
+
+       m_imageList.Create(16, 16, ILC_COLORDDB, 1, 0);
+       bitmap.LoadBitmap(IDB_LOGO);
+       m_imageList.Add(&bitmap, (CBitmap*) NULL);
+
+       mTree.SetImageList(&m_imageList, TVSIL_NORMAL);
+       
+exit:
+
+       // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it).
+       if ( err != kNoErr )
+       {
+               s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
+               mTree.DeleteAllItems();
+               mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
+               err = kNoErr;
+       }
+
+       return( err );
+}
+
+//===========================================================================================================================
+//     OnDestroy
+//===========================================================================================================================
+
+void   ExplorerBarWindow::OnDestroy( void ) 
+{
+       // Stop any resolves that may still be pending (shouldn't be any).
+       
+       StopResolve();
+       
+       // Clean up the extant browses
+       while (m_serviceRefs.size() > 0)
+       {
+               //
+               // take the head of the list
+               //
+               DNSServiceRef ref = m_serviceRefs.front();
+
+               //
+               // Stop will remove it from the list
+               //
+               Stop( ref );
+       }
+
+       // Clean up the service handlers.
+       
+       int             i;
+       int             n;
+       
+       n = (int) mServiceHandlers.GetSize();
+       for( i = 0; i < n; ++i )
+       {
+               delete mServiceHandlers[ i ];
+       }
+       
+       CWnd::OnDestroy();
+}
+
+//===========================================================================================================================
+//     OnSize
+//===========================================================================================================================
+
+void   ExplorerBarWindow::OnSize( UINT inType, int inX, int inY ) 
+{
+       CWnd::OnSize( inType, inX, inY );
+       mTree.MoveWindow( 0, 0, inX, inY );
+}
+
+//===========================================================================================================================
+//     OnDoubleClick
+//===========================================================================================================================
+
+void   ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult )
+{
+       HTREEITEM                       item;
+       ServiceInfo *           service;
+       OSStatus                        err;
+       
+       DEBUG_UNUSED( inNMHDR );
+       
+       item = mTree.GetSelectedItem();
+       require( item, exit );
+       
+       service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) );
+       require_quiet( service, exit );
+       
+       err = StartResolve( service );
+       require_noerr( err, exit );
+
+exit:
+       *outResult = 0;
+}
+
+
+//===========================================================================================================================
+//     OnServiceEvent
+//===========================================================================================================================
+
+LONG
+ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam)
+{
+       if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
+    {
+               dlog( kDebugLevelError, "OnServiceEvent: window error\n" );
+    }
+    else
+    {
+               SOCKET sock = (SOCKET) inWParam;
+
+               // iterate thru list
+               ServiceRefList::iterator it;
+
+               for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++)
+               {
+                       DNSServiceRef ref = *it;
+
+                       check(ref != NULL);
+
+                       if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
+                       {
+                               DNSServiceErrorType err;
+
+                               err = DNSServiceProcessResult(ref);
+
+                               if (err != 0)
+                               {
+                                       CString s;
+
+                                       s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE );
+                                       mTree.DeleteAllItems();
+                                       mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST );
+
+                                       Stop(ref);
+                               }
+
+                               break;
+                       }
+               }
+       }
+
+       return ( 0 );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     BrowseCallBack
+//===========================================================================================================================
+
+void DNSSD_API
+       ExplorerBarWindow::BrowseCallBack(
+               DNSServiceRef                   inRef,
+               DNSServiceFlags                 inFlags,
+               uint32_t                                inInterfaceIndex,
+               DNSServiceErrorType     inErrorCode,
+               const char *                    inName, 
+               const char *                    inType, 
+               const char *                    inDomain,       
+               void *                                  inContext )
+{
+       ServiceHandlerEntry *           obj;
+       ServiceInfo *                           service;
+       OSStatus                                        err;
+       
+       DEBUG_UNUSED( inRef );
+       
+       obj             =       NULL;
+       service = NULL;
+       
+       require_noerr( inErrorCode, exit );
+       obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext );
+       check( obj );
+       check( obj->obj );
+       
+       //
+       // set the UI to hold off on updates
+       //
+       obj->obj->mTree.SetRedraw(FALSE);
+
+       try
+       {
+               service = new ServiceInfo;
+               require_action( service, exit, err = kNoMemoryErr );
+               
+               err = UTF8StringToStringObject( inName, service->displayName );
+               check_noerr( err );
+
+               service->name = strdup( inName );
+               require_action( service->name, exit, err = kNoMemoryErr );
+               
+               service->type = strdup( inType );
+               require_action( service->type, exit, err = kNoMemoryErr );
+               
+               service->domain = strdup( inDomain );
+               require_action( service->domain, exit, err = kNoMemoryErr );
+               
+               service->ifi            = inInterfaceIndex;
+               service->handler        = obj;
+
+               service->refs           = 1;
+               
+               if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd   (service);
+               else                               obj->obj->OnServiceRemove(service);
+       
+               service = NULL;
+       }
+       catch( ... )
+       {
+               dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" );
+       }
+       
+exit:
+       //
+       // If no more coming, then update UI
+       //
+       if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0))
+       {
+               obj->obj->mTree.SetRedraw(TRUE);
+               obj->obj->mTree.Invalidate();
+       }
+
+       if( service )
+       {
+               delete service;
+       }
+}
+
+//===========================================================================================================================
+//     OnServiceAdd
+//===========================================================================================================================
+
+LONG   ExplorerBarWindow::OnServiceAdd( ServiceInfo * service )
+{
+       ServiceHandlerEntry *           handler;
+       int                                                     cmp;
+       int                                                     index;
+       
+       
+       check( service );
+       handler = service->handler; 
+       check( handler );
+       
+       cmp = FindServiceArrayIndex( handler->array, *service, index );
+       if( cmp == 0 )
+       {
+               // Found a match so update the item. The index is index + 1 so subtract 1.
+               
+               index -= 1;
+               check( index < handler->array.GetSize() );
+
+               handler->array[ index ]->refs++;
+
+               delete service;
+       }
+       else
+       {
+               HTREEITEM               afterItem;
+               
+               // Insert the new item in sorted order.
+               
+               afterItem = ( index > 0 ) ? handler->array[ index - 1 ]->item : TVI_FIRST;
+               handler->array.InsertAt( index, service );
+               service->item = mTree.InsertItem( service->displayName, handler->treeItem, afterItem );
+               mTree.SetItemData( service->item, (DWORD_PTR) service );
+               
+               // Make sure the item is visible if this is the first time a service was added.
+       
+               if( handler->treeFirst )
+               {
+                       handler->treeFirst = false;
+                       mTree.EnsureVisible( service->item );
+               }
+       }
+       return( 0 );
+}
+
+//===========================================================================================================================
+//     OnServiceRemove
+//===========================================================================================================================
+
+LONG   ExplorerBarWindow::OnServiceRemove( ServiceInfo * service )
+{
+       ServiceHandlerEntry *           handler;
+       int                                                     cmp;
+       int                                                     index;
+       
+       
+       check( service );
+       handler = service->handler; 
+       check( handler );
+       
+       // Search to see if we know about this service instance. If so, remove it from the list.
+       
+       cmp = FindServiceArrayIndex( handler->array, *service, index );
+       check( cmp == 0 );
+
+       if( cmp == 0 )
+       {
+               // Possibly found a match remove the item. The index
+               // is index + 1 so subtract 1.
+               index -= 1;
+               check( index < handler->array.GetSize() );
+
+               if ( --handler->array[ index ]->refs == 0 )
+               {
+                       mTree.DeleteItem( handler->array[ index ]->item );
+                       delete handler->array[ index ];
+                       handler->array.RemoveAt( index );
+               }
+       }
+
+       delete service;
+       return( 0 );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     StartResolve
+//===========================================================================================================================
+
+OSStatus       ExplorerBarWindow::StartResolve( ServiceInfo *inService )
+{
+       OSStatus                err;
+       
+       check( inService );
+       
+       // Stop any current resolve that may be in progress.
+       
+       StopResolve();
+       
+       // Resolve the service.
+       err = DNSServiceResolve( &mResolveServiceRef, 0, 0, 
+               inService->name, inService->type, inService->domain, (DNSServiceResolveReply) ResolveCallBack, inService->handler );
+       require_noerr( err, exit );
+
+       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE);
+       require_noerr( err, exit );
+       
+       m_serviceRefs.push_back(mResolveServiceRef);
+
+exit:
+       return( err );
+}
+
+//===========================================================================================================================
+//     StopResolve
+//===========================================================================================================================
+
+void   ExplorerBarWindow::StopResolve( void )
+{
+       if( mResolveServiceRef )
+       {
+               Stop( mResolveServiceRef );
+               mResolveServiceRef = NULL;
+       }
+}
+
+//===========================================================================================================================
+//     ResolveCallBack
+//===========================================================================================================================
+
+void DNSSD_API
+       ExplorerBarWindow::ResolveCallBack(
+               DNSServiceRef                   inRef,
+               DNSServiceFlags                 inFlags,
+               uint32_t                                inInterfaceIndex,
+               DNSServiceErrorType             inErrorCode,
+               const char *                    inFullName,     
+               const char *                    inHostName, 
+               uint16_t                                inPort,
+               uint16_t                                inTXTSize,
+               const char *                    inTXT,
+               void *                                  inContext )
+{
+       ExplorerBarWindow *                     obj;
+       ServiceHandlerEntry *           handler;
+       OSStatus                                        err;
+       
+       DEBUG_UNUSED( inRef );
+       DEBUG_UNUSED( inFlags );
+       DEBUG_UNUSED( inErrorCode );
+       DEBUG_UNUSED( inFullName );
+       
+       require_noerr( inErrorCode, exit );
+       handler = (ServiceHandlerEntry *) inContext;
+       check( handler );
+       obj = handler->obj;
+       check( obj );
+       
+       try
+       {
+               ResolveInfo *           resolve;
+               int                                     idx;
+               
+               dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName );
+               
+               // Stop resolving after the first good result.
+               
+               obj->StopResolve();
+               
+               // Post a message to the main thread so it can handle it since MFC is not thread safe.
+               
+               resolve = new ResolveInfo;
+               require_action( resolve, exit, err = kNoMemoryErr );
+               
+               UTF8StringToStringObject( inHostName, resolve->host );
+
+               // rdar://problem/3841564
+               // 
+               // strip trailing dot from hostname because some flavors of Windows
+               // have trouble parsing it.
+
+               idx = resolve->host.ReverseFind('.');
+
+               if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx))
+               {
+                       resolve->host.Delete(idx, 1);
+               }
+
+               resolve->port           = ntohs( inPort );
+               resolve->ifi            = inInterfaceIndex;
+               resolve->handler        = handler;
+               
+               err = resolve->txt.SetData( inTXT, inTXTSize );
+               check_noerr( err );
+               
+               obj->OnResolve(resolve);
+       }
+       catch( ... )
+       {
+               dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" );
+       }
+
+exit:
+       return;
+}
+
+//===========================================================================================================================
+//     OnResolve
+//===========================================================================================================================
+
+LONG   ExplorerBarWindow::OnResolve( ResolveInfo * resolve )
+{
+       CString                         url;
+       uint8_t *                       path;
+       uint8_t                         pathSize;
+       char *                          pathPrefix;
+       CString                         username;
+       CString                         password;
+       
+       
+       check( resolve );
+               
+       // Get login info if needed.
+       
+       if( resolve->handler->needsLogin )
+       {
+               LoginDialog             dialog;
+               
+               if( !dialog.GetLogin( username, password ) )
+               {
+                       goto exit;
+               }
+       }
+       
+       // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/".
+       
+       pathPrefix = "";
+       if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 )
+       {
+               uint8_t *       txtData;
+               uint16_t        txtLen; 
+
+               resolve->txt.GetData( &txtData, &txtLen );
+
+               path     = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize);
+
+               if (path == NULL)
+               {
+                       path = (uint8_t*) "";
+                       pathSize = 1;
+               }
+       }
+       else
+       {
+               path            = (uint8_t *) "";
+               pathSize        = 1;
+       }
+
+       // Build the URL in the following format:
+       //
+       // <urlScheme>[<username>[:<password>]@]<name/ip>[<path>]
+
+       url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme );                                  // URL Scheme
+       if( username.GetLength() > 0 )
+       {
+               url.AppendFormat( TEXT( "%s" ), username );                                                                     // Username
+               if( password.GetLength() > 0 )
+               {
+                       url.AppendFormat( TEXT( ":%s" ), password );                                                    // Password
+               }
+               url.AppendFormat( TEXT( "@" ) );
+       }
+       
+       url += resolve->host;                                                                                                                   // Host
+       url.AppendFormat( TEXT( ":%d" ), resolve->port );                                                               // :Port
+       url.AppendFormat( TEXT( "%S" ), pathPrefix );                                                                   // Path Prefix ("/" or empty).
+       url.AppendFormat( TEXT( "%.*S" ), (int) pathSize, (char *) path );                              // Path (possibly empty).
+       
+       // Tell Internet Explorer to go to the URL.
+       
+       check( mOwner );
+       mOwner->GoToURL( url );
+
+exit:
+       delete resolve;
+       return( 0 );
+}
+
+//===========================================================================================================================
+//     Stop
+//===========================================================================================================================
+void ExplorerBarWindow::Stop( DNSServiceRef ref )
+{
+       m_serviceRefs.remove( ref );
+
+       WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0);
+
+       DNSServiceRefDeallocate( ref );
+}
+
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//     FindServiceArrayIndex
+//===========================================================================================================================
+
+DEBUG_LOCAL int        FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex )
+{
+       int             result;
+       int             lo;
+       int             hi;
+       int             mid;
+       
+       result  = -1;
+       mid             = 0;
+       lo              = 0;
+       hi              = (int)( inArray.GetSize() - 1 );
+       while( lo <= hi )
+       {
+               mid = ( lo + hi ) / 2;
+               result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName );
+#if 0
+               if( result == 0 )
+               {
+                       result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi );
+               }
+#endif
+               if( result == 0 )
+               {
+                       break;
+               }
+               else if( result < 0 )
+               {
+                       hi = mid - 1;
+               }
+               else
+               {
+                       lo = mid + 1;
+               }
+       }
+       if( result == 0 )
+       {
+               mid += 1;       // Bump index so new item is inserted after matching item.
+       }
+       else if( result > 0 )
+       {
+               mid += 1;
+       }
+       outIndex = mid;
+       return( result );
+}