Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
14 KiB

  1. /******************************************************************************
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. CPrint.cpp
  5. Abstract:
  6. Class that wraps the multi-topic printing process
  7. Revision History:
  8. Davide Massarenti (Dmassare) 05/07/2000
  9. created
  10. ******************************************************************************/
  11. #include "stdafx.h"
  12. ////////////////////////////////////////////////////////////////////////////////
  13. static const DWORD c_MaxWait = 30000; // Max time to wait for temp file to change, before aborting.
  14. ////////////////////////////////////////////////////////////////////////////////
  15. static DWORD WaitMultipleObjectsWithMessageLoop( HANDLE* rgEvents, DWORD dwNum )
  16. {
  17. DWORD dwRet;
  18. MSG msg;
  19. while(1)
  20. {
  21. dwRet = ::MsgWaitForMultipleObjects( dwNum, rgEvents, FALSE, INFINITE, QS_ALLINPUT );
  22. if(/*dwRet >= WAIT_OBJECT_0 &&*/
  23. dwRet < WAIT_OBJECT_0 + dwNum)
  24. {
  25. return dwRet - WAIT_OBJECT_0; // An event was signaled.
  26. }
  27. if(dwRet >= WAIT_ABANDONED_0 &&
  28. dwRet < WAIT_ABANDONED_0 + dwNum )
  29. {
  30. return dwRet - WAIT_ABANDONED_0; // An event was abandoned.
  31. }
  32. if(dwRet != WAIT_OBJECT_0 + dwNum)
  33. {
  34. return -1;
  35. }
  36. // There is one or more window message available. Dispatch them
  37. while(PeekMessage( &msg, NULL, NULL, NULL, PM_REMOVE ))
  38. {
  39. TranslateMessage( &msg );
  40. DispatchMessage ( &msg );
  41. dwRet = ::WaitForMultipleObjects( dwNum, rgEvents, FALSE, 0 );
  42. if(/*dwRet >= WAIT_OBJECT_0 &&*/
  43. dwRet < WAIT_OBJECT_0 + dwNum)
  44. {
  45. return dwRet - WAIT_OBJECT_0; // An event was signaled.
  46. }
  47. if(dwRet >= WAIT_ABANDONED_0 &&
  48. dwRet < WAIT_ABANDONED_0 + dwNum )
  49. {
  50. return dwRet - WAIT_ABANDONED_0; // An event was abandoned.
  51. }
  52. }
  53. }
  54. return -1;
  55. }
  56. ////////////////////////////////////////////////////////////////////////////////
  57. Printing::Print::Print()
  58. {
  59. m_pCallback = NULL; // Notification* m_pCallback;
  60. //
  61. // MPC::WStringList m_lstURLs;
  62. //
  63. m_hwnd = NULL; // HWND m_hwnd;
  64. // WindowHandle m_wnd;
  65. // CComPtr<IWebBrowser2> m_spWebBrowser2;
  66. //
  67. // CComPtr<CDispatchSink> m_spObjDisp;
  68. m_eventDocComplete = NULL; // HANDLE m_eventDocComplete;
  69. m_eventAbortPrint = NULL; // HANDLE m_eventAbortPrint;
  70. //
  71. // CComPtr<IUnknown> m_spUnkControl;
  72. m_dwCookie = 0; // DWORD m_dwCookie;
  73. // CComPtr<IOleCommandTarget> m_spOleCmdTarg;
  74. // MPC::wstring m_szPrintDir;
  75. // MPC::wstring m_szPrintFile;
  76. //
  77. // CComPtr<IStream> m_streamPrintData;
  78. }
  79. Printing::Print::~Print()
  80. {
  81. Terminate();
  82. }
  83. ////////////////////////////////////////
  84. HRESULT Printing::Print::Initialize( /*[in]*/ HWND hwnd )
  85. {
  86. __HCP_FUNC_ENTRY( "Printing::Print::Initialize" );
  87. HRESULT hr;
  88. __MPC_EXIT_IF_METHOD_FAILS(hr, Terminate());
  89. m_hwnd = hwnd;
  90. __MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_eventDocComplete = ::CreateEvent( NULL, FALSE, FALSE, NULL )));
  91. __MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_eventAbortPrint = ::CreateEvent( NULL, FALSE, FALSE, NULL )));
  92. m_wnd.SetAbortEvent( m_eventAbortPrint );
  93. hr = S_OK;
  94. __HCP_FUNC_CLEANUP;
  95. __HCP_FUNC_EXIT(hr);
  96. }
  97. HRESULT Printing::Print::Terminate()
  98. {
  99. if(m_spObjDisp)
  100. {
  101. if(m_dwCookie != 0)
  102. {
  103. ::AtlUnadvise( m_spUnkControl, DIID_DWebBrowserEvents2, m_dwCookie );
  104. }
  105. m_spObjDisp.Release();
  106. }
  107. m_spWebBrowser2.Release();
  108. m_spUnkControl .Release();
  109. m_spOleCmdTarg .Release();
  110. if(m_wnd.m_hWnd)
  111. {
  112. m_wnd.DestroyWindow();
  113. }
  114. //
  115. // Delete temp files.
  116. //
  117. m_streamPrintData.Release();
  118. if(m_szPrintFile.size())
  119. {
  120. (void)MPC::DeleteFile( m_szPrintFile, true, true );
  121. m_szPrintFile.erase();
  122. }
  123. if(m_szPrintDir.size())
  124. {
  125. if(!::RemoveDirectoryW( m_szPrintDir.c_str() ))
  126. {
  127. (void)::MoveFileExW( m_szPrintDir.c_str(), NULL, MOVEFILE_DELAY_UNTIL_REBOOT );
  128. }
  129. m_szPrintDir.erase();
  130. }
  131. return S_OK;
  132. }
  133. ////////////////////////////////////////////////////////////////////////////////
  134. HRESULT Printing::Print::AddUrl( /*[in]*/ LPCWSTR szUrl )
  135. {
  136. m_lstURLs.push_back( szUrl );
  137. return S_OK;
  138. }
  139. HRESULT Printing::Print::PrintAll( /*[in]*/ Notification* pCallback )
  140. {
  141. __HCP_FUNC_ENTRY( "Printing::Print::PrintAll" );
  142. HRESULT hr;
  143. int iLen = m_lstURLs.size();
  144. int iPos = 0;
  145. m_pCallback = pCallback;
  146. if(iLen > 0)
  147. {
  148. __MPC_EXIT_IF_METHOD_FAILS(hr, PreparePrintFileLoc());
  149. //
  150. // Make sure the host window knows what's going on.
  151. //
  152. m_wnd.SetMultiTopic ( true );
  153. m_wnd.SetPrintFileName( m_szPrintFile.c_str() );
  154. for(MPC::WStringIter it = m_lstURLs.begin(); it != m_lstURLs.end(); it++, iPos++)
  155. {
  156. if(m_pCallback)
  157. {
  158. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pCallback->Progress( it->c_str(), iPos, iLen ));
  159. }
  160. __MPC_EXIT_IF_METHOD_FAILS(hr, PrintSingleURL( *it ));
  161. }
  162. //
  163. // ok, send it all to the printer...
  164. //
  165. __MPC_EXIT_IF_METHOD_FAILS(hr, RawDataToPrinter());
  166. if(m_pCallback)
  167. {
  168. __MPC_EXIT_IF_METHOD_FAILS(hr, m_pCallback->Progress( NULL, iLen, iLen ));
  169. }
  170. }
  171. hr = S_OK;
  172. __HCP_FUNC_CLEANUP;
  173. __HCP_FUNC_EXIT(hr);
  174. }
  175. ////////////////////////////////////////////////////////////////////////////////
  176. HRESULT Printing::Print::PrintSingleURL( /*[in]*/ MPC::wstring& szUrl )
  177. {
  178. __HCP_FUNC_ENTRY( "Printing::Print::PrintSingleURL" );
  179. HRESULT hr;
  180. //
  181. // Navigate to the url, creating the control if necessary
  182. //
  183. if(!m_wnd.m_hWnd)
  184. {
  185. RECT rect = { 0, 0, 800, 600 };
  186. DWORD dwStyles = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  187. if(m_hwnd) dwStyles |= WS_CHILD;
  188. if(!m_wnd.Create( m_hwnd, rect, szUrl.c_str(), dwStyles, WS_EX_CLIENTEDGE ))
  189. {
  190. __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
  191. }
  192. m_wnd.ShowWindow( SW_SHOW );
  193. __MPC_EXIT_IF_METHOD_FAILS(hr, HookUpEventSink());
  194. }
  195. else
  196. {
  197. if(!m_spWebBrowser2)
  198. {
  199. __MPC_EXIT_IF_METHOD_FAILS(hr, m_spUnkControl->QueryInterface( &m_spWebBrowser2 ));
  200. }
  201. __MPC_EXIT_IF_METHOD_FAILS(hr, m_spWebBrowser2->Navigate( CComBSTR( szUrl.c_str() ), NULL, NULL, NULL, NULL ));
  202. }
  203. //
  204. // Wait for document to be loaded
  205. //
  206. __MPC_EXIT_IF_METHOD_FAILS(hr, WaitForDocComplete());
  207. //
  208. // If the URL don't match, it means the URL doesn't exist...
  209. //
  210. if(MPC::StrICmp( szUrl, m_spObjDisp->GetCurrentURL() ))
  211. {
  212. __MPC_SET_ERROR_AND_EXIT(hr, S_FALSE);
  213. }
  214. __MPC_EXIT_IF_METHOD_FAILS(hr, DoPrint());
  215. //
  216. // Since we now do a synchronous print operation, there's no need for snooping at the spool directory state.
  217. //
  218. //__MPC_EXIT_IF_METHOD_FAILS(hr, WaitForPrintComplete());
  219. __MPC_EXIT_IF_METHOD_FAILS(hr, UpdatePrintBuffer());
  220. hr = S_OK;
  221. __HCP_FUNC_CLEANUP;
  222. if(m_wnd.GetAbortState() == true)
  223. {
  224. hr = E_ABORT;
  225. }
  226. __HCP_FUNC_EXIT(hr);
  227. }
  228. HRESULT Printing::Print::HookUpEventSink()
  229. {
  230. __HCP_FUNC_ENTRY( "Printing::Print::HookUpEventSink" );
  231. HRESULT hr;
  232. m_spUnkControl.Release();
  233. if(!m_wnd.m_hWnd)
  234. {
  235. __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
  236. }
  237. //
  238. // Hook up the connection point
  239. //
  240. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &m_spObjDisp ));
  241. m_spObjDisp->SetNotificationEvent( m_eventDocComplete );
  242. __MPC_EXIT_IF_METHOD_FAILS(hr, m_wnd.QueryControl( &m_spUnkControl ));
  243. __MPC_EXIT_IF_METHOD_FAILS(hr, ::AtlAdvise( m_spUnkControl, m_spObjDisp, DIID_DWebBrowserEvents2, &m_dwCookie ));
  244. hr = S_OK;
  245. __HCP_FUNC_CLEANUP;
  246. __HCP_FUNC_EXIT(hr);
  247. }
  248. HRESULT Printing::Print::WaitForDocComplete()
  249. {
  250. __HCP_FUNC_ENTRY( "Printing::Print::WaitForDocComplete" );
  251. HRESULT hr;
  252. if(MPC::WaitForSingleObject( m_eventDocComplete ) != WAIT_OBJECT_0)
  253. {
  254. __MPC_SET_ERROR_AND_EXIT(hr, E_ABORT);
  255. }
  256. hr = S_OK;
  257. __HCP_FUNC_CLEANUP;
  258. __HCP_FUNC_EXIT(hr);
  259. }
  260. HRESULT Printing::Print::WaitForPrintComplete()
  261. {
  262. __HCP_FUNC_ENTRY( "Printing::Print::WaitForPrintComplete" );
  263. HRESULT hr;
  264. HANDLE hFileChangeNotify;
  265. HANDLE rgEventsToWait[2];
  266. __MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (hFileChangeNotify = ::FindFirstChangeNotificationW( m_szPrintDir.c_str(), FALSE, FILE_NOTIFY_CHANGE_SIZE )));
  267. rgEventsToWait[0] = hFileChangeNotify;
  268. rgEventsToWait[1] = m_eventAbortPrint;
  269. for(;;)
  270. {
  271. DWORD dwRet;
  272. dwRet = MPC::WaitForMultipleObjects( 2, rgEventsToWait, c_MaxWait );
  273. if(dwRet == WAIT_OBJECT_0)
  274. {
  275. HANDLE hFile = ::CreateFileW( m_szPrintFile.c_str(), GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
  276. if(hFile != INVALID_HANDLE_VALUE)
  277. {
  278. DWORD dwSize = ::GetFileSize( hFile, NULL );
  279. ::CloseHandle( hFile );
  280. if(dwSize != 0) break;
  281. }
  282. (void)::FindNextChangeNotification(hFileChangeNotify);
  283. }
  284. if(dwRet == WAIT_TIMEOUT ||
  285. dwRet == WAIT_OBJECT_0 + 1 )
  286. {
  287. __MPC_SET_ERROR_AND_EXIT(hr, E_ABORT);
  288. }
  289. }
  290. hr = S_OK;
  291. __HCP_FUNC_CLEANUP;
  292. if(hFileChangeNotify) ::FindCloseChangeNotification( hFileChangeNotify );
  293. __HCP_FUNC_EXIT(hr);
  294. }
  295. HRESULT Printing::Print::DoPrint()
  296. {
  297. __HCP_FUNC_ENTRY( "Printing::Print::DoPrint" );
  298. HRESULT hr;
  299. // send the command to print
  300. if(!m_spOleCmdTarg)
  301. {
  302. __MPC_EXIT_IF_METHOD_FAILS(hr, m_wnd.QueryControl( &m_spOleCmdTarg ));
  303. }
  304. //
  305. // Make a synchronous print operation.
  306. //
  307. {
  308. VARIANT vArgIN;
  309. vArgIN.vt = VT_I2;
  310. vArgIN.iVal = PRINT_WAITFORCOMPLETION;
  311. __MPC_EXIT_IF_METHOD_FAILS(hr, m_spOleCmdTarg->Exec( NULL, OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, &vArgIN, NULL ));
  312. }
  313. hr = S_OK;
  314. __HCP_FUNC_CLEANUP;
  315. if(m_wnd.GetAbortState() == true)
  316. {
  317. hr = E_ABORT;
  318. }
  319. __HCP_FUNC_EXIT(hr);
  320. }
  321. HRESULT Printing::Print::PreparePrintFileLoc()
  322. {
  323. __HCP_FUNC_ENTRY( "Printing::Print::PreparePrintFileLoc" );
  324. HRESULT hr;
  325. MPC::wstring szWritablePath;
  326. MPC::wstring szPrintData;
  327. CComPtr<MPC::FileStream> stream;
  328. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::GetUserWritablePath( m_szPrintDir, HC_ROOT_HELPCTR L"\\Spool" ));
  329. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::GetTemporaryFileName( m_szPrintFile, m_szPrintDir.c_str() ));
  330. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::GetTemporaryFileName( szPrintData , m_szPrintDir.c_str() ));
  331. //
  332. // Create a stream for a temporary file.
  333. //
  334. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &stream ));
  335. __MPC_EXIT_IF_METHOD_FAILS(hr, stream->InitForReadWrite( szPrintData.c_str() ));
  336. __MPC_EXIT_IF_METHOD_FAILS(hr, stream->DeleteOnRelease ( ));
  337. m_streamPrintData = stream;
  338. hr = S_OK;
  339. __HCP_FUNC_CLEANUP;
  340. __HCP_FUNC_EXIT(hr);
  341. }
  342. HRESULT Printing::Print::UpdatePrintBuffer()
  343. {
  344. __HCP_FUNC_ENTRY( "Printing::Print::UpdatePrintBuffer" );
  345. HRESULT hr;
  346. CComPtr<MPC::FileStream> stream;
  347. //
  348. // Open the single-topic print file and copy it to the multi-topic print file.
  349. //
  350. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &stream ));
  351. __MPC_EXIT_IF_METHOD_FAILS(hr, stream->InitForRead ( m_szPrintFile.c_str() ));
  352. __MPC_EXIT_IF_METHOD_FAILS(hr, stream->DeleteOnRelease( ));
  353. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::BaseStream::TransferData( stream, m_streamPrintData ));
  354. hr = S_OK;
  355. __HCP_FUNC_CLEANUP;
  356. __HCP_FUNC_EXIT(hr);
  357. }
  358. HRESULT Printing::Print::RawDataToPrinter()
  359. {
  360. __HCP_FUNC_ENTRY( "Printing::Print::RawDataToPrinter" );
  361. HRESULT hr;
  362. HANDLE hPrinter = NULL;
  363. DWORD dwJob = 0;
  364. //
  365. // Reset stream to beginning.
  366. //
  367. {
  368. LARGE_INTEGER li;
  369. li.LowPart = 0;
  370. li.HighPart = 0;
  371. __MPC_EXIT_IF_METHOD_FAILS(hr, m_streamPrintData->Seek( li, STREAM_SEEK_SET, NULL ));
  372. }
  373. //
  374. // Open the printer, create a job and copy all the data into it.
  375. //
  376. {
  377. DOC_INFO_1W docinfo;
  378. BYTE rgBuf[1024];
  379. ULONG dwRead;
  380. DWORD dwWritten;
  381. MPC::wstring strTitle; MPC::LocalizeString( IDS_HELPCTR_PRINT_TITLE, strTitle );
  382. // Fill in the structure with info about this "document."
  383. docinfo.pDocName = (LPWSTR)strTitle.c_str();;
  384. docinfo.pOutputFile = NULL;
  385. docinfo.pDatatype = L"RAW";
  386. __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::OpenPrinterW( m_wnd.GetPrinterName(), &hPrinter, NULL ));
  387. // Inform the spooler the document is beginning.
  388. __MPC_EXIT_IF_CALL_RETURNS_ZERO(hr, (dwJob = ::StartDocPrinterW( hPrinter, 1, (LPBYTE)&docinfo )));
  389. __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::StartPagePrinter( hPrinter ));
  390. while(1)
  391. {
  392. __MPC_EXIT_IF_METHOD_FAILS(hr, m_streamPrintData->Read( rgBuf, sizeof(rgBuf), &dwRead ));
  393. if(hr == S_FALSE || dwRead == 0) // End of File.
  394. {
  395. break;
  396. }
  397. __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::WritePrinter( hPrinter, rgBuf, dwRead, &dwWritten ));
  398. }
  399. }
  400. hr = S_OK;
  401. __HCP_FUNC_CLEANUP;
  402. if(hPrinter)
  403. {
  404. if(dwJob)
  405. {
  406. // End the page.
  407. if(!::EndPagePrinter( hPrinter ))
  408. {
  409. if(SUCCEEDED(hr))
  410. {
  411. hr = HRESULT_FROM_WIN32(::GetLastError());
  412. }
  413. }
  414. // Inform the spooler that the document is ending.
  415. if(!::EndDocPrinter( hPrinter ))
  416. {
  417. if(SUCCEEDED(hr))
  418. {
  419. hr = HRESULT_FROM_WIN32(::GetLastError());
  420. }
  421. }
  422. }
  423. // Tidy up the printer handle.
  424. if(!::ClosePrinter( hPrinter ))
  425. {
  426. if(SUCCEEDED(hr))
  427. {
  428. hr = HRESULT_FROM_WIN32(::GetLastError());
  429. }
  430. }
  431. }
  432. __HCP_FUNC_EXIT(hr);
  433. }