2007-12-10 20:53:52 +00:00

419 lines
15 KiB
C++

/***************************************************************************
* DSOFPRINT.CPP
*
* CDsoDocObject: Print Code for CDsoDocObject object
*
* Copyright ©1999-2004; Microsoft Corporation. All rights reserved.
* Written by Microsoft Developer Support Office Integration (PSS DSOI)
*
* This code is provided via KB 311765 as a sample. It is not a formal
* product and has not been tested with all containers or servers. Use it
* for educational purposes only. See the EULA.TXT file included in the
* KB download for full terms of use and restrictions.
*
* THIS CODE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
*
***************************************************************************/
#include "dsoframer.h"
////////////////////////////////////////////////////////////////////////
// CDsoFramerControl::_PrintOutOld
//
// Prints the current object by calling IOleCommandTarget for Print.
//
STDMETHODIMP CDsoFramerControl::_PrintOutOld(VARIANT PromptToSelectPrinter)
{
DWORD dwOption = BOOL_FROM_VARIANT(PromptToSelectPrinter, FALSE)
? OLECMDEXECOPT_PROMPTUSER : OLECMDEXECOPT_DODEFAULT;
TRACE1("CDsoFramerControl::_PrintOutOld(%d)\n", dwOption);
CHECK_NULL_RETURN(m_pDocObjFrame, ProvideErrorInfo(DSO_E_DOCUMENTNOTOPEN));
// Cannot access object if in modal condition...
if ((m_fModalState) || (m_pDocObjFrame->InPrintPreview()))
return ProvideErrorInfo(DSO_E_INMODALSTATE);
return m_pDocObjFrame->DoOleCommand(OLECMDID_PRINT, dwOption, NULL, NULL);
}
////////////////////////////////////////////////////////////////////////
// CDsoFramerControl::PrintOut
//
// Prints document using either IPrint or IOleCommandTarget, depending
// on parameters passed. This offers a bit more control over printing if
// the doc object server supports IPrint interface.
//
STDMETHODIMP CDsoFramerControl::PrintOut(VARIANT PromptUser, VARIANT PrinterName,
VARIANT Copies, VARIANT FromPage, VARIANT ToPage, VARIANT OutputFile)
{
HRESULT hr;
BOOL fPromptUser = BOOL_FROM_VARIANT(PromptUser, FALSE);
LPWSTR pwszPrinter = LPWSTR_FROM_VARIANT(PrinterName);
LPWSTR pwszOutput = LPWSTR_FROM_VARIANT(OutputFile);
LONG lCopies = LONG_FROM_VARIANT(Copies, 1);
LONG lFrom = LONG_FROM_VARIANT(FromPage, 0);
LONG lTo = LONG_FROM_VARIANT(ToPage, 0);
TRACE3("CDsoFramerControl::PrintOut(%d, %S, %d)\n", fPromptUser, pwszPrinter, lCopies);
CHECK_NULL_RETURN(m_pDocObjFrame, ProvideErrorInfo(DSO_E_DOCUMENTNOTOPEN));
// First we do validation of all parameters passed to the function...
if ((pwszPrinter) && (*pwszPrinter == L'\0'))
return E_INVALIDARG;
if ((pwszOutput) && (*pwszOutput == L'\0'))
return E_INVALIDARG;
if ((lCopies < 1) || (lCopies > 200))
return E_INVALIDARG;
if (((lFrom != 0) || (lTo != 0)) && ((lFrom < 1) || (lTo < lFrom)))
return E_INVALIDARG;
// Cannot access object if in modal condition...
if ((m_fModalState) || (m_pDocObjFrame->InPrintPreview()))
return ProvideErrorInfo(DSO_E_INMODALSTATE);
// If no printer name was provided, we can print to the default device
// using IOleCommandTarget with OLECMDID_PRINT...
if (pwszPrinter == NULL)
return _PrintOutOld(PromptUser);
// Ask the embedded document to print itself to specific printer...
hr = m_pDocObjFrame->PrintDocument(pwszPrinter, pwszOutput, (UINT)lCopies,
(UINT)lFrom, (UINT)lTo, fPromptUser);
// If call failed because interface doesn't exist, change error
// to let caller know it is because DocObj doesn't support this command...
if (FAILED(hr) && (hr == E_NOINTERFACE))
hr = DSO_E_COMMANDNOTSUPPORTED;
return ProvideErrorInfo(hr);
}
////////////////////////////////////////////////////////////////////////
// CDsoFramerControl::PrintPreview
//
// Asks embedded object to attempt a print preview (only Office docs do this).
//
STDMETHODIMP CDsoFramerControl::PrintPreview()
{
HRESULT hr;
ODS("CDsoFramerControl::PrintPreview\n");
CHECK_NULL_RETURN(m_pDocObjFrame, ProvideErrorInfo(DSO_E_DOCUMENTNOTOPEN));
if (m_fModalState) // Cannot access object if in modal condition...
return ProvideErrorInfo(DSO_E_INMODALSTATE);
// Try to set object into print preview mode...
hr = m_pDocObjFrame->StartPrintPreview();
// If call failed because interface doesn't exist, change error
// to let caller know it is because DocObj doesn't support this command...
if (FAILED(hr) && (hr == E_NOINTERFACE))
hr = DSO_E_COMMANDNOTSUPPORTED;
return ProvideErrorInfo(hr);
}
////////////////////////////////////////////////////////////////////////
// CDsoFramerControl::PrintPreviewExit
//
// Closes an active preview.
//
STDMETHODIMP CDsoFramerControl::PrintPreviewExit()
{
ODS("CDsoFramerControl::PrintPreviewExit\n");
CHECK_NULL_RETURN(m_pDocObjFrame, ProvideErrorInfo(DSO_E_DOCUMENTNOTOPEN));
// Try to set object out of print preview mode...
if (m_pDocObjFrame->InPrintPreview())
m_pDocObjFrame->ExitPrintPreview(TRUE);
return S_OK;
}
////////////////////////////////////////////////////////////////////////
// CDsoDocObject::PrintDocument
//
// We can use the IPrint interface for an ActiveX Document object to
// selectively print object to a given printer.
//
STDMETHODIMP CDsoDocObject::PrintDocument(LPCWSTR pwszPrinter, LPCWSTR pwszOutput, UINT cCopies, UINT nFrom, UINT nTo, BOOL fPromptUser)
{
HRESULT hr;
IPrint *print;
HANDLE hPrint;
DVTARGETDEVICE* ptd = NULL;
ODS("CDsoDocObject::PrintDocument\n");
CHECK_NULL_RETURN(m_pole, E_UNEXPECTED);
// First thing we need to do is ask object for IPrint. If it does not
// support it, we cannot continue. It is up to DocObj if this is allowed...
hr = m_pole->QueryInterface(IID_IPrint, (void**)&print);
RETURN_ON_FAILURE(hr);
// Now setup printer settings into DEVMODE for IPrint. Open printer
// settings and gather default DEVMODE...
if (FOpenPrinter(pwszPrinter, &hPrint))
{
LPDEVMODEW pdevMode = NULL;
LPWSTR pwszDefProcessor = NULL;
LPWSTR pwszDefDriver = NULL;
LPWSTR pwszDefPort = NULL;
LPWSTR pwszPort;
DWORD cbDevModeSize;
if (FGetPrinterSettings(hPrint, &pwszDefProcessor,
&pwszDefDriver, &pwszDefPort, &pdevMode, &cbDevModeSize) && (pdevMode))
{
DWORD cbPrintName, cbDeviceName, cbOutputName;
DWORD cbDVTargetSize;
pdevMode->dmFields |= DM_COPIES;
pdevMode->dmCopies = (WORD)((cCopies) ? cCopies : 1);
pwszPort = ((pwszOutput) ? (LPWSTR)pwszOutput : pwszDefPort);
// We calculate the size we will need for the TARGETDEVICE structure...
cbPrintName = ((lstrlenW(pwszDefProcessor) + 1) * sizeof(WCHAR));
cbDeviceName = ((lstrlenW(pwszDefDriver) + 1) * sizeof(WCHAR));
cbOutputName = ((lstrlenW(pwszPort) + 1) * sizeof(WCHAR));
cbDVTargetSize = sizeof(DWORD) + sizeof(DEVNAMES) + cbPrintName +
cbDeviceName + cbOutputName + cbDevModeSize;
// Allocate new target device using COM Task Allocator...
ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(cbDVTargetSize);
if (ptd)
{
// Copy all the data in the DVT...
DWORD dwOffset = sizeof(DWORD) + sizeof(DEVNAMES);
ptd->tdSize = cbDVTargetSize;
ptd->tdDriverNameOffset = (WORD)dwOffset;
memcpy((BYTE*)(((BYTE*)ptd) + dwOffset), pwszDefProcessor, cbPrintName);
dwOffset += cbPrintName;
ptd->tdDeviceNameOffset = (WORD)dwOffset;
memcpy((BYTE*)(((BYTE*)ptd) + dwOffset), pwszDefDriver, cbDeviceName);
dwOffset += cbDeviceName;
ptd->tdPortNameOffset = (WORD)dwOffset;
memcpy((BYTE*)(((BYTE*)ptd) + dwOffset), pwszPort, cbOutputName);
dwOffset += cbOutputName;
ptd->tdExtDevmodeOffset = (WORD)dwOffset;
memcpy((BYTE*)(((BYTE*)ptd) + dwOffset), pdevMode, cbDevModeSize);
dwOffset += cbDevModeSize;
ASSERT(dwOffset == cbDVTargetSize);
}
else hr = E_OUTOFMEMORY;
// We're done with the devmode...
DsoMemFree(pdevMode);
}
else hr = E_WIN32_LASTERROR;
SAFE_FREESTRING(pwszDefPort);
SAFE_FREESTRING(pwszDefDriver);
SAFE_FREESTRING(pwszDefProcessor);
ClosePrinter(hPrint);
}
else hr = E_WIN32_LASTERROR;
// If we were successful in getting TARGETDEVICE struct, provide the page range
// for the print job and ask docobj server to print it...
if (SUCCEEDED(hr))
{
PAGESET *ppgset;
DWORD cbPgRngSize = sizeof(PAGESET) + sizeof(PAGERANGE);
LONG cPages, cLastPage;
DWORD grfPrintFlags;
// Setup the page range to print...
if ((ppgset = (PAGESET*)CoTaskMemAlloc(cbPgRngSize)) != NULL)
{
ppgset->cbStruct = cbPgRngSize;
ppgset->cPageRange = 1;
ppgset->fOddPages = TRUE;
ppgset->fEvenPages = TRUE;
ppgset->cPageRange = 1;
ppgset->rgPages[0].nFromPage = ((nFrom) ? nFrom : 1);
ppgset->rgPages[0].nToPage = ((nTo) ? nTo : PAGESET_TOLASTPAGE);
// Give indication we are waiting (on the print)...
HCURSOR hCur = SetCursor(LoadCursor(NULL, IDC_WAIT));
SEH_TRY
// Setup the initial page number (optional)...
print->SetInitialPageNum(ppgset->rgPages[0].nFromPage);
grfPrintFlags = (PRINTFLAG_MAYBOTHERUSER | PRINTFLAG_RECOMPOSETODEVICE);
if (fPromptUser) grfPrintFlags |= PRINTFLAG_PROMPTUSER;
if (pwszOutput) grfPrintFlags |= PRINTFLAG_PRINTTOFILE;
// Now ask server to print it using settings passed...
hr = print->Print(grfPrintFlags, &ptd, &ppgset, NULL, (IContinueCallback*)&m_xContinueCallback,
ppgset->rgPages[0].nFromPage, &cPages, &cLastPage);
SEH_EXCEPT(hr)
SetCursor(hCur);
if (ppgset)
CoTaskMemFree(ppgset);
}
else hr = E_OUTOFMEMORY;
}
// We are done...
if (ptd) CoTaskMemFree(ptd);
print->Release();
return hr;
}
////////////////////////////////////////////////////////////////////////
// CDsoDocObject::StartPrintPreview
//
// Ask embedded object to go into print preview (if supportted).
//
STDMETHODIMP CDsoDocObject::StartPrintPreview()
{
HRESULT hr;
IOleInplacePrintPreview *prev;
HCURSOR hCur;
ODS("CDsoDocObject::StartPrintPreview\n");
CHECK_NULL_RETURN(m_pole, E_UNEXPECTED);
// No need to do anything if already in preview...
if (InPrintPreview()) return S_FALSE;
// Otherwise, ask document server if it supports preview...
hr = m_pole->QueryInterface(IID_IOleInplacePrintPreview, (void**)&prev);
if (SUCCEEDED(hr))
{
// Tell user we waiting (switch to preview can be slow for very large docs)...
hCur = SetCursor(LoadCursor(NULL, IDC_WAIT));
// If it does, make sure it can go into preview mode...
hr = prev->QueryStatus();
if (SUCCEEDED(hr))
{
SEH_TRY
if (m_hwndCtl) // Notify the control that preview started...
SendMessage(m_hwndCtl, DSO_WM_ASYNCH_STATECHANGE, DSO_STATE_INTERACTIVE, (LPARAM)FALSE);
// We will allow application to bother user and switch printers...
hr = prev->StartPrintPreview(
(PREVIEWFLAG_MAYBOTHERUSER | PREVIEWFLAG_PROMPTUSER | PREVIEWFLAG_USERMAYCHANGEPRINTER),
NULL, (IOlePreviewCallback*)&m_xPreviewCallback, 1);
SEH_EXCEPT(hr)
// If the call succeeds, we keep hold of interface to close preview later
if (SUCCEEDED(hr))
{
SAFE_SET_INTERFACE(m_pprtprv, prev);
}
else
{ // Otherwise, notify the control that preview failed...
if (m_hwndCtl)
PostMessage(m_hwndCtl, DSO_WM_ASYNCH_STATECHANGE, DSO_STATE_INTERACTIVE, (LPARAM)TRUE);
}
}
SetCursor(hCur);
prev->Release();
}
else if (IsPPTObject() && (m_hwndUIActiveObj))
{
// PowerPoint doesn't have print preview, but it does have slide show mode, so we can use this to
// toggle the viewing into slideshow...
if (PostMessage(m_hwndUIActiveObj, WM_KEYDOWN, VK_F5, 0x00000001) &&
PostMessage(m_hwndUIActiveObj, WM_KEYUP, VK_F5, 0xC0000001))
{
hr = S_OK; m_fAttemptPptPreview = TRUE;
}
}
return hr;
}
////////////////////////////////////////////////////////////////////////
// CDsoDocObject::ExitPrintPreview
//
// Drop out of print preview and restore items as needed.
//
STDMETHODIMP CDsoDocObject::ExitPrintPreview(BOOL fForceExit)
{
TRACE1("CDsoDocObject::ExitPrintPreview(fForceExit=%d)\n", (DWORD)fForceExit);
// Need to be in preview to run this function...
if (!InPrintPreview()) return S_FALSE;
// If the user closes the app or otherwise terminates the preview, we need
// to notify the ActiveDocument server to leave preview mode...
if (m_pprtprv)
{
if (fForceExit) // Tell docobj we want to end preview...
{
HRESULT hr = m_pprtprv->EndPrintPreview(TRUE);
ASSERT(SUCCEEDED(hr)); (void)hr;
}
}
else if (m_fInPptSlideShow)
{
if ((fForceExit) && (m_hwndUIActiveObj))
{
// HACK: When it goes into slideshow, PPT 2007 changes the active window but
// doesn't call SetActiveObject to update us with new object and window handle.
// If we post VK_ESCAPE to the window handle we have, it can fail. It needs to go
// to the slideshow window that PPT failed to give us handle for. As workaround,
// setting focus to the UI window we have handle for will automatically forward
// to the right window, so we can use that trick to get the right window and
// make the call in a way that that should succeed regardless of PPT version...
SetFocus(m_hwndUIActiveObj);
PostMessage(GetFocus(), WM_KEYDOWN, VK_ESCAPE, 0x00000001);
PostMessage(GetFocus(), WM_KEYUP, VK_ESCAPE, 0xC0000001);
}
m_fAttemptPptPreview = FALSE;
m_fInPptSlideShow = FALSE;
}
if (m_hwndCtl) // Notify the control that preview ended...
SendMessage(m_hwndCtl, DSO_WM_ASYNCH_STATECHANGE, DSO_STATE_INTERACTIVE, (LPARAM)TRUE);
// Free our reference to preview interface...
SAFE_RELEASE_INTERFACE(m_pprtprv);
return S_OK;
}
////////////////////////////////////////////////////////////////////////
// CDsoDocObject::CheckForPPTPreviewChange
//
// Used to update control when PPT goes in/out of slideshow asynchronously.
//
STDMETHODIMP_(void) CDsoDocObject::CheckForPPTPreviewChange()
{
if (m_fInPptSlideShow)
{
ExitPrintPreview(FALSE);
}
else if (m_fAttemptPptPreview)
{
m_fInPptSlideShow = TRUE;
m_fAttemptPptPreview = FALSE;
if (m_hwndCtl) // Notify the control that preview started...
SendMessage(m_hwndCtl, DSO_WM_ASYNCH_STATECHANGE, DSO_STATE_INTERACTIVE, (LPARAM)FALSE);
}
}