419 lines
15 KiB
C++
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);
|
|
}
|
|
} |