]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp
mDNSResponder-98.tar.gz
[apple/mdnsresponder.git] / Clients / PrinterSetupWizard / PrinterSetupWizardSheet.cpp
1 /*
2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22
23 Change History (most recent first):
24
25 $Log: PrinterSetupWizardSheet.cpp,v $
26 Revision 1.22 2005/01/25 18:49:43 shersche
27 Get icon resources from resource DLL
28
29 Revision 1.21 2005/01/10 01:09:32 shersche
30 Use the "note" key to populate pLocation field when setting up printer
31
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
34
35 Revision 1.19 2004/12/31 07:23:53 shersche
36 Don't modify the button setting in SetSelectedPrinter()
37
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
42
43 Revision 1.17 2004/10/12 18:02:53 shersche
44 <rdar://problem/3764873> Escape '/', '@', '"' characters in printui command.
45 Bug #: 3764873
46
47 Revision 1.16 2004/09/13 21:27:22 shersche
48 <rdar://problem/3796483> Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks
49 Bug #: 3796483
50
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
53 Bug #: 3785766
54
55 Revision 1.14 2004/09/02 01:57:58 cheshire
56 <rdar://problem/3783611> Fix incorrect testing of MoreComing flag
57
58 Revision 1.13 2004/07/26 21:06:29 shersche
59 <rdar://problem/3739200> Removing trailing '.' in hostname
60 Bug #: 3739200
61
62 Revision 1.12 2004/07/13 21:24:23 rpantos
63 Fix for <rdar://problem/3701120>.
64
65 Revision 1.11 2004/06/28 00:51:47 shersche
66 Move call to EnumPrinters out of browse callback into standalone function
67
68 Revision 1.10 2004/06/27 23:06:47 shersche
69 code cleanup, make sure EnumPrinters returns non-zero value
70
71 Revision 1.9 2004/06/27 15:49:31 shersche
72 clean up some cruft in the printer browsing code
73
74 Revision 1.8 2004/06/27 08:04:51 shersche
75 copy selected printer to prevent printer being deleted out from under
76
77 Revision 1.7 2004/06/26 23:27:12 shersche
78 support for installing multiple printers of the same name
79
80 Revision 1.6 2004/06/26 21:22:39 shersche
81 handle spaces in file names
82
83 Revision 1.5 2004/06/26 03:19:57 shersche
84 clean up warning messages
85
86 Submitted by: herscher
87
88 Revision 1.4 2004/06/25 02:26:52 shersche
89 Normalize key fields in text record entries
90 Submitted by: herscher
91
92 Revision 1.3 2004/06/24 20:12:07 shersche
93 Clean up source code
94 Submitted by: herscher
95
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
101
102 Revision 1.1 2004/06/18 04:36:57 rpantos
103 First checked in
104
105
106 */
107
108 #include "stdafx.h"
109 #include "PrinterSetupWizardApp.h"
110 #include "PrinterSetupWizardSheet.h"
111 #include "CommonServices.h"
112 #include "DebugServices.h"
113 #include "WinServices.h"
114 #include "About.h"
115 #include <winspool.h>
116 #include <tcpxcv.h>
117 #include <string>
118
119 // unreachable code
120 #pragma warning(disable:4702)
121
122
123 #if( !TARGET_OS_WINDOWS_CE )
124 # include <mswsock.h>
125 # include <process.h>
126 #endif
127
128 // Private Messages
129
130 #define WM_PROCESS_EVENT ( WM_USER + 0x100 )
131
132
133 // CPrinterSetupWizardSheet
134 CPrinterSetupWizardSheet * CPrinterSetupWizardSheet::m_self;
135
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 )
142 {
143 m_arrow = LoadCursor(0, IDC_ARROW);
144 m_wait = LoadCursor(0, IDC_APPSTARTING);
145 m_active = m_arrow;
146 m_self = this;
147
148 Init();
149 }
150
151
152 CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet()
153 {
154 if ( m_selectedPrinter != NULL )
155 {
156 delete m_selectedPrinter;
157 m_selectedPrinter = NULL;
158 }
159
160 m_self = NULL;
161 }
162
163
164 // ------------------------------------------------------
165 // SetSelectedPrinter
166 //
167 // Manages setting a printer as the printer to install. Stops
168 // any pending resolves.
169 //
170 void
171 CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer)
172 {
173 check( !printer || ( printer != m_selectedPrinter ) );
174
175 m_selectedPrinter = printer;
176 }
177
178
179 // ------------------------------------------------------
180 // InstallPrinter
181 //
182 // Installs a printer with Windows.
183 //
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.
192 //
193
194 OSStatus
195 CPrinterSetupWizardSheet::InstallPrinter(Printer * printer)
196 {
197 Service * service;
198 BOOL ok;
199 OSStatus err;
200
201 service = printer->services.front();
202 check( service );
203
204 //
205 // if the driver isn't installed, then install it
206 //
207 if ( !printer->driverInstalled )
208 {
209 DWORD dwResult;
210 HANDLE hThread;
211 unsigned threadID;
212
213 m_driverThreadFinished = false;
214
215 //
216 // create the thread
217 //
218 hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallDriverThread, printer, 0, &threadID );
219 err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr );
220 require_noerr( err, exit );
221
222 //
223 // go modal
224 //
225 while (!m_driverThreadFinished)
226 {
227 MSG msg;
228
229 GetMessage( &msg, m_hWnd, 0, 0 );
230 TranslateMessage(&msg);
231 DispatchMessage(&msg);
232 }
233
234 //
235 // Wait until child process exits.
236 //
237 dwResult = WaitForSingleObject( hThread, INFINITE );
238 err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
239 require_noerr( err, exit );
240
241 //
242 // check the return value of thread
243 //
244 require_noerr( m_driverThreadExitCode, exit );
245
246 //
247 // now we know that the driver was successfully installed
248 //
249 printer->driverInstalled = true;
250 }
251
252 if ( service->type == kPDLServiceType )
253 {
254 err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_RAWTCP_TYPE );
255 require_noerr( err, exit );
256 }
257 else if ( service->type == kLPRServiceType )
258 {
259 err = InstallPrinterPDLAndLPR( printer, service, PROTOCOL_LPR_TYPE );
260 require_noerr( err, exit );
261 }
262 else if ( service->type == kIPPServiceType )
263 {
264 err = InstallPrinterIPP( printer, service );
265 require_noerr( err, exit );
266 }
267 else
268 {
269 err = kUnknownErr;
270 require_noerr( err, exit );
271 }
272
273 printer->installed = true;
274
275 //
276 // if the user specified a default printer, set it
277 //
278 if (printer->deflt)
279 {
280 ok = SetDefaultPrinter( printer->actualName );
281 err = translate_errno( ok, errno_compat(), err = kUnknownErr );
282 require_noerr( err, exit );
283 }
284
285 exit:
286
287 return err;
288 }
289
290
291 OSStatus
292 CPrinterSetupWizardSheet::InstallPrinterPDLAndLPR(Printer * printer, Service * service, DWORD protocol )
293 {
294 PRINTER_DEFAULTS printerDefaults = { NULL, NULL, SERVER_ACCESS_ADMINISTER };
295 DWORD dwStatus;
296 DWORD cbInputData = 100;
297 PBYTE pOutputData = NULL;
298 DWORD cbOutputNeeded = 0;
299 PORT_DATA_1 portData;
300 PRINTER_INFO_2 pInfo;
301 HANDLE hXcv = NULL;
302 HANDLE hPrinter = NULL;
303 Queue * q;
304 BOOL ok;
305 OSStatus err;
306
307 check(printer != NULL);
308 check(printer->installed == false);
309
310 q = service->queues.front();
311 check( q );
312
313 ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults);
314 err = translate_errno( ok, errno_compat(), kUnknownErr );
315 require_noerr( err, exit );
316
317 //
318 // BUGBUG: MSDN said this is not required, but my experience shows it is required
319 //
320 try
321 {
322 pOutputData = new BYTE[cbInputData];
323 }
324 catch (...)
325 {
326 pOutputData = NULL;
327 }
328
329 require_action( pOutputData, exit, err = kNoMemoryErr );
330
331 //
332 // setup the port
333 //
334 ZeroMemory(&portData, sizeof(PORT_DATA_1));
335 wcscpy(portData.sztPortName, printer->portName);
336
337 portData.dwPortNumber = service->portNumber;
338 portData.dwVersion = 1;
339
340 portData.dwProtocol = protocol;
341 portData.cbSize = sizeof PORT_DATA_1;
342 portData.dwReserved = 0L;
343
344 wcscpy(portData.sztQueue, q->name);
345 wcscpy(portData.sztIPAddress, service->hostname);
346 wcscpy(portData.sztHostAddress, service->hostname);
347
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 );
351
352 //
353 // add the printer
354 //
355 ZeroMemory(&pInfo, sizeof(pInfo));
356
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;
372 pInfo.Priority = 0;
373 pInfo.DefaultPriority = 0;
374 pInfo.StartTime = 0;
375 pInfo.UntilTime = 0;
376
377 hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo);
378 err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
379 require_noerr( err, exit );
380
381 exit:
382
383 if (hPrinter != NULL)
384 {
385 ClosePrinter(hPrinter);
386 }
387
388 if (hXcv != NULL)
389 {
390 ClosePrinter(hXcv);
391 }
392
393 if (pOutputData != NULL)
394 {
395 delete [] pOutputData;
396 }
397
398 return err;
399 }
400
401
402 OSStatus
403 CPrinterSetupWizardSheet::InstallPrinterIPP(Printer * printer, Service * service)
404 {
405 DEBUG_UNUSED( service );
406
407 HANDLE hPrinter = NULL;
408 PRINTER_INFO_2 pInfo;
409 OSStatus err;
410
411 //
412 // add the printer
413 //
414 ZeroMemory(&pInfo, sizeof(PRINTER_INFO_2));
415
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;
422
423 hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pInfo);
424 err = translate_errno( hPrinter, errno_compat(), kUnknownErr );
425 require_noerr( err, exit );
426
427 exit:
428
429 if ( hPrinter != NULL )
430 {
431 ClosePrinter(hPrinter);
432 }
433
434 return err;
435 }
436
437
438 BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet)
439 ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent )
440 ON_WM_SETCURSOR()
441 ON_WM_TIMER()
442 END_MESSAGE_MAP()
443
444
445 // ------------------------------------------------------
446 // OnCommand
447 //
448 // Traps when the user hits Finish
449 //
450 BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam)
451 {
452 //
453 // Check if this is OK
454 //
455 if (wParam == ID_WIZFINISH) // If OK is hit...
456 {
457 OnOK();
458 }
459
460 return CPropertySheet::OnCommand(wParam, lParam);
461 }
462
463
464 // ------------------------------------------------------
465 // OnInitDialog
466 //
467 // Initializes this Dialog object.
468 //
469 BOOL CPrinterSetupWizardSheet::OnInitDialog()
470 {
471 CPropertySheet::OnInitDialog();
472
473 return TRUE;
474 }
475
476
477 // ------------------------------------------------------
478 // OnSetCursor
479 //
480 // This is called when Windows wants to know what cursor
481 // to display. So we tell it.
482 //
483 BOOL
484 CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message)
485 {
486 DEBUG_UNUSED(pWnd);
487 DEBUG_UNUSED(nHitTest);
488 DEBUG_UNUSED(message);
489
490 SetCursor(m_active);
491 return TRUE;
492 }
493
494
495 // ------------------------------------------------------
496 // OnContextMenu
497 //
498 // This is not fully implemented yet.
499 //
500
501 void
502 CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos)
503 {
504 DEBUG_UNUSED(pWnd);
505 DEBUG_UNUSED(pos);
506
507 CAbout dlg;
508
509 dlg.DoModal();
510 }
511
512
513 // ------------------------------------------------------
514 // OnOK
515 //
516 // This is called when the user hits the "Finish" button
517 //
518 void
519 CPrinterSetupWizardSheet::OnOK()
520 {
521 check ( m_selectedPrinter != NULL );
522
523 SetWizardButtons( PSWIZB_DISABLEDFINISH );
524
525 if ( InstallPrinter( m_selectedPrinter ) != kNoErr )
526 {
527 CString caption;
528 CString message;
529
530 caption.LoadString(IDS_INSTALL_ERROR_CAPTION);
531 message.LoadString(IDS_INSTALL_ERROR_MESSAGE);
532
533 MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION);
534 }
535 }
536
537
538 // CPrinterSetupWizardSheet message handlers
539
540 void CPrinterSetupWizardSheet::Init(void)
541 {
542 AddPage(&m_pgFirst);
543 AddPage(&m_pgSecond);
544 AddPage(&m_pgThird);
545 AddPage(&m_pgFourth);
546
547 m_psh.dwFlags &= (~PSH_HASHELP);
548
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);
552
553 m_psh.hInstance = GetNonLocalizedResources();
554
555 SetWizardMode();
556 }
557
558
559 LONG
560 CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam)
561 {
562 DEBUG_UNUSED(inLParam);
563
564 m_driverThreadExitCode = (DWORD) inWParam;
565 m_driverThreadFinished = true;
566
567 return 0;
568 }
569
570
571 unsigned WINAPI
572 CPrinterSetupWizardSheet::InstallDriverThread( LPVOID inParam )
573 {
574 Printer * printer = (Printer*) inParam;
575 DWORD exitCode = 0;
576 DWORD dwResult;
577 OSStatus err;
578 STARTUPINFO si;
579 PROCESS_INFORMATION pi;
580 BOOL ok;
581
582 check( printer );
583 check( m_self );
584
585 //
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.
589 //
590 {
591 CString command;
592
593 ZeroMemory( &si, sizeof(si) );
594 si.cb = sizeof(si);
595 ZeroMemory( &pi, sizeof(pi) );
596
597 command.Format(L"rundll32.exe printui.dll,PrintUIEntry /ia /m \"%s\" /f \"%s\"", (LPCTSTR) printer->model, (LPCTSTR) printer->infFileName );
598
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 );
602
603 dwResult = WaitForSingleObject( pi.hProcess, INFINITE );
604 translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr );
605 require_noerr( err, exit );
606
607 ok = GetExitCodeProcess( pi.hProcess, &exitCode );
608 err = translate_errno( ok, errno_compat(), kUnknownErr );
609 require_noerr( err, exit );
610 }
611
612 exit:
613
614 //
615 // Close process and thread handles.
616 //
617 if ( pi.hProcess )
618 {
619 CloseHandle( pi.hProcess );
620 }
621
622 if ( pi.hThread )
623 {
624 CloseHandle( pi.hThread );
625 }
626
627 //
628 // alert the main thread
629 //
630 m_self->PostMessage( WM_PROCESS_EVENT, err, exitCode );
631
632 _endthreadex_compat( 0 );
633
634 return 0;
635 }