]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
mDNSResponder-87.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / PrinterSetupWizardSheet.cpp
diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
new file mode 100644 (file)
index 0000000..02b2952
--- /dev/null
@@ -0,0 +1,1085 @@
+/*
+ * Copyright (c) 1997-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: PrinterSetupWizardSheet.cpp,v $
+Revision 1.17  2004/10/12 18:02:53  shersche
+<rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
+Bug #: 3764873
+
+Revision 1.16  2004/09/13 21:27:22  shersche
+<rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
+Bug #: 3796483
+
+Revision 1.15  2004/09/11 05:59:06  shersche
+<rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
+Bug #: 3785766
+
+Revision 1.14  2004/09/02 01:57:58  cheshire
+<rdar://problem/3783611> Fix incorrect testing of MoreComing flag
+
+Revision 1.13  2004/07/26 21:06:29  shersche
+<rdar://problem/3739200> Removing trailing '.' in hostname
+Bug #: 3739200
+
+Revision 1.12  2004/07/13 21:24:23  rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.11  2004/06/28 00:51:47  shersche
+Move call to EnumPrinters out of browse callback into standalone function
+
+Revision 1.10  2004/06/27 23:06:47  shersche
+code cleanup, make sure EnumPrinters returns non-zero value
+
+Revision 1.9  2004/06/27 15:49:31  shersche
+clean up some cruft in the printer browsing code
+
+Revision 1.8  2004/06/27 08:04:51  shersche
+copy selected printer to prevent printer being deleted out from under
+
+Revision 1.7  2004/06/26 23:27:12  shersche
+support for installing multiple printers of the same name
+
+Revision 1.6  2004/06/26 21:22:39  shersche
+handle spaces in file names
+
+Revision 1.5  2004/06/26 03:19:57  shersche
+clean up warning messages
+
+Submitted by: herscher
+
+Revision 1.4  2004/06/25 02:26:52  shersche
+Normalize key fields in text record entries
+Submitted by: herscher
+
+Revision 1.3  2004/06/24 20:12:07  shersche
+Clean up source code
+Submitted by: herscher
+
+Revision 1.2  2004/06/23 17:58:21  shersche
+<rdar://problem/3701837> eliminated memory leaks on exit
+<rdar://problem/3701926> installation of a printer that is already installed results in a no-op
+Bug #: 3701837, 3701926
+Submitted by: herscher
+
+Revision 1.1  2004/06/18 04:36:57  rpantos
+First checked in
+
+
+*/
+
+#include "stdafx.h"
+#include "PrinterSetupWizardApp.h"
+#include "PrinterSetupWizardSheet.h"
+#include "CommonServices.h"
+#include "DebugServices.h"
+#include "WinServices.h"
+#include "About.h"
+#include <winspool.h>
+#include <tcpxcv.h>
+#include <string>
+
+// unreachable code
+#pragma warning(disable:4702)
+
+
+#if( !TARGET_OS_WINDOWS_CE )
+#      include <mswsock.h>
+#      include <process.h>
+#endif
+
+// Private Messages
+
+#define WM_SERVICE_EVENT                               ( WM_USER + 0x100 )
+#define WM_PROCESS_EVENT                               ( WM_USER + 0x101 )
+
+// Service Types
+
+#define        kPDLDataStreamServiceType               "_pdl-datastream._tcp"
+#define kLPRServiceType                                        "_printer._tcp"
+#define kIPPServiceType                                        "_ipp._tcp"
+
+
+// CPrinterSetupWizardSheet
+
+IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet)
+CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
+       :CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
+       m_selectedPrinter(NULL)
+{
+       m_arrow         =       LoadCursor(0, IDC_ARROW);
+       m_wait          =       LoadCursor(0, IDC_APPSTARTING);
+       m_active        =       m_arrow;
+       
+       Init();
+}
+
+
+CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
+{
+       //
+       // rdar://problem/3701837 memory leaks
+       //
+       // Clean up the ServiceRef and printer list on exit
+       //
+       if (m_pdlBrowser != NULL)
+       {
+               DNSServiceRefDeallocate(m_pdlBrowser);
+               m_pdlBrowser = NULL;
+       }
+
+       while (m_printerList.size() > 0)
+       {
+               Printer * printer = m_printerList.front();
+
+               m_printerList.pop_front();
+
+               delete printer;
+       }
+
+       if (m_selectedPrinter != NULL)
+       {
+               delete m_selectedPrinter;
+       }
+}
+
+
+// ------------------------------------------------------
+// InstallEventHandler
+//
+// Installs an event handler for DNSService events.  
+//     
+int
+CPrinterSetupWizardSheet::InstallEventHandler(EventHandler * handler)
+{
+       PrinterList::iterator iter;
+       
+       m_eventHandlerList.push_back(handler);
+
+       iter = m_printerList.begin();
+
+       while (iter != m_printerList.end())
+       {
+               Printer * printer = *iter++;
+
+               handler->OnAddPrinter(printer, iter != m_printerList.end());
+       }
+       
+       return kNoErr;
+}
+
+
+// ------------------------------------------------------
+// RemoveEventHandler
+//
+// Removes an event handler for DNSService events.  
+//     
+int
+CPrinterSetupWizardSheet::RemoveEventHandler(EventHandler * handler)
+{
+       m_eventHandlerList.remove(handler);
+
+       return kNoErr;
+}
+
+
+
+// ------------------------------------------------------
+// SetSelectedPrinter
+//
+// Manages setting a printer as the printer to install.  Stops
+// any pending resolves.  
+//     
+OSStatus
+CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
+{
+       OSStatus err;
+
+       //
+       // we only want one resolve going on at a time, so we check
+       // state of the m_selectedPrinter
+       //
+       if (m_selectedPrinter != NULL)
+       {
+               //
+               // if we're currently resolving, then stop the resolve
+               //
+               if (m_selectedPrinter->serviceRef)
+               {
+                       err = StopResolve(m_selectedPrinter);
+                       require_noerr(err, exit);
+               }
+
+               delete m_selectedPrinter;
+               m_selectedPrinter = NULL;
+       }
+
+       check( m_selectedPrinter == NULL );
+
+       try
+       {
+               m_selectedPrinter = new Printer;
+       }
+       catch (...)
+       {
+               m_selectedPrinter = NULL;
+       }
+
+       require_action( m_selectedPrinter, exit, err = E_OUTOFMEMORY );
+
+       m_selectedPrinter->window               =       printer->window;
+       m_selectedPrinter->serviceRef   =       NULL;
+       m_selectedPrinter->item                 =       NULL;
+       m_selectedPrinter->ifi                  =       printer->ifi;
+       m_selectedPrinter->name                 =       printer->name;
+       m_selectedPrinter->displayName  =       printer->displayName;
+       m_selectedPrinter->actualName   =       printer->actualName;
+       m_selectedPrinter->type                 =       printer->type;
+       m_selectedPrinter->domain               =       printer->domain;
+       m_selectedPrinter->installed    =       printer->installed;
+       m_selectedPrinter->deflt                =       printer->deflt;
+       m_selectedPrinter->refs                 =       1;
+                       
+       err = StartResolve(m_selectedPrinter);
+       require_noerr(err, exit);
+
+exit:
+
+       return err;
+}
+
+
+// ------------------------------------------------------
+// InstallPrinter
+//
+// Installs a printer with Windows.
+//
+// NOTE: this works one of two ways, depending on whether
+// there are drivers already installed for this printer.
+// If there are, then we can just create a port with XcvData,
+// and then call AddPrinter.  If not, we use the printui.dll
+// to install the printer. Actually installing drivers that
+// are not currently installed is painful, and it's much
+// easier and less error prone to just let printui.dll do
+// the hard work for us.
+//     
+
+OSStatus
+CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
+{
+       PRINTER_DEFAULTS        printerDefaults =       { NULL,  NULL, SERVER_ACCESS_ADMINISTER };
+       DWORD                           dwStatus;
+       DWORD                           cbInputData             =       100;
+       PBYTE                           pOutputData             =       NULL;
+       DWORD                           cbOutputNeeded  =       0;
+       PORT_DATA_1                     portData;
+       HANDLE                          hXcv                    =       NULL;
+       HANDLE                          hPrinter                =       NULL;
+       BOOL                            ok;
+       OSStatus                        err;
+
+       check(printer != NULL);
+       check(printer->installed == false);
+
+       ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       require_noerr( err, exit );
+       
+       //
+       // BUGBUG: MSDN said this is not required, but my experience shows it is required
+       //
+       try
+       {
+               pOutputData = new BYTE[cbInputData];
+       }
+       catch (...)
+       {
+               pOutputData = NULL;
+       }
+
+       require_action( pOutputData, exit, err = kNoMemoryErr );
+       
+       //
+       // setup the port
+       //
+       ZeroMemory(&portData, sizeof(PORT_DATA_1));
+       wcscpy(portData.sztPortName, printer->portName);
+       
+       portData.dwPortNumber   =       printer->portNumber;
+       portData.dwVersion              =       1;
+       
+       portData.dwProtocol     = PROTOCOL_RAWTCP_TYPE;
+       portData.cbSize         = sizeof PORT_DATA_1;
+       portData.dwReserved     = 0L;
+       
+       wcscpy(portData.sztQueue, printer->hostname);
+       wcscpy(portData.sztIPAddress, printer->hostname); 
+       wcscpy(portData.sztHostAddress, printer->hostname);
+               
+       ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData,  &cbOutputNeeded, &dwStatus);
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       require_noerr( err, exit );
+       
+       if (printer->driverInstalled)
+       {
+               PRINTER_INFO_2 pInfo;
+       
+               ZeroMemory(&pInfo, sizeof(pInfo));
+                       
+               pInfo.pPrinterName                      =       printer->actualName.GetBuffer();
+               pInfo.pServerName                       =       NULL;
+               pInfo.pShareName                        =       NULL;
+               pInfo.pPortName                         =       printer->portName.GetBuffer();
+               pInfo.pDriverName                       =       printer->model.GetBuffer();
+               pInfo.pComment                          =       printer->model.GetBuffer();
+               pInfo.pLocation                         =       L"";
+               pInfo.pDevMode                          =       NULL;
+               pInfo.pDevMode                          =       NULL;
+               pInfo.pSepFile                          =       L"";
+               pInfo.pPrintProcessor           =       L"winprint";
+               pInfo.pDatatype                         =       L"RAW";
+               pInfo.pParameters                       =       L"";
+               pInfo.pSecurityDescriptor       =       NULL;
+               pInfo.Attributes                        =       PRINTER_ATTRIBUTE_QUEUED;
+               pInfo.Priority                          =       0;
+               pInfo.DefaultPriority           =       0;
+               pInfo.StartTime                         =       0;
+               pInfo.UntilTime                         =       0;
+       
+               hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
+               err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
+               require_noerr( err, exit );
+       }
+       else
+       {
+               DWORD           dwResult;
+               HANDLE          hThread;
+               unsigned        threadID;
+                       
+       
+               m_processFinished = false;
+       
+               //
+               // create the thread
+               //
+               hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallPrinterThread, printer, 0, &threadID );
+               err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
+               require_noerr( err, exit );
+                       
+               //
+               // go modal
+               //
+               while (!m_processFinished)
+               {
+                       MSG msg;
+       
+                       GetMessage( &msg, m_hWnd, 0, 0 );
+                       TranslateMessage(&msg);
+                       DispatchMessage(&msg);
+               }
+       
+               //
+               // Wait until child process exits.
+               //
+               dwResult = WaitForSingleObject( hThread, INFINITE );
+               err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
+               require_noerr( err, exit );
+       }
+
+       printer->installed = true;
+
+       //
+       // if the user specified a default printer, set it
+       //
+       if (printer->deflt)
+       {
+               ok = SetDefaultPrinter(printer->actualName);
+               err = translate_errno( ok, errno_compat(), err = kUnknownErr );
+               require_noerr( err, exit );
+       }
+
+exit:
+
+       if (hPrinter != NULL)
+       {
+               ClosePrinter(hPrinter);
+       }
+
+       if (hXcv != NULL)
+       {
+               ClosePrinter(hXcv);
+       }
+
+       if (pOutputData != NULL)
+       {
+               delete [] pOutputData;
+       }
+
+       return err;
+}
+
+
+BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
+ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent )
+ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
+ON_WM_SETCURSOR()
+END_MESSAGE_MAP()
+
+
+// ------------------------------------------------------
+// OnCommand
+//
+// Traps when the user hits Finish  
+//     
+BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
+{
+       //
+       // Check if this is OK
+       //
+       if (wParam == ID_WIZFINISH)              // If OK is hit...
+       {
+               OnOK();
+       }
+       return CPropertySheet::OnCommand(wParam, lParam);
+}
+
+
+// ------------------------------------------------------
+// OnInitDialog
+//
+// Initializes this Dialog object.  We start the browse here,
+// so that printers show up instantly when the user clicks
+// the next button.  
+//     
+BOOL CPrinterSetupWizardSheet::OnInitDialog()
+{
+       OSStatus err;
+
+       CPropertySheet::OnInitDialog();
+       
+       //
+       // setup the DNS-SD browsing
+       //
+       err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLDataStreamServiceType, NULL, OnBrowse, this );
+       require_noerr( err, exit );
+
+       m_serviceRefList.push_back(m_pdlBrowser);
+
+       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(m_pdlBrowser), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
+       require_noerr( err, exit );
+
+       LoadPrinterNames();
+
+exit:
+
+       if (err != kNoErr)
+       {
+               WizardException exc;
+
+               exc.text.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_TEXT);
+               exc.caption.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_CAPTION);
+
+               throw(exc);
+       }
+
+       return TRUE;
+}
+
+
+// ------------------------------------------------------
+// OnSetCursor
+//
+// This is called when Windows wants to know what cursor
+// to display.  So we tell it.  
+//     
+BOOL
+CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message)
+{
+       DEBUG_UNUSED(pWnd);
+       DEBUG_UNUSED(nHitTest);
+       DEBUG_UNUSED(message);
+
+       SetCursor(m_active);
+       return TRUE;
+}
+
+
+// ------------------------------------------------------
+// OnContextMenu
+//
+// This is not fully implemented yet.  
+//     
+
+void
+CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos)
+{
+       DEBUG_UNUSED(pWnd);
+       DEBUG_UNUSED(pos);
+
+       CAbout dlg;
+
+       dlg.DoModal();
+}
+
+
+// ------------------------------------------------------
+// OnOK
+//
+// This is called when the user hits the "Finish" button  
+//     
+void
+CPrinterSetupWizardSheet::OnOK()
+{
+       check ( m_selectedPrinter != NULL );
+
+       SetWizardButtons( PSWIZB_DISABLEDFINISH );
+
+       if ( InstallPrinter( m_selectedPrinter ) != kNoErr )
+       {
+               CString caption;
+               CString message;
+
+               caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
+               message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
+
+               MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
+       }
+}
+
+
+// CPrinterSetupWizardSheet message handlers
+
+void CPrinterSetupWizardSheet::Init(void)
+{
+       AddPage(&m_pgFirst);
+       AddPage(&m_pgSecond);
+       AddPage(&m_pgThird);
+       AddPage(&m_pgFourth);
+
+       m_psh.dwFlags &= (~PSH_HASHELP);
+
+       m_psh.dwFlags |= PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER;
+       m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
+       m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON);
+
+       m_psh.hInstance = AfxGetInstanceHandle();
+
+       SetWizardMode();
+}
+
+
+LONG
+CPrinterSetupWizardSheet::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 begin = m_serviceRefList.begin();
+               ServiceRefList::iterator end   = m_serviceRefList.end();
+
+               while (begin != end)
+               {
+                       DNSServiceRef ref = *begin++;
+
+                       check(ref != NULL);
+
+                       if ((SOCKET) DNSServiceRefSockFD(ref) == sock)
+                       {
+                               DNSServiceProcessResult(ref);
+                               break;
+                       }
+               }
+       }
+
+       return ( 0 );
+}
+
+
+LONG
+CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
+{
+       DEBUG_UNUSED(inWParam);
+       DEBUG_UNUSED(inLParam);
+
+       m_processFinished = true;
+
+       return 0;
+}
+
+
+void DNSSD_API
+CPrinterSetupWizardSheet::OnBrowse(
+                                                               DNSServiceRef                   inRef,
+                                                               DNSServiceFlags                 inFlags,
+                                                               uint32_t                                inInterfaceIndex,
+                                                               DNSServiceErrorType     inErrorCode,
+                                                               const char *                    inName, 
+                                                               const char *                    inType, 
+                                                               const char *                    inDomain,       
+                                                               void *                                  inContext )
+{
+       DEBUG_UNUSED(inRef);
+
+       CPrinterSetupWizardSheet        *       self;
+       Printer                                         *       printer;
+       EventHandlerList::iterator              it;
+       DWORD                                                   printerNameCount;
+       bool                                                    moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing);
+
+       require_noerr( inErrorCode, exit );
+       
+       self = reinterpret_cast <CPrinterSetupWizardSheet*>( inContext );
+       require_quiet( self, exit );
+
+       printer = self->LookUp(inName);
+
+       if (inFlags & kDNSServiceFlagsAdd)
+       {
+               OSStatus err;
+
+               if (printer != NULL)
+               {
+                       printer->refs++;
+               }
+               else
+               {
+                       try
+                       {
+                               printer = new Printer;
+                       }
+                       catch (...)
+                       {
+                               printer = NULL;
+                       }
+
+                       require_action( printer, exit, err = E_OUTOFMEMORY );
+
+                       printer->window         =       self;
+                       printer->ifi            =       inInterfaceIndex;
+                       printer->name           =       inName;
+                       err = UTF8StringToStringObject(inName, printer->displayName);
+                       check_noerr( err );
+                       printer->actualName     =       printer->displayName;
+
+                       //
+                       // Compare this name against printers that are already installed
+                       // to avoid name clashes.  Rename as necessary
+                       // to come up with a unique name.
+                       //
+                       printerNameCount = 2;
+
+                       for (;;)
+                       {
+                               PrinterNameMap::iterator it;
+
+                               it = self->m_printerNames.find(printer->actualName);
+
+                               if (it != self->m_printerNames.end())
+                               {
+                                       printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount);
+                               }
+                               else
+                               {
+                                       break;
+                               }
+
+                               printerNameCount++;
+                       }
+
+                       printer->type           =       inType;
+                       printer->domain         =       inDomain;
+                       printer->installed      =       false;
+                       printer->deflt          =       false;
+                       printer->refs           =       1;
+                       
+                       self->m_printerList.push_back( printer );
+
+                       //
+                       // now invoke event handlers for AddPrinter event
+                       //
+                       for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
+                       {
+                               EventHandler * handler = *it;
+
+                               handler->OnAddPrinter(printer, moreComing);
+                       }
+               }
+       }
+       else
+       {
+               if ((printer != NULL) && (--printer->refs == 0))
+               {
+                       //
+                       // now invoke event handlers for RemovePrinter event
+                       //
+                       for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++)
+                       {
+                               EventHandler * handler = *it;
+
+                               handler->OnRemovePrinter(printer, moreComing);
+                       }
+
+                       self->m_printerList.remove(printer);
+
+                       //
+                       // check to see if we've selected this printer
+                       //
+                       if (self->m_selectedPrinter == printer)
+                       {
+                               //
+                               // this guy is being removed while we're resolving it...so let's 
+                               // stop the resolve
+                               //
+                               if (printer->serviceRef != NULL)
+                               {
+                                       self->StopResolve(printer);
+                               }
+
+                               self->m_selectedPrinter = NULL;
+                       }
+
+                       delete printer;
+               }
+       }
+
+exit:
+
+       return;
+}
+
+
+void DNSSD_API
+CPrinterSetupWizardSheet::OnResolve(
+                                                               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 )
+{
+       DEBUG_UNUSED(inFullName);
+       DEBUG_UNUSED(inInterfaceIndex);
+       DEBUG_UNUSED(inFlags);
+       DEBUG_UNUSED(inRef);
+
+       Printer                                         *       printer;
+       CPrinterSetupWizardSheet        *       self;
+       EventHandlerList::iterator              it1;
+       EventHandlerList::iterator              it2;
+       int                                                             idx;
+       OSStatus                                                err;
+
+       require_noerr( inErrorCode, exit );
+
+       printer = reinterpret_cast<Printer*>( inContext );
+       require_quiet( printer, exit);
+
+       self = printer->window;
+       require_quiet( self, exit );
+
+       err = self->StopResolve(printer);
+       require_noerr(err, exit);
+       
+       //
+       // hold on to the hostname...
+       //
+       err = UTF8StringToStringObject( inHostName, printer->hostname );
+       require_noerr( err, exit );
+
+       //
+       // <rdar://problem/3739200> remove the trailing dot on hostname
+       //
+       idx = printer->hostname.ReverseFind('.');
+
+       if ((idx > 1) && ((printer->hostname.GetLength() - 1) == idx))
+       {
+               printer->hostname.Delete(idx, 1);
+       }
+
+       //
+       // hold on to the port
+       //
+       printer->portNumber = ntohs(inPort);
+
+       //
+       // parse the text record.  we create a stringlist of text record
+       // entries that can be interrogated later
+       //
+       while (inTXTSize)
+       {
+               char buf[256];
+
+               unsigned char num = *inTXT;
+               check( (int) num < inTXTSize );
+
+               memset(buf, 0, sizeof(buf));
+               memcpy(buf, inTXT + 1, num);
+               
+               inTXTSize -= (num + 1);
+               inTXT += (num + 1);
+
+               CString elem;
+
+               err = UTF8StringToStringObject( buf, elem );
+               require_noerr( err, exit );
+
+               int curPos = 0;
+
+               CString key = elem.Tokenize(L"=", curPos);
+               CString val = elem.Tokenize(L"=", curPos);
+
+               key.MakeLower();
+
+               if ((key == L"usb_mfg") || (key == L"usb_manufacturer"))
+               {
+                       printer->usb_MFG = val;
+               }
+               else if ((key == L"usb_mdl") || (key == L"usb_model"))
+               {
+                       printer->usb_MDL = val;
+               }
+               else if (key == L"description")
+               {
+                       printer->description = val;
+               }
+               else if (key == L"product")
+               {
+                       printer->product = val;
+               }
+       }
+
+       //
+       // now invoke event handlers for Resolve event
+       //
+       it1 = self->m_eventHandlerList.begin();
+       it2 = self->m_eventHandlerList.end();
+
+       while (it1 != it2)
+       {
+               EventHandler * handler = *it1++;
+
+               handler->OnResolvePrinter(printer);
+       }
+
+exit:
+
+       return;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::LoadPrinterNames()
+{
+       PBYTE           buffer  =       NULL;
+       OSStatus        err             = 0;
+
+       //
+       // rdar://problem/3701926 - Printer can't be installed twice
+       //
+       // First thing we want to do is make sure the printer isn't already installed.
+       // If the printer name is found, we'll try and rename it until we
+       // find a unique name
+       //
+       DWORD dwNeeded = 0, dwNumPrinters = 0;
+
+       BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters);
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+
+       if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0))
+       {
+               try
+               {
+                       buffer = new unsigned char[dwNeeded];
+               }
+               catch (...)
+               {
+                       buffer = NULL;
+               }
+       
+               require_action( buffer, exit, kNoMemoryErr );
+               ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters);
+               err = translate_errno( ok, errno_compat(), kUnknownErr );
+               require_noerr( err, exit );
+
+               for (DWORD index = 0; index < dwNumPrinters; index++)
+               {
+                       PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4));
+
+                       m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName;
+               }
+       }
+
+exit:
+
+       if (buffer != NULL)
+       {
+               delete [] buffer;
+       }
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StartResolve(Printer * printer)
+{
+       OSStatus err;
+
+       check( printer );
+
+       err = DNSServiceResolve( &printer->serviceRef, 0, 0, printer->name.c_str(), printer->type.c_str(), printer->domain.c_str(), (DNSServiceResolveReply) OnResolve, printer );
+       require_noerr( err, exit);
+
+       m_serviceRefList.push_back(printer->serviceRef);
+
+       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE);
+       require_noerr( err, exit );
+
+       //
+       // set the cursor to arrow+hourglass
+       //
+       m_active = m_wait;
+       SetCursor(m_active);
+
+exit:
+
+       return err;
+}
+
+
+OSStatus
+CPrinterSetupWizardSheet::StopResolve(Printer * printer)
+{
+       OSStatus err;
+
+       check( printer );
+       check( printer->serviceRef );
+
+       m_serviceRefList.remove( printer->serviceRef );
+
+       err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, 0, 0);
+       check(err == 0);        
+
+       DNSServiceRefDeallocate( printer->serviceRef );
+
+       printer->serviceRef = NULL;
+       
+       //
+       // set the cursor back to normal
+       //
+       m_active = m_arrow;
+       SetCursor(m_active);
+
+       return kNoErr;
+}
+
+
+Printer*
+CPrinterSetupWizardSheet::LookUp(const char * inName)
+{
+       PrinterList::iterator it1 = m_printerList.begin();
+       PrinterList::iterator it2 = m_printerList.end();
+
+       while (it1 != it2)
+       {
+               Printer * printer = *it1++;
+
+               if (printer->name == inName)
+               {
+                       return printer;
+               }
+       }
+
+       return NULL;
+}
+
+
+unsigned WINAPI
+CPrinterSetupWizardSheet::InstallPrinterThread( LPVOID inParam )
+{
+       check( inParam );
+               
+       Printer                 *       printer = (Printer*) inParam;
+       CString                         actualName;
+       CString                         command;
+       DWORD                           exitCode = 0;
+       DWORD                           dwResult;
+       OSStatus                        err;
+       STARTUPINFO                     si;
+       PROCESS_INFORMATION pi;
+       BOOL                            ok;
+
+       ZeroMemory( &si, sizeof(si) );
+       si.cb = sizeof(si);
+       ZeroMemory( &pi, sizeof(pi) );
+
+       //
+       // <rdar://problem/3764873> Escape '\', '@', '"' characters which seem to cause problems for printui
+       //
+
+       actualName = printer->actualName;
+
+       actualName.Replace(L"\\", L"\\\\");
+       actualName.Replace(L"@", L"\\@");
+       actualName.Replace(L"\"", L"\\\"");
+       
+       command.Format(L"rundll32.exe printui.dll,PrintUIEntry /if /b \"%s\" /f \"%s\" /r \"%s\" /m \"%s\"", (LPCTSTR) actualName, (LPCTSTR) printer->infFileName, (LPCTSTR) printer->portName, (LPCTSTR) printer->model);
+
+       ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       require_noerr( err, exit );
+
+       dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
+       translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
+       require_noerr( err, exit );
+
+       ok = GetExitCodeProcess( pi.hProcess, &exitCode );
+       err = translate_errno( ok, errno_compat(), kUnknownErr );
+       require_noerr( err, exit );
+
+       //
+       // Close process and thread handles. 
+       //
+       CloseHandle( pi.hProcess );
+       CloseHandle( pi.hThread );
+
+exit:
+
+       //
+       // alert the main thread
+       //
+       printer->window->PostMessage( WM_PROCESS_EVENT, err, exitCode );
+
+       return 0;
+}