|
|
/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name: CPrint.cpp
Abstract: Class that wraps the multi-topic printing process
Revision History: Davide Massarenti (Dmassare) 05/07/2000 created
******************************************************************************/
#include "stdafx.h"
////////////////////////////////////////////////////////////////////////////////
static const DWORD c_MaxWait = 30000; // Max time to wait for temp file to change, before aborting.
////////////////////////////////////////////////////////////////////////////////
static DWORD WaitMultipleObjectsWithMessageLoop( HANDLE* rgEvents, DWORD dwNum ) { DWORD dwRet; MSG msg;
while(1) { dwRet = ::MsgWaitForMultipleObjects( dwNum, rgEvents, FALSE, INFINITE, QS_ALLINPUT );
if(/*dwRet >= WAIT_OBJECT_0 &&*/ dwRet < WAIT_OBJECT_0 + dwNum) { return dwRet - WAIT_OBJECT_0; // An event was signaled.
}
if(dwRet >= WAIT_ABANDONED_0 && dwRet < WAIT_ABANDONED_0 + dwNum ) { return dwRet - WAIT_ABANDONED_0; // An event was abandoned.
}
if(dwRet != WAIT_OBJECT_0 + dwNum) { return -1; }
// There is one or more window message available. Dispatch them
while(PeekMessage( &msg, NULL, NULL, NULL, PM_REMOVE )) { TranslateMessage( &msg ); DispatchMessage ( &msg );
dwRet = ::WaitForMultipleObjects( dwNum, rgEvents, FALSE, 0 );
if(/*dwRet >= WAIT_OBJECT_0 &&*/ dwRet < WAIT_OBJECT_0 + dwNum) { return dwRet - WAIT_OBJECT_0; // An event was signaled.
}
if(dwRet >= WAIT_ABANDONED_0 && dwRet < WAIT_ABANDONED_0 + dwNum ) { return dwRet - WAIT_ABANDONED_0; // An event was abandoned.
} } }
return -1; }
////////////////////////////////////////////////////////////////////////////////
Printing::Print::Print() { m_pCallback = NULL; // Notification* m_pCallback;
//
// MPC::WStringList m_lstURLs;
//
m_hwnd = NULL; // HWND m_hwnd;
// WindowHandle m_wnd;
// CComPtr<IWebBrowser2> m_spWebBrowser2;
//
// CComPtr<CDispatchSink> m_spObjDisp;
m_eventDocComplete = NULL; // HANDLE m_eventDocComplete;
m_eventAbortPrint = NULL; // HANDLE m_eventAbortPrint;
//
// CComPtr<IUnknown> m_spUnkControl;
m_dwCookie = 0; // DWORD m_dwCookie;
// CComPtr<IOleCommandTarget> m_spOleCmdTarg;
// MPC::wstring m_szPrintDir;
// MPC::wstring m_szPrintFile;
//
// CComPtr<IStream> m_streamPrintData;
}
Printing::Print::~Print() { Terminate(); }
////////////////////////////////////////
HRESULT Printing::Print::Initialize( /*[in]*/ HWND hwnd ) { __HCP_FUNC_ENTRY( "Printing::Print::Initialize" );
HRESULT hr;
__MPC_EXIT_IF_METHOD_FAILS(hr, Terminate());
m_hwnd = hwnd;
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_eventDocComplete = ::CreateEvent( NULL, FALSE, FALSE, NULL ))); __MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_eventAbortPrint = ::CreateEvent( NULL, FALSE, FALSE, NULL )));
m_wnd.SetAbortEvent( m_eventAbortPrint );
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr); }
HRESULT Printing::Print::Terminate() { if(m_spObjDisp) { if(m_dwCookie != 0) { ::AtlUnadvise( m_spUnkControl, DIID_DWebBrowserEvents2, m_dwCookie ); }
m_spObjDisp.Release(); }
m_spWebBrowser2.Release(); m_spUnkControl .Release(); m_spOleCmdTarg .Release();
if(m_wnd.m_hWnd) { m_wnd.DestroyWindow(); }
//
// Delete temp files.
//
m_streamPrintData.Release();
if(m_szPrintFile.size()) { (void)MPC::DeleteFile( m_szPrintFile, true, true );
m_szPrintFile.erase(); }
if(m_szPrintDir.size()) { if(!::RemoveDirectoryW( m_szPrintDir.c_str() )) { (void)::MoveFileExW( m_szPrintDir.c_str(), NULL, MOVEFILE_DELAY_UNTIL_REBOOT ); }
m_szPrintDir.erase(); }
return S_OK; }
////////////////////////////////////////////////////////////////////////////////
HRESULT Printing::Print::AddUrl( /*[in]*/ LPCWSTR szUrl ) { m_lstURLs.push_back( szUrl );
return S_OK; }
HRESULT Printing::Print::PrintAll( /*[in]*/ Notification* pCallback ) { __HCP_FUNC_ENTRY( "Printing::Print::PrintAll" );
HRESULT hr; int iLen = m_lstURLs.size(); int iPos = 0;
m_pCallback = pCallback;
if(iLen > 0) { // Note: Printing to the printer directly!
// By not setting the MultiTopic property PrintToFile is invoked - see PrintHost.cpp
// Since we are not printing to file, also comment out all calls that create and manipulate temp files
//__MPC_EXIT_IF_METHOD_FAILS(hr, PreparePrintFileLoc());
//
// Make sure the host window knows what's going on.
//
//m_wnd.SetMultiTopic ( true );
//m_wnd.SetPrintFileName( m_szPrintFile.c_str() );
for(MPC::WStringIter it = m_lstURLs.begin(); it != m_lstURLs.end(); it++, iPos++) { if(m_pCallback) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_pCallback->Progress( it->c_str(), iPos, iLen )); }
__MPC_EXIT_IF_METHOD_FAILS(hr, PrintSingleURL( *it )); }
//
// ok, send it all to the printer...
//
//__MPC_EXIT_IF_METHOD_FAILS(hr, RawDataToPrinter());
if(m_pCallback) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_pCallback->Progress( NULL, iLen, iLen )); } }
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr); }
////////////////////////////////////////////////////////////////////////////////
HRESULT Printing::Print::PrintSingleURL( /*[in]*/ MPC::wstring& szUrl ) { __HCP_FUNC_ENTRY( "Printing::Print::PrintSingleURL" );
HRESULT hr;
//
// Navigate to the url, creating the control if necessary
//
if(!m_wnd.m_hWnd) { RECT rect = { 0, 0, 800, 600 }; DWORD dwStyles = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
if(m_hwnd) dwStyles |= WS_CHILD;
if(!m_wnd.Create( m_hwnd, rect, szUrl.c_str(), dwStyles, WS_EX_CLIENTEDGE )) { __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL); }
m_wnd.ShowWindow( SW_SHOW );
__MPC_EXIT_IF_METHOD_FAILS(hr, HookUpEventSink()); } else { if(!m_spWebBrowser2) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_spUnkControl->QueryInterface( &m_spWebBrowser2 )); }
__MPC_EXIT_IF_METHOD_FAILS(hr, m_spWebBrowser2->Navigate( CComBSTR( szUrl.c_str() ), NULL, NULL, NULL, NULL )); }
//
// Wait for document to be loaded
//
__MPC_EXIT_IF_METHOD_FAILS(hr, WaitForDocComplete());
//
// If the URL don't match, it means the URL doesn't exist...
//
if(MPC::StrICmp( szUrl, m_spObjDisp->GetCurrentURL() )) { __MPC_SET_ERROR_AND_EXIT(hr, S_FALSE); }
__MPC_EXIT_IF_METHOD_FAILS(hr, DoPrint());
//
// Since we now do a synchronous print operation, there's no need for snooping at the spool directory state.
//
//__MPC_EXIT_IF_METHOD_FAILS(hr, WaitForPrintComplete());
//__MPC_EXIT_IF_METHOD_FAILS(hr, UpdatePrintBuffer());
hr = S_OK;
__HCP_FUNC_CLEANUP;
if(m_wnd.GetAbortState() == true) { hr = E_ABORT; }
__HCP_FUNC_EXIT(hr); }
HRESULT Printing::Print::HookUpEventSink() { __HCP_FUNC_ENTRY( "Printing::Print::HookUpEventSink" );
HRESULT hr;
m_spUnkControl.Release();
if(!m_wnd.m_hWnd) { __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL); }
//
// Hook up the connection point
//
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &m_spObjDisp ));
m_spObjDisp->SetNotificationEvent( m_eventDocComplete );
__MPC_EXIT_IF_METHOD_FAILS(hr, m_wnd.QueryControl( &m_spUnkControl ));
__MPC_EXIT_IF_METHOD_FAILS(hr, ::AtlAdvise( m_spUnkControl, m_spObjDisp, DIID_DWebBrowserEvents2, &m_dwCookie ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr); }
HRESULT Printing::Print::WaitForDocComplete() { __HCP_FUNC_ENTRY( "Printing::Print::WaitForDocComplete" );
HRESULT hr;
if(MPC::WaitForSingleObject( m_eventDocComplete ) != WAIT_OBJECT_0) { __MPC_SET_ERROR_AND_EXIT(hr, E_ABORT); }
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT Printing::Print::WaitForPrintComplete() { __HCP_FUNC_ENTRY( "Printing::Print::WaitForPrintComplete" );
HRESULT hr; HANDLE hFileChangeNotify; HANDLE rgEventsToWait[2];
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (hFileChangeNotify = ::FindFirstChangeNotificationW( m_szPrintDir.c_str(), FALSE, FILE_NOTIFY_CHANGE_SIZE )));
rgEventsToWait[0] = hFileChangeNotify; rgEventsToWait[1] = m_eventAbortPrint;
for(;;) { DWORD dwRet;
dwRet = MPC::WaitForMultipleObjects( 2, rgEventsToWait, c_MaxWait );
if(dwRet == WAIT_OBJECT_0) { HANDLE hFile = ::CreateFileW( m_szPrintFile.c_str(), GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
if(hFile != INVALID_HANDLE_VALUE) { DWORD dwSize = ::GetFileSize( hFile, NULL );
::CloseHandle( hFile );
if(dwSize != 0) break; }
(void)::FindNextChangeNotification(hFileChangeNotify); }
if(dwRet == WAIT_TIMEOUT || dwRet == WAIT_OBJECT_0 + 1 ) { __MPC_SET_ERROR_AND_EXIT(hr, E_ABORT); } }
hr = S_OK;
__HCP_FUNC_CLEANUP;
if(hFileChangeNotify) ::FindCloseChangeNotification( hFileChangeNotify );
__HCP_FUNC_EXIT(hr); }
HRESULT Printing::Print::DoPrint() { __HCP_FUNC_ENTRY( "Printing::Print::DoPrint" );
HRESULT hr;
// send the command to print
if(!m_spOleCmdTarg) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_wnd.QueryControl( &m_spOleCmdTarg )); }
//
// Make a synchronous print operation.
//
{ VARIANT vArgIN;
vArgIN.vt = VT_I2; vArgIN.iVal = PRINT_WAITFORCOMPLETION;
__MPC_EXIT_IF_METHOD_FAILS(hr, m_spOleCmdTarg->Exec( NULL, OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, &vArgIN, NULL )); }
hr = S_OK;
__HCP_FUNC_CLEANUP;
if(m_wnd.GetAbortState() == true) { hr = E_ABORT; }
__HCP_FUNC_EXIT(hr); }
HRESULT Printing::Print::PreparePrintFileLoc() { __HCP_FUNC_ENTRY( "Printing::Print::PreparePrintFileLoc" );
HRESULT hr; MPC::wstring szWritablePath; MPC::wstring szPrintData; CComPtr<MPC::FileStream> stream;
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::GetUserWritablePath( m_szPrintDir, HC_ROOT_HELPCTR L"\\Spool" ));
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::GetTemporaryFileName( m_szPrintFile, m_szPrintDir.c_str() )); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::GetTemporaryFileName( szPrintData , m_szPrintDir.c_str() ));
//
// Create a stream for a temporary file.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &stream ));
__MPC_EXIT_IF_METHOD_FAILS(hr, stream->InitForReadWrite( szPrintData.c_str() )); __MPC_EXIT_IF_METHOD_FAILS(hr, stream->DeleteOnRelease ( ));
m_streamPrintData = stream; hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr); }
HRESULT Printing::Print::UpdatePrintBuffer() { __HCP_FUNC_ENTRY( "Printing::Print::UpdatePrintBuffer" );
HRESULT hr; CComPtr<MPC::FileStream> stream;
//
// Open the single-topic print file and copy it to the multi-topic print file.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &stream ));
__MPC_EXIT_IF_METHOD_FAILS(hr, stream->InitForRead ( m_szPrintFile.c_str() )); __MPC_EXIT_IF_METHOD_FAILS(hr, stream->DeleteOnRelease( ));
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::BaseStream::TransferData( stream, m_streamPrintData ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr); }
HRESULT Printing::Print::RawDataToPrinter() { __HCP_FUNC_ENTRY( "Printing::Print::RawDataToPrinter" );
HRESULT hr; HANDLE hPrinter = NULL; DWORD dwJob = 0;
//
// Reset stream to beginning.
//
{ LARGE_INTEGER li;
li.LowPart = 0; li.HighPart = 0;
__MPC_EXIT_IF_METHOD_FAILS(hr, m_streamPrintData->Seek( li, STREAM_SEEK_SET, NULL )); }
//
// Open the printer, create a job and copy all the data into it.
//
{ DOC_INFO_1W docinfo; BYTE rgBuf[1024]; ULONG dwRead; DWORD dwWritten; MPC::wstring strTitle; MPC::LocalizeString( IDS_HELPCTR_PRINT_TITLE, strTitle );
// Fill in the structure with info about this "document."
docinfo.pDocName = (LPWSTR)strTitle.c_str();; docinfo.pOutputFile = NULL; docinfo.pDatatype = L"RAW";
__MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::OpenPrinterW( m_wnd.GetPrinterName(), &hPrinter, NULL ));
// Inform the spooler the document is beginning.
__MPC_EXIT_IF_CALL_RETURNS_ZERO(hr, (dwJob = ::StartDocPrinterW( hPrinter, 1, (LPBYTE)&docinfo )));
__MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::StartPagePrinter( hPrinter ));
while(1) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_streamPrintData->Read( rgBuf, sizeof(rgBuf), &dwRead )); if(hr == S_FALSE || dwRead == 0) // End of File.
{ break; }
__MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::WritePrinter( hPrinter, rgBuf, dwRead, &dwWritten )); } }
hr = S_OK;
__HCP_FUNC_CLEANUP;
if(hPrinter) { if(dwJob) { // End the page.
if(!::EndPagePrinter( hPrinter )) { if(SUCCEEDED(hr)) { hr = HRESULT_FROM_WIN32(::GetLastError()); } }
// Inform the spooler that the document is ending.
if(!::EndDocPrinter( hPrinter )) { if(SUCCEEDED(hr)) { hr = HRESULT_FROM_WIN32(::GetLastError()); } } }
// Tidy up the printer handle.
if(!::ClosePrinter( hPrinter )) { if(SUCCEEDED(hr)) { hr = HRESULT_FROM_WIN32(::GetLastError()); } } }
__HCP_FUNC_EXIT(hr); }
|