2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: PrinterSetupWizardSheet.cpp,v $
26 Revision 1.22 2005/01/25 18:49:43 shersche
27 Get icon resources from resource DLL
29 Revision 1.21 2005/01/10 01:09:32 shersche
30 Use the "note" key to populate pLocation field when setting up printer
32 Revision 1.20 2005/01/03 19:05:01 shersche
33 Store pointer to instance of wizard sheet so that print driver install thread sends a window message to the correct window
35 Revision 1.19 2004/12/31 07:23:53 shersche
36 Don't modify the button setting in SetSelectedPrinter()
38 Revision 1.18 2004/12/29 18:53:38 shersche
39 <rdar://problem/3725106>
40 <rdar://problem/3737413> Added support for LPR and IPP protocols as well as support for obtaining multiple text records. Reorganized and simplified codebase.
41 Bug #: 3725106, 3737413
43 Revision 1.17 2004/10/12 18:02:53 shersche
44 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
47 Revision 1.16 2004/09/13 21:27:22 shersche
48 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
51 Revision 1.15 2004/09/11 05:59:06 shersche
52 <rdar://problem/3785766> Fix code that generates unique printer names based on currently installed printers
55 Revision 1.14 2004/09/02 01:57:58 cheshire
56 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
58 Revision 1.13 2004/07/26 21:06:29 shersche
59 <rdar://problem/3739200> Removing trailing '.' in hostname
62 Revision 1.12 2004/07/13 21:24:23 rpantos
63 Fix for <rdar://problem/3701120>.
65 Revision 1.11 2004/06/28 00:51:47 shersche
66 Move call to EnumPrinters out of browse callback into standalone function
68 Revision 1.10 2004/06/27 23:06:47 shersche
69 code cleanup, make sure EnumPrinters returns non-zero value
71 Revision 1.9 2004/06/27 15:49:31 shersche
72 clean up some cruft in the printer browsing code
74 Revision 1.8 2004/06/27 08:04:51 shersche
75 copy selected printer to prevent printer being deleted out from under
77 Revision 1.7 2004/06/26 23:27:12 shersche
78 support for installing multiple printers of the same name
80 Revision 1.6 2004/06/26 21:22:39 shersche
81 handle spaces in file names
83 Revision 1.5 2004/06/26 03:19:57 shersche
84 clean up warning messages
86 Submitted by: herscher
88 Revision 1.4 2004/06/25 02:26:52 shersche
89 Normalize key fields in text record entries
90 Submitted by: herscher
92 Revision 1.3 2004/06/24 20:12:07 shersche
94 Submitted by: herscher
96 Revision 1.2 2004/06/23 17:58:21 shersche
97 <rdar://problem/3701837> eliminated memory leaks on exit
98 <rdar://problem/3701926> installation of a printer that is already installed results in a no-op
99 Bug #: 3701837, 3701926
100 Submitted by: herscher
102 Revision 1.1 2004/06/18 04:36:57 rpantos
109 #include "PrinterSetupWizardApp.h"
110 #include "PrinterSetupWizardSheet.h"
111 #include "CommonServices.h"
112 #include "DebugServices.h"
113 #include "WinServices.h"
115 #include <winspool.h>
120 #pragma warning(disable:4702)
123 #if( !TARGET_OS_WINDOWS_CE )
124 # include <mswsock.h>
125 # include <process.h>
130 #define WM_PROCESS_EVENT ( WM_USER + 0x100 )
133 // CPrinterSetupWizardSheet
134 CPrinterSetupWizardSheet
* CPrinterSetupWizardSheet::m_self
;
136 IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet
, CPropertySheet
)
137 CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption
, CWnd
* pParentWnd
, UINT iSelectPage
)
138 :CPropertySheet(nIDCaption
, pParentWnd
, iSelectPage
),
139 m_selectedPrinter(NULL
),
140 m_driverThreadExitCode( 0 ),
141 m_driverThreadFinished( false )
143 m_arrow
= LoadCursor(0, IDC_ARROW
);
144 m_wait
= LoadCursor(0, IDC_APPSTARTING
);
152 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
154 if ( m_selectedPrinter
!= NULL
)
156 delete m_selectedPrinter
;
157 m_selectedPrinter
= NULL
;
164 // ------------------------------------------------------
165 // SetSelectedPrinter
167 // Manages setting a printer as the printer to install. Stops
168 // any pending resolves.
171 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer
* printer
)
173 check( !printer
|| ( printer
!= m_selectedPrinter
) );
175 m_selectedPrinter
= printer
;
179 // ------------------------------------------------------
182 // Installs a printer with Windows.
184 // NOTE: this works one of two ways, depending on whether
185 // there are drivers already installed for this printer.
186 // If there are, then we can just create a port with XcvData,
187 // and then call AddPrinter. If not, we use the printui.dll
188 // to install the printer. Actually installing drivers that
189 // are not currently installed is painful, and it's much
190 // easier and less error prone to just let printui.dll do
191 // the hard work for us.
195 CPrinterSetupWizardSheet::InstallPrinter(Printer
* printer
)
201 service
= printer
->services
.front();
205 // if the driver isn't installed, then install it
207 if ( !printer
->driverInstalled
)
213 m_driverThreadFinished
= false;
218 hThread
= (HANDLE
) _beginthreadex_compat( NULL
, 0, InstallDriverThread
, printer
, 0, &threadID
);
219 err
= translate_errno( hThread
, (OSStatus
) GetLastError(), kUnknownErr
);
220 require_noerr( err
, exit
);
225 while (!m_driverThreadFinished
)
229 GetMessage( &msg
, m_hWnd
, 0, 0 );
230 TranslateMessage(&msg
);
231 DispatchMessage(&msg
);
235 // Wait until child process exits.
237 dwResult
= WaitForSingleObject( hThread
, INFINITE
);
238 err
= translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
239 require_noerr( err
, exit
);
242 // check the return value of thread
244 require_noerr( m_driverThreadExitCode
, exit
);
247 // now we know that the driver was successfully installed
249 printer
->driverInstalled
= true;
252 if ( service
->type
== kPDLServiceType
)
254 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_RAWTCP_TYPE
);
255 require_noerr( err
, exit
);
257 else if ( service
->type
== kLPRServiceType
)
259 err
= InstallPrinterPDLAndLPR( printer
, service
, PROTOCOL_LPR_TYPE
);
260 require_noerr( err
, exit
);
262 else if ( service
->type
== kIPPServiceType
)
264 err
= InstallPrinterIPP( printer
, service
);
265 require_noerr( err
, exit
);
270 require_noerr( err
, exit
);
273 printer
->installed
= true;
276 // if the user specified a default printer, set it
280 ok
= SetDefaultPrinter( printer
->actualName
);
281 err
= translate_errno( ok
, errno_compat(), err
= kUnknownErr
);
282 require_noerr( err
, exit
);
292 CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer
* printer
, Service
* service
, DWORD protocol
)
294 PRINTER_DEFAULTS printerDefaults
= { NULL
, NULL
, SERVER_ACCESS_ADMINISTER
};
296 DWORD cbInputData
= 100;
297 PBYTE pOutputData
= NULL
;
298 DWORD cbOutputNeeded
= 0;
299 PORT_DATA_1 portData
;
300 PRINTER_INFO_2 pInfo
;
302 HANDLE hPrinter
= NULL
;
307 check(printer
!= NULL
);
308 check(printer
->installed
== false);
310 q
= service
->queues
.front();
313 ok
= OpenPrinter(L
",XcvMonitor Standard TCP/IP Port", &hXcv
, &printerDefaults
);
314 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
315 require_noerr( err
, exit
);
318 // BUGBUG: MSDN said this is not required, but my experience shows it is required
322 pOutputData
= new BYTE
[cbInputData
];
329 require_action( pOutputData
, exit
, err
= kNoMemoryErr
);
334 ZeroMemory(&portData
, sizeof(PORT_DATA_1
));
335 wcscpy(portData
.sztPortName
, printer
->portName
);
337 portData
.dwPortNumber
= service
->portNumber
;
338 portData
.dwVersion
= 1;
340 portData
.dwProtocol
= protocol
;
341 portData
.cbSize
= sizeof PORT_DATA_1
;
342 portData
.dwReserved
= 0L;
344 wcscpy(portData
.sztQueue
, q
->name
);
345 wcscpy(portData
.sztIPAddress
, service
->hostname
);
346 wcscpy(portData
.sztHostAddress
, service
->hostname
);
348 ok
= XcvData(hXcv
, L
"AddPort", (PBYTE
) &portData
, sizeof(PORT_DATA_1
), pOutputData
, cbInputData
, &cbOutputNeeded
, &dwStatus
);
349 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
350 require_noerr( err
, exit
);
355 ZeroMemory(&pInfo
, sizeof(pInfo
));
357 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
358 pInfo
.pServerName
= NULL
;
359 pInfo
.pShareName
= NULL
;
360 pInfo
.pPortName
= printer
->portName
.GetBuffer();
361 pInfo
.pDriverName
= printer
->model
.GetBuffer();
362 pInfo
.pComment
= printer
->model
.GetBuffer();
363 pInfo
.pLocation
= service
->location
.GetBuffer();
364 pInfo
.pDevMode
= NULL
;
365 pInfo
.pDevMode
= NULL
;
366 pInfo
.pSepFile
= L
"";
367 pInfo
.pPrintProcessor
= L
"winprint";
368 pInfo
.pDatatype
= L
"RAW";
369 pInfo
.pParameters
= L
"";
370 pInfo
.pSecurityDescriptor
= NULL
;
371 pInfo
.Attributes
= PRINTER_ATTRIBUTE_QUEUED
;
373 pInfo
.DefaultPriority
= 0;
377 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
) &pInfo
);
378 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
379 require_noerr( err
, exit
);
383 if (hPrinter
!= NULL
)
385 ClosePrinter(hPrinter
);
393 if (pOutputData
!= NULL
)
395 delete [] pOutputData
;
403 CPrinterSetupWizardSheet::InstallPrinterIPP(Printer
* printer
, Service
* service
)
405 DEBUG_UNUSED( service
);
407 HANDLE hPrinter
= NULL
;
408 PRINTER_INFO_2 pInfo
;
414 ZeroMemory(&pInfo
, sizeof(PRINTER_INFO_2
));
416 pInfo
.pPrinterName
= printer
->actualName
.GetBuffer();
417 pInfo
.pPortName
= printer
->portName
.GetBuffer();
418 pInfo
.pDriverName
= printer
->model
.GetBuffer();
419 pInfo
.pPrintProcessor
= L
"winprint";
420 pInfo
.pLocation
= service
->location
.GetBuffer();
421 pInfo
.Attributes
= PRINTER_ATTRIBUTE_NETWORK
| PRINTER_ATTRIBUTE_LOCAL
;
423 hPrinter
= AddPrinter(NULL
, 2, (LPBYTE
)&pInfo
);
424 err
= translate_errno( hPrinter
, errno_compat(), kUnknownErr
);
425 require_noerr( err
, exit
);
429 if ( hPrinter
!= NULL
)
431 ClosePrinter(hPrinter
);
438 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet
, CPropertySheet
)
439 ON_MESSAGE( WM_PROCESS_EVENT
, OnProcessEvent
)
445 // ------------------------------------------------------
448 // Traps when the user hits Finish
450 BOOL
CPrinterSetupWizardSheet::OnCommand(WPARAM wParam
, LPARAM lParam
)
453 // Check if this is OK
455 if (wParam
== ID_WIZFINISH
) // If OK is hit...
460 return CPropertySheet::OnCommand(wParam
, lParam
);
464 // ------------------------------------------------------
467 // Initializes this Dialog object.
469 BOOL
CPrinterSetupWizardSheet::OnInitDialog()
471 CPropertySheet::OnInitDialog();
477 // ------------------------------------------------------
480 // This is called when Windows wants to know what cursor
481 // to display. So we tell it.
484 CPrinterSetupWizardSheet::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
487 DEBUG_UNUSED(nHitTest
);
488 DEBUG_UNUSED(message
);
495 // ------------------------------------------------------
498 // This is not fully implemented yet.
502 CPrinterSetupWizardSheet::OnContextMenu(CWnd
* pWnd
, CPoint pos
)
513 // ------------------------------------------------------
516 // This is called when the user hits the "Finish" button
519 CPrinterSetupWizardSheet::OnOK()
521 check ( m_selectedPrinter
!= NULL
);
523 SetWizardButtons( PSWIZB_DISABLEDFINISH
);
525 if ( InstallPrinter( m_selectedPrinter
) != kNoErr
)
530 caption
.LoadString(IDS_INSTALL_ERROR_CAPTION
);
531 message
.LoadString(IDS_INSTALL_ERROR_MESSAGE
);
533 MessageBox(message
, caption
, MB_OK
|MB_ICONEXCLAMATION
);
538 // CPrinterSetupWizardSheet message handlers
540 void CPrinterSetupWizardSheet::Init(void)
543 AddPage(&m_pgSecond
);
545 AddPage(&m_pgFourth
);
547 m_psh
.dwFlags
&= (~PSH_HASHELP
);
549 m_psh
.dwFlags
|= PSH_WIZARD97
|PSH_WATERMARK
|PSH_HEADER
;
550 m_psh
.pszbmWatermark
= MAKEINTRESOURCE(IDB_WATERMARK
);
551 m_psh
.pszbmHeader
= MAKEINTRESOURCE(IDB_BANNER_ICON
);
553 m_psh
.hInstance
= GetNonLocalizedResources();
560 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam
, LPARAM inLParam
)
562 DEBUG_UNUSED(inLParam
);
564 m_driverThreadExitCode
= (DWORD
) inWParam
;
565 m_driverThreadFinished
= true;
572 CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam
)
574 Printer
* printer
= (Printer
*) inParam
;
579 PROCESS_INFORMATION pi
;
586 // because we're calling endthreadex(), C++ objects won't be cleaned up
587 // correctly. we'll nest the CString 'command' inside a block so
588 // that it's destructor will be invoked.
593 ZeroMemory( &si
, sizeof(si
) );
595 ZeroMemory( &pi
, sizeof(pi
) );
597 command
.Format(L
"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR
) printer
->model
, (LPCTSTR
) printer
->infFileName
);
599 ok
= CreateProcess(NULL
, command
.GetBuffer(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
600 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
601 require_noerr( err
, exit
);
603 dwResult
= WaitForSingleObject( pi
.hProcess
, INFINITE
);
604 translate_errno( dwResult
== WAIT_OBJECT_0
, errno_compat(), err
= kUnknownErr
);
605 require_noerr( err
, exit
);
607 ok
= GetExitCodeProcess( pi
.hProcess
, &exitCode
);
608 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
609 require_noerr( err
, exit
);
615 // Close process and thread handles.
619 CloseHandle( pi
.hProcess
);
624 CloseHandle( pi
.hThread
);
628 // alert the main thread
630 m_self
->PostMessage( WM_PROCESS_EVENT
, err
, exitCode
);
632 _endthreadex_compat( 0 );