//=============================================================== // // tmpprint.cxx : Implementation of the CTemplatePrinter Peer // // Synposis : This class has two major responsibilities // 1) Providing printer UI (dialogs, &c...) to a print template // 2) Providing a way for the template document to reach the printer // //=============================================================== #include "headers.h" #pragma MARK_DATA(__FILE__) #pragma MARK_CODE(__FILE__) #pragma MARK_CONST(__FILE__) #ifndef X_TMPPRINT_HXX_ #define X_TMPPRINT_HXX_ #include "tmpprint.hxx" #endif #ifndef X_IEXTAG_H_ #define X_IEXTAG_H_ #include "iextag.h" #endif #ifndef X_SHLGUID_H_ #define X_SHLGUID_H_ #include #endif #ifndef X_MSHTMLRC_H_ #define X_MSHTMLRC_H_ #include "mshtmlrc.h" // For default header/footer resource #endif #ifndef X_UTILS_HXX_ #define X_UTILS_HXX_ #include "utils.hxx" #endif #ifndef X_DLGS_H_ #define X_DLGS_H_ #include "dlgs.h" #endif #ifndef X_VRSSCAN_HXX_ #define X_VRSSCAN_HXX_ #include "vrsscan.h" #endif #ifndef X_WINSPOOL_H_ #define X_WINSPOOL_H_ #include "winspool.h" #endif #ifndef X_WINGDI_H_ #define X_WINGDI_H_ #include "wingdi.h" #endif #include #include #include #include #include // NB (greglett) // We need to define this because we're building with a WINVER of 4, and this is only deifned for NT5 // Remove this as soon as the winver changes. #define NEED_BECAUSE_COMPILED_AT_WINVER_4 #ifdef NEED_BECAUSE_COMPILED_AT_WINVER_4 #define PD_CURRENTPAGE 0x00400000 #define PD_NOCURRENTPAGE 0x00800000 #endif // This is defined in transform.hxx, but we can't access that as a peer. inline int MulDivQuick(int nMultiplicand, int nMultiplier, int nDivisor) { Assert(nDivisor); return (!nDivisor-1) & MulDiv(nMultiplicand, nMultiplier, nDivisor); } #define ORIENTPORTRAIT _T("portrait") #define ORIENTLANDSCAPE _T("landscape") static const TCHAR *s_aachPrintArg[] = { _T("__IE_BrowseDocument"), // PRINTARG_BROWSEDOC _T("__IE_PrinterCMD_DevNames"), // PRINTARG_DEVNAMES _T("__IE_PrinterCMD_DevMode"), // PRINTARG_DEVMODE _T("__IE_PrinterCMD_Printer"), // PRINTARG_PRINTER _T("__IE_PrinterCMD_Device"), // PRINTARG_DRIVER _T("__IE_PrinterCMD_Port"), // PRINTARG_PORT _T("__IE_PrintType"), // PRINTARG_TYPE }; #define DEVCAP_COPIES 0 #define DEVCAP_COLLATE 1 #define DEVCAP_DUPLEX 2 #define DEVCAP_LAST_RETAIL 3 // Add more retail properties before this! #ifndef DBG #define DEVCAP_LAST DEVCAP_LAST_RETAIL #else #define DEVCAP_DBG_PRINTERNAME DEVCAP_LAST_RETAIL #define DEVCAP_LAST DEVCAP_LAST_RETAIL + 1 #endif static const TCHAR *s_aachDeviceCapabilities[] = { _T("copies"), _T("collate"), _T("duplex"), // Add more retail properties after this! #if DBG == 1 _T("printerName"), #endif }; //+---------------------------------------------------------------------------- // // Function : InitMultiByteFromWideChar // // Synopsis : Allocates & creates a wide char string from a multi byte string. // //----------------------------------------------------------------------------- LPSTR InitMultiByteFromWideChar(LPCWSTR pchWide, long cchWide) { long cchMulti; char * pchMulti; // // Alloc space on heap for buffer. // cchMulti = ::WideCharToMultiByte(CP_ACP, 0, pchWide, cchWide, NULL, 0, NULL, NULL); Assert(cchMulti > 0); cchMulti++; pchMulti = new char[cchMulti]; if (pchMulti) { ::WideCharToMultiByte(CP_ACP, 0, pchWide, cchWide, pchMulti, cchMulti, NULL, NULL); pchMulti[cchMulti - 1] = '\0'; } return pchMulti; } inline LPSTR InitMultiByteFromWideChar(LPCWSTR pwch) { return InitMultiByteFromWideChar(pwch, _tcslen(pwch)); } //+---------------------------------------------------------------------------- // // Function : InitWideCharFromMultiByte // // Synopsis : Allocates & creates a multibyte string from a widechar string. // //----------------------------------------------------------------------------- LPWSTR InitWideCharFromMultiByte(LPSTR pchMulti, long cchMulti) { long cchWide; LPWSTR pchWide; // // Alloc space on heap for buffer. // cchWide = ::MultiByteToWideChar(CP_ACP, 0, pchMulti, cchMulti, NULL, 0); Assert(cchWide > 0); cchWide++; pchWide = new WCHAR[cchWide]; if (pchWide) { ::MultiByteToWideChar(CP_ACP, 0, pchMulti, cchMulti, pchWide, cchWide); pchWide[cchWide - 1] = _T('\0'); } return pchWide; } inline LPWSTR InitWideCharFromMultiByte(LPSTR pwch) { return InitWideCharFromMultiByte(pwch, strlen(pwch)); } //+---------------------------------------------------------------------------- // // Function : CreateDevNames // // Synopsis : Takes the three strings in a DEVNAMES structure, allocates & // & creates the structure as a GHND. // //----------------------------------------------------------------------------- HRESULT CreateDevNames(TCHAR *pchDriver, TCHAR *pchPrinter, TCHAR *pchPort, HGLOBAL *pDN) { HRESULT hr = S_OK; DWORD dwLenDriver, dwLenPrinter, dwLenPort; DWORD nStructSize; Assert(pDN); if (!pchDriver || !pchPrinter || !pchPort) { hr = E_FAIL; goto Cleanup; } dwLenDriver = _tcslen(pchDriver) + 1; dwLenPrinter = _tcslen(pchPrinter) + 1; dwLenPort = _tcslen(pchPort) + 1; nStructSize = sizeof(DEVNAMES) + ((dwLenPrinter + dwLenDriver + dwLenPort) * sizeof(TCHAR)); (*pDN) = ::GlobalAlloc(GHND, nStructSize); if (!(*pDN)) { hr = E_FAIL; goto Cleanup; } { DEVNAMES *pDevNames = ((DEVNAMES *) ::GlobalLock(*pDN)); if (pDevNames) { #pragma warning(disable: 4244) pDevNames->wDriverOffset = sizeof(DEVNAMES) / sizeof(TCHAR); pDevNames->wDeviceOffset = pDevNames->wDriverOffset + dwLenDriver; pDevNames->wOutputOffset = pDevNames->wDeviceOffset + dwLenPrinter; #pragma warning(default: 4244) _tcscpy((((TCHAR *)pDevNames) + pDevNames->wDriverOffset), pchDriver); _tcscpy((((TCHAR *)pDevNames) + pDevNames->wDeviceOffset), pchPrinter); _tcscpy((((TCHAR *)pDevNames) + pDevNames->wOutputOffset), pchPort); } ::GlobalUnlock(*pDN); } Cleanup: return hr; } //+---------------------------------------------------------------------------- // // Member : Init - IElementBehavior method impl // // Synopsis : peer Interface, initialization // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::Init(IElementBehaviorSite * pPeerSite) { HRESULT hr = S_OK; HKEY hKey = NULL; if (!pPeerSite) { hr = E_POINTER; goto Cleanup; } // cache our peer element _pPeerSite = pPeerSite; _pPeerSite->AddRef(); _pPeerSite->QueryInterface(IID_IElementBehaviorSiteOM, (void**)&_pPeerSiteOM); GetDialogArguments(); // Cache the dialog arguments. // What order should we obtain default print settings? // 1. Settings passed in by our host (1a and 1b should be mutually exclusive) // 1a. A DEVMODE/DEVNAMES // 1b. A printer (and maybe a port & driver name, too) // 2. Read defaults from the registry // 3. Get the default Windows printer, if any. // // READ IN A DEVMODE/DEVNAMES // { VARIANT varDM; VARIANT varDN; VariantInit(&varDN); VariantInit(&varDM); // Only accept arguments in matched pair. if ( GetDialogArgument(&varDN, PRINTARG_DEVNAMES) == S_OK && GetDialogArgument(&varDM, PRINTARG_DEVMODE) == S_OK && V_VT(&varDN) == VT_HANDLE && V_VT(&varDM) == VT_HANDLE && V_BYREF(&varDN) && V_BYREF(&varDM) ) { RemoveDialogArgument(PRINTARG_DEVNAMES); RemoveDialogArgument(PRINTARG_DEVMODE); _hDevNames = V_BYREF(&varDN); // NB We will release this! _hDevMode = V_BYREF(&varDM); // NB We will release this! } VariantClear(&varDN); VariantClear(&varDM); } // // READ IN A PRINTER/PORT/DRIVER // if (!_hDevNames) { VARIANT varPrinter; VARIANT varDriver; VARIANT varPort; VariantInit(&varPrinter); VariantInit(&varDriver); VariantInit(&varPort); Assert(!_hDevMode); if ( GetDialogArgument(&varPrinter, PRINTARG_PRINTER) == S_OK && V_VT(&varPrinter) == VT_BSTR && V_BSTR(&varPrinter) ) { GetDialogArgument(&varDriver, PRINTARG_DRIVER); GetDialogArgument(&varPort, PRINTARG_PORT); if (g_fUnicodePlatform) hr = ReadDeviceUnicode(V_BSTR(&varPrinter), V_VT(&varDriver) == VT_BSTR ? V_BSTR(&varDriver) : NULL, V_VT(&varPort) == VT_BSTR ? V_BSTR(&varPort) : NULL ); else hr = ReadDeviceNonUnicode(V_BSTR(&varPrinter), V_VT(&varDriver) == VT_BSTR ? V_BSTR(&varDriver) : NULL, V_VT(&varPort) == VT_BSTR ? V_BSTR(&varPort) : NULL ); } VariantClear(&varPrinter); VariantClear(&varDriver); VariantClear(&varPort); } // Get these default settings from the registry, if we can // 1. Header/Footer // 2. Margins // 3. Target device (printer) // 4. Page size/Paper source information. if (GetRegPrintOptionsKey(PRINTOPTSUBKEY_PAGESETUP, &hKey) == S_OK) { ReadHeaderFooterFromRegistry(hKey); ReadMarginsFromRegistry(hKey); RegCloseKey(hKey); } // 5. Table of links if (GetRegPrintOptionsKey(PRINTOPTSUBKEY_MAIN, &hKey) == S_OK) { _fPrintTableOfLinks = ReadBoolFromRegistry(hKey, _T("Print_Shortcuts")); RegCloseKey(hKey); } hr = GetDeviceProperties(); // Returns E_FAIL if we don't have a valid printer at this point. // // DEFAULT WINDOWS PRINTER // if (hr) hr = GetPrintDialogSettings(FALSE, NULL); // Get printer defaults. Cleanup: return hr; } //+---------------------------------------------------------------------------- // // Member : Detach - IElementBehavior method impl // // Synopsis : peer Interface, destruction work upon detaching from document // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::Detach() { // Abort any currently printing document. if (_hDC) { ::AbortDoc(_hDC); ::DeleteDC(_hDC); _hDC = NULL; } // Free any cached resources. if (_hInstResource) { MLFreeLibrary(_hInstResource); _hInstResource = NULL; } if (_hInstRatings) { FreeLibrary(_hInstRatings); _hInstRatings = NULL; } if (_hInstComctl32) { FreeLibrary(_hInstComctl32); _hInstComctl32 = NULL; } ReturnPrintHandles(); // Send our print handles back to the master thread CDoc. if (_hDevNames) { ::GlobalFree(_hDevNames); _hDevNames = NULL; } if (_hDevMode) { ::GlobalFree(_hDevMode); _hDevMode = NULL; } // Clear any COM interfaces we currently reference ClearInterface( &_pevDlgArgs ); ClearInterface( &_pPeerSite ); ClearInterface( &_pPeerSiteOM ); return S_OK; } //+---------------------------------------------------------------------------- // // Member : Notify - IElementBehavior method impl // // Synopsis : peer Interface, called for notification of document events. // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::Notify(LONG lEvent, VARIANT *) { return S_OK; } //+---------------------------------------------------------------------------- // // Member : (ITemplatePrinter) CTemplatePrinter::printPage // // Synopsis : takes the passed element and prints it on its own page // should be called any number of times after startDoc, and before endDoc. // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::printBlankPage() { // No TEMPLATESECURITYCHECK() required because printPage has one. return printPage(NULL); } STDMETHODIMP CTemplatePrinter::printPage(IDispatch *pElemDisp) { TEMPLATESECURITYCHECK() IHTMLElementRender *pRender = NULL; HRESULT hr = S_OK; if (!_hDC) { hr = E_FAIL; goto Cleanup; } if (::StartPage(_hDC) <= 0) { // Returns a "nonzero" value on success, "zero" on failure. DWORD dwError = GetLastError(); Assert(FALSE && "error calling StartPage api"); hr = E_FAIL; goto Cleanup; } #ifdef DBG RECT rcUnprintTest; SIZE szResTest; SIZE szPage; szResTest.cx = ::GetDeviceCaps(_hDC, LOGPIXELSX); szResTest.cy = ::GetDeviceCaps(_hDC, LOGPIXELSY); szPage.cx = ::GetDeviceCaps(_hDC, PHYSICALWIDTH); szPage.cy = ::GetDeviceCaps(_hDC, PHYSICALHEIGHT); rcUnprintTest.left = ::GetDeviceCaps(_hDC, PHYSICALOFFSETX); rcUnprintTest.top = ::GetDeviceCaps(_hDC, PHYSICALOFFSETY); rcUnprintTest.right = szPage.cx - ::GetDeviceCaps(_hDC, HORZRES) - rcUnprintTest.left; rcUnprintTest.bottom = szPage.cy - ::GetDeviceCaps(_hDC, VERTRES) - rcUnprintTest.top; Assert( rcUnprintTest.left == _rcUnprintable.left && rcUnprintTest.right == _rcUnprintable.right && rcUnprintTest.top == _rcUnprintable.top && rcUnprintTest.bottom == _rcUnprintable.bottom ); #endif // If we have been given an element, draw it to the screen. if (pElemDisp) { hr = pElemDisp->QueryInterface(IID_IHTMLElementRender, (void **)&pRender); if (hr) goto Cleanup; ::SetViewportOrgEx(_hDC, -_rcUnprintable.left,-_rcUnprintable.top, NULL); hr = pRender->DrawToDC(_hDC); } if (::EndPage(_hDC) <= 0) { // Known issues with EndPage: // 1. Win95 Fax fails the EndPage API when the "SetUpMyFax" wizard is aborted. dwError=0. (100092) DWORD dwError = GetLastError(); goto Cleanup; } Cleanup: ReleaseInterface(pRender); if (hr) stopDoc(); return hr; } //+---------------------------------------------------------------------------- // // Member : (ITemplatePrinter) CTemplatePrinter::startDoc // // Synopsis : Gets/Inits the default printer and starts to print a document. // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::startDoc(BSTR bstrTitle, VARIANT_BOOL * p) { TEMPLATESECURITYCHECK() HRESULT hr = S_OK; DOCINFO docinfo; TCHAR achTitle[MAX_JOBNAME]; if (!p) { hr = E_POINTER; goto Cleanup; } *p = VB_FALSE; if ( _hDC || !_hDevNames || !_hDevMode) { hr = S_FALSE; goto Cleanup; } { DEVNAMES *pDevNames = ((DEVNAMES *) ::GlobalLock(_hDevNames)); void *pDevMode = ::GlobalLock(_hDevMode); if (pDevNames && pDevMode) { // (greglett) Non-Unicode badness. See comment at definition of _hDevMode if (g_fUnicodePlatform) { if (!_fUsePrinterCopyCollate) { // Force template to do its own copies/collation (to prevent both us and the printer from doing so). ((DEVMODEW *)pDevMode)->dmCollate = FALSE; ((DEVMODEW *)pDevMode)->dmCopies = 1; } else { // We might want to check if the hardware supports copy/collation ((DEVMODEW *)pDevMode)->dmFields |= DM_COPIES | DM_COLLATE; ((DEVMODEW *)pDevMode)->dmCollate = _fCollate; ((DEVMODEW *)pDevMode)->dmCopies = _nCopies; } _hDC = ::CreateDCW(((TCHAR *)pDevNames) + pDevNames->wDriverOffset, ((TCHAR *)pDevNames) + pDevNames->wDeviceOffset, NULL, (DEVMODEW *) pDevMode); } else { LPSTR pchDriver = InitMultiByteFromWideChar(((TCHAR *)pDevNames) + pDevNames->wDriverOffset); LPSTR pchDevice = InitMultiByteFromWideChar(((TCHAR *)pDevNames) + pDevNames->wDeviceOffset); if (!_fUsePrinterCopyCollate) { // Force template to do its own copies/collation (to prevent both us and the printer from doing so). ((DEVMODEA *)pDevMode)->dmCollate = FALSE; ((DEVMODEA *)pDevMode)->dmCopies = 1; } else { // We might want to check if the hardware supports copy/collation ((DEVMODEA *)pDevMode)->dmFields |= DM_COPIES | DM_COLLATE; ((DEVMODEA *)pDevMode)->dmCollate = _fCollate; ((DEVMODEA *)pDevMode)->dmCopies = _nCopies; } if (pchDriver && pchDevice) { _hDC = ::CreateDCA(pchDriver, pchDevice, NULL, (DEVMODEA *)pDevMode); } if (pchDriver) delete []pchDriver; if (pchDevice) delete []pchDevice; } } ::GlobalUnlock(_hDevNames); ::GlobalUnlock(_hDevMode); if (!_hDC) { DWORD dwError = GetLastError(); Assert(!"Failed to create DC!"); hr = E_FAIL; goto Cleanup; } } // // Fill out the DOCINFO structure // ::ZeroMemory(&docinfo,sizeof(DOCINFO)); ::ZeroMemory(achTitle,sizeof(TCHAR) * MAX_JOBNAME); docinfo.cbSize = sizeof(DOCINFO); docinfo.fwType = 0; if (bstrTitle) _tcsncpy(achTitle, bstrTitle, MAX_JOBNAME - 1); docinfo.lpszDocName = achTitle; if (_achFileName[0]) docinfo.lpszOutput = _achFileName; // // Set up the document so that it can begin accepting pages // if (::StartDoc(_hDC, &docinfo) > 0) *p = VB_TRUE; #ifdef DBG else { DWORD dwError = GetLastError(); goto Cleanup; } #endif Cleanup: return hr; } //+---------------------------------------------------------------------------- // // Member : (ITemplatePrinter) CTemplatePrinter::endDoc // // Synopsis : 'Finishes' the doc - takes pages printed via printPage and queues the job // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::stopDoc() { TEMPLATESECURITYCHECK() HRESULT hr = S_OK; if (!_hDC) goto Cleanup; if (::EndDoc(_hDC) <=0) { DWORD dwError = GetLastError(); Assert(FALSE && "error calling EndDoc API"); hr = E_FAIL; goto Cleanup; } ::DeleteDC(_hDC); _hDC = NULL; Cleanup: return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put framesetDocument // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_framesetDocument(VARIANT_BOOL * p) { return GetFlagSafe(p,_fFramesetDocument); } STDMETHODIMP CTemplatePrinter::put_framesetDocument(VARIANT_BOOL v) { PUTFLAGSAFE(_fFramesetDocument, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printTableOfLinks // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_tableOfLinks(VARIANT_BOOL * p) { return GetFlagSafe(p,_fPrintTableOfLinks); } STDMETHODIMP CTemplatePrinter::put_tableOfLinks(VARIANT_BOOL v) { PUTFLAGSAFE(_fPrintTableOfLinks, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printAllLinkedDocuments // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_allLinkedDocuments(VARIANT_BOOL * p) { return GetFlagSafe(p, _fPrintAllLinkedDocuments); } STDMETHODIMP CTemplatePrinter::put_allLinkedDocuments(VARIANT_BOOL v) { PUTFLAGSAFE(_fPrintAllLinkedDocuments, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printFrameActive // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_frameActive(VARIANT_BOOL * p) { return GetFlagSafe(p,_fFrameActive); } STDMETHODIMP CTemplatePrinter::put_frameActive(VARIANT_BOOL v) { if (!!v) _fFrameAsShown = FALSE; PUTFLAGSAFE(_fFrameActive, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter2) CTemplatePrinter::get/put printFrameActive // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_frameActiveEnabled(VARIANT_BOOL * p) { return GetFlagSafe(p,_fFrameActiveEnabled); } STDMETHODIMP CTemplatePrinter::put_frameActiveEnabled(VARIANT_BOOL v) { PUTFLAGSAFE(_fFrameActiveEnabled, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printFrameAsShown // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_frameAsShown(VARIANT_BOOL * p) { return GetFlagSafe(p,_fFrameAsShown); } STDMETHODIMP CTemplatePrinter::put_frameAsShown(VARIANT_BOOL v) { if (!!v) _fFrameActive = FALSE; PUTFLAGSAFE(_fFrameAsShown, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printSelection // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_selection(VARIANT_BOOL * p) { return GetFlagSafe(p,_fPrintSelection); } STDMETHODIMP CTemplatePrinter::put_selection(VARIANT_BOOL v) { PUTFLAGSAFE(_fPrintSelection, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter2) CTemplatePrinter::get/put printSelectionEnabled // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_selectionEnabled(VARIANT_BOOL * p) { return GetFlagSafe(p,_fPrintSelectionEnabled); } STDMETHODIMP CTemplatePrinter::put_selectionEnabled(VARIANT_BOOL v) { PUTFLAGSAFE(_fPrintSelectionEnabled, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printSelectedPages // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_selectedPages(VARIANT_BOOL * p) { return GetFlagSafe(p,_fPrintSelectedPages); } STDMETHODIMP CTemplatePrinter::put_selectedPages(VARIANT_BOOL v) { PUTFLAGSAFE(_fPrintSelectedPages, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printCurrentPage // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_currentPage(VARIANT_BOOL * p) { return GetFlagSafe(p,_fPrintCurrentPage); } STDMETHODIMP CTemplatePrinter::put_currentPage(VARIANT_BOOL v) { PUTFLAGSAFE(_fPrintCurrentPage, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printCurrentPageAvail // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_currentPageAvail(VARIANT_BOOL * p) { return GetFlagSafe(p,_fCurrentPageAvail); } STDMETHODIMP CTemplatePrinter::put_currentPageAvail(VARIANT_BOOL v) { PUTFLAGSAFE(_fCurrentPageAvail, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put printCollate // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_collate(VARIANT_BOOL * p) { return GetFlagSafe(p,_fCollate); } STDMETHODIMP CTemplatePrinter::put_collate(VARIANT_BOOL v) { PUTFLAGSAFE(_fCollate, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter2) CTemplatePrinter::get/put usePrinterCopyCollate // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_usePrinterCopyCollate(VARIANT_BOOL * p) { return GetFlagSafe(p,_fUsePrinterCopyCollate); } STDMETHODIMP CTemplatePrinter::put_usePrinterCopyCollate(VARIANT_BOOL v) { PUTFLAGSAFE(_fUsePrinterCopyCollate, v); } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get_duplex // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_duplex(VARIANT_BOOL * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) { hr = E_INVALIDARG; goto Cleanup; } *p = VB_FALSE; if (_hDevMode) { void * pDevMode = ::GlobalLock(_hDevMode); if (pDevMode) { // (greglett) Unicode weirdness. DEVMMODEA on Win9x, DEVMODEW on NT. if ( ( g_fUnicodePlatform && (((DEVMODEW *)pDevMode)->dmFields & DM_DUPLEX) && ((DEVMODEW *)pDevMode)->dmDuplex != DMDUP_SIMPLEX ) || ( !g_fUnicodePlatform && (((DEVMODEA *)pDevMode)->dmFields & DM_DUPLEX) && ((DEVMODEA *)pDevMode)->dmDuplex != DMDUP_SIMPLEX ) ) { *p = VB_TRUE; } ::GlobalUnlock(_hDevMode); } } Cleanup: return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put copies // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_copies(WORD * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else *p = _nCopies; return hr; } STDMETHODIMP CTemplatePrinter::put_copies(WORD v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (v < 0) hr = E_INVALIDARG; else _nCopies = v; //////////////////////////////////////////////////////////////////// // a-naande 7-30-02 winse 25090 // if the DEVMODE struct isn't null, then the value in dmCopies will supersede // nCopies in the PRINTDLG struct, so set it to match _nCopies if(SUCCEEDED(hr) && _hDevMode) { void *pDevMode = ::GlobalLock(_hDevMode); if (pDevMode) { if (g_fUnicodePlatform) { ((DEVMODEW *)pDevMode)->dmCopies = _nCopies; } else { ((DEVMODEA *)pDevMode)->dmCopies = _nCopies; } ::GlobalUnlock(_hDevMode); } } return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put pageFrom // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_pageFrom(WORD * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else *p = _nPageFrom; return hr; } STDMETHODIMP CTemplatePrinter::put_pageFrom(WORD v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (v < 0) hr = E_INVALIDARG; else _nPageFrom = v; return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put pageTo // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_pageTo(WORD * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else *p = _nPageTo; return hr; } STDMETHODIMP CTemplatePrinter::put_pageTo(WORD v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (v < 0) hr = E_INVALIDARG; else _nPageTo = v; return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put marginLeft/Right/Top/Bottom // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_marginLeft(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else *p = (_rcMargin.left / 1000); return hr; } STDMETHODIMP CTemplatePrinter::put_marginLeft(long v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (v < 0) hr = E_INVALIDARG; else _rcMargin.left = v * 1000; return hr; } STDMETHODIMP CTemplatePrinter::get_marginRight(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else *p = (_rcMargin.right / 1000); return hr; } STDMETHODIMP CTemplatePrinter::put_marginRight(long v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (v < 0) hr = E_INVALIDARG; else _rcMargin.right = v * 1000; return hr; } STDMETHODIMP CTemplatePrinter::get_marginTop(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else // Input in 1/100 inches. *p = (_rcMargin.top / 1000); return hr; } STDMETHODIMP CTemplatePrinter::put_marginTop(long v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (v < 0) hr = E_INVALIDARG; else // Output in 1/100 inches. _rcMargin.top = v * 1000; return hr; } STDMETHODIMP CTemplatePrinter::get_marginBottom(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else // Input in 1/100 inches. *p = (_rcMargin.bottom / 1000); return hr; } STDMETHODIMP CTemplatePrinter::put_marginBottom(long v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (v < 0) hr = E_INVALIDARG; else // Output in 1/100 inches. _rcMargin.bottom = v * 1000; return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get pageWidth/Height // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_pageWidth(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else // Output in 1/100 inches. *p = _ptPaperSize.x / 10; return hr; } STDMETHODIMP CTemplatePrinter::get_pageHeight(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else // Output in 1/100 inches. *p = _ptPaperSize.y / 10; return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter2) CTemplatePrinter::get/put orientation // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_orientation(BSTR * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) { hr = E_POINTER; goto Cleanup; } *p = NULL; if (GetOrientation() == DMORIENT_LANDSCAPE) *p = SysAllocString(ORIENTLANDSCAPE); else *p = SysAllocString(ORIENTPORTRAIT); if (!p) hr = E_OUTOFMEMORY; Cleanup: return hr; } STDMETHODIMP CTemplatePrinter::put_orientation(BSTR v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!v) { hr = E_INVALIDARG; goto Cleanup; } if (_tcsicmp(v, ORIENTPORTRAIT) == 0) SetOrientation(DMORIENT_PORTRAIT); else if (_tcsicmp(v, ORIENTLANDSCAPE) == 0) SetOrientation(DMORIENT_LANDSCAPE); else hr = E_INVALIDARG; Cleanup: return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get unprintableLeft/Top/Right/Bottom // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_unprintableLeft(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else if (_szResolution.cx == 0) *p = 0; else // Output should be in 1/100 inches, not printer pixels. *p = MulDivQuick(_rcUnprintable.left, 100, _szResolution.cx); return hr; } STDMETHODIMP CTemplatePrinter::get_unprintableTop(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else if (_szResolution.cy == 0) *p = 0; else // Output should be in 1/100 inches, not printer pixels. *p = MulDivQuick(_rcUnprintable.top, 100, _szResolution.cy); return hr; } STDMETHODIMP CTemplatePrinter::get_unprintableRight(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else if (_szResolution.cx == 0) *p = 0; else // Output should be in 1/100 inches, not printer pixels. *p = MulDivQuick(_rcUnprintable.right, 100, _szResolution.cx); return hr; } STDMETHODIMP CTemplatePrinter::get_unprintableBottom(long * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else if (_szResolution.cy == 0) *p = 0; else // Output should be in 1/100 inches, not printer pixels. *p = MulDivQuick(_rcUnprintable.bottom, 100, _szResolution.cy); return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put header // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_header(BSTR * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else { *p = SysAllocString(_achHeader); if (!p) hr = E_OUTOFMEMORY; } return hr; } STDMETHODIMP CTemplatePrinter::put_header(BSTR v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; TCHAR *achTemp; achTemp = v; if (! (_tcslen(achTemp) <= ARRAY_SIZE(_achHeader) - 1)) hr = E_INVALIDARG; else { _fPersistHFToRegistry = FALSE; _tcscpy(_achHeader, achTemp); } return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::get/put footer // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::get_footer(BSTR * p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; if (!p) hr = E_POINTER; else { *p = SysAllocString(_achFooter); if (!p) hr = E_OUTOFMEMORY; } return hr; } STDMETHODIMP CTemplatePrinter::put_footer(BSTR v) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; TCHAR *achTemp; achTemp = v; if (! (_tcslen(achTemp) <= ARRAY_SIZE(_achFooter) - 1)) hr = E_INVALIDARG; else { _fPersistHFToRegistry = FALSE; _tcscpy(_achFooter, achTemp); } return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter2) CTemplatePrinter::deviceSupports // // Takes a BSTR indicating which property to query (supported values in the // defined above). // Returns information about that property. // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::deviceSupports(BSTR bstrProperty, VARIANT * pvar) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; void *pDevMode = NULL; DEVNAMES *pDevNames = NULL; TCHAR *achDevice = NULL; TCHAR *achPort = NULL; int i; if (!pvar) { hr = E_INVALIDARG; goto Cleanup; } VariantInit(pvar); pDevMode = ::GlobalLock(_hDevMode); pDevNames = ((DEVNAMES *) ::GlobalLock(_hDevNames)); if (!pDevMode || !pDevNames) { hr = E_FAIL; goto Cleanup; } for (i = 0; (i < DEVCAP_LAST) && (_tcsicmp(bstrProperty, s_aachDeviceCapabilities[i]) != 0); i++); if (i >= DEVCAP_LAST) { hr = E_INVALIDARG; goto Cleanup; } achDevice = ((TCHAR *)pDevNames) + (pDevNames->wDeviceOffset); achPort = ((TCHAR *)pDevNames) + (pDevNames->wOutputOffset); switch (i) { case DEVCAP_COPIES: V_VT(pvar) = VT_INT; V_INT(pvar) = ::DeviceCapabilities(achDevice, achPort, DC_COPIES, NULL, NULL); break; case DEVCAP_COLLATE: V_VT(pvar) = VT_BOOL; V_BOOL(pvar) = (::DeviceCapabilities(achDevice, achPort, DC_COLLATE, NULL, NULL) != 0) ? VB_TRUE : VB_FALSE; break; case DEVCAP_DUPLEX: V_VT(pvar) = VT_BOOL; V_BOOL(pvar) = (::DeviceCapabilities(achDevice, achPort, DC_DUPLEX, NULL, NULL) != 0) ? VB_TRUE : VB_FALSE; break; #if DBG==1 case DEVCAP_DBG_PRINTERNAME: V_BSTR(pvar) = ::SysAllocString(achDevice); if (V_BSTR(pvar)) V_VT(pvar) = VT_BSTR; else hr = E_OUTOFMEMORY; break; #endif default: Assert(FALSE && "Unrecognized DEVCAP_ value."); break; } Cleanup: if (pDevNames) ::GlobalUnlock(_hDevNames); if (pDevMode) ::GlobalUnlock(_hDevMode); return hr; } STDMETHODIMP CTemplatePrinter::updatePageStatus(long * p) { TEMPLATESECURITYCHECK(); VARIANT varHost; HRESULT hr = S_OK; IOleCommandTarget *pioct = NULL; const GUID *pguid = NULL; DWORD nCmdId = 0; VariantInit(&varHost); if (!p) { hr = E_POINTER; goto Cleanup; } if ( GetDialogArgument(&varHost, PRINTARG_BROWSEDOC) == S_OK && V_VT(&varHost) == VT_UNKNOWN && V_UNKNOWN(&varHost) ) { VARIANT varIn; V_VT(&varIn) = VT_I4; V_I4(&varIn) = (*p > 0) ? (*p) : 0; hr = V_UNKNOWN(&varHost)->QueryInterface(IID_IOleCommandTarget, (void **)&pioct); if (hr) goto Cleanup; Assert(pioct); hr = pioct->Exec(&CGID_MSHTML, IDM_UPDATEPAGESTATUS, 0, &varIn, 0); hr = S_OK; // If the host isn't listening, we'll get an OLE error. Don't throw it to script! } Cleanup: ReleaseInterface(pioct); VariantClear(&varHost); return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::printNonNative // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::printNonNative(IUnknown* pDoc, VARIANT_BOOL *p) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; IOleCommandTarget *pioct = NULL; IPrint *pIPrint = NULL; VARIANT varOut; VARIANT varIn; VariantInit(&varOut); if (!pDoc || !p) { hr = E_POINTER; goto Cleanup; } *p = VB_FALSE; hr = pDoc->QueryInterface(IID_IOleCommandTarget, (void **)&pioct); if (hr) goto Cleanup; Assert(pioct); V_VT(&varIn) = VT_I4; V_I4(&varIn) = IPRINT_DOCUMENT; hr = pioct->Exec( &CGID_MSHTML, IDM_GETIPRINT, NULL, &varIn, &varOut); if ( hr || V_VT(&varOut) != VT_UNKNOWN || !V_UNKNOWN(&varOut)) goto Cleanup; // We don't get back an IPrint collection unless it has at least one member. // At this point, we can claim that we should be printing, and a template does not need to. *p = VB_TRUE; hr = PrintIPrintCollection(&varOut); Cleanup: VariantClear(&varOut); ReleaseInterface(pioct); return hr; } STDMETHODIMP CTemplatePrinter::printNonNativeFrames(IUnknown *pMarkup, VARIANT_BOOL fActiveFrame) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; IOleCommandTarget *pioct = NULL; VARIANT varOut; VARIANT varIn; VARIANT varBrowseDoc; if (!pMarkup) { hr = E_POINTER; goto Cleanup; } VariantInit(&varOut); VariantInit(&varIn); VariantInit(&varBrowseDoc); // Use the browse document if one exists - it will have the WebOC frame. // NB (greglett) // This assumes that the reference we are passed to the browse doc // stays good - including nested frames &c... on the browse doc while the WebOC is still // loaded & (if it was selected) active. // Yes, this may exhibit unexpected behavior if the user Prints and navigates away. // A better solution would be appreciated. if ( GetDialogArgument(&varBrowseDoc, PRINTARG_BROWSEDOC) == S_OK && V_VT(&varBrowseDoc) == VT_UNKNOWN && V_UNKNOWN(&varBrowseDoc) ) { hr = V_UNKNOWN(&varBrowseDoc)->QueryInterface(IID_IOleCommandTarget, (void **)&pioct); } // Otherwise, use the content document if (!pioct) { hr = pMarkup->QueryInterface(IID_IOleCommandTarget, (void **)&pioct); if (hr) goto Cleanup; } Assert(pioct); V_VT(&varIn) = VT_I4; V_I4(&varIn) = fActiveFrame ? IPRINT_ACTIVEFRAME : IPRINT_ALLFRAMES; hr = pioct->Exec( &CGID_MSHTML, IDM_GETIPRINT, NULL, &varIn, &varOut); if ( hr || V_VT(&varOut) != VT_UNKNOWN || !V_UNKNOWN(&varOut)) goto Cleanup; hr = PrintIPrintCollection(&varOut); Cleanup: VariantClear(&varOut); VariantClear(&varBrowseDoc); ReleaseInterface(pioct); return hr; } HRESULT CTemplatePrinter::PrintIPrintCollection(VARIANT * pvarIPrintAry) { TEMPLATESECURITYCHECK(); HRESULT hr = S_OK; IDispatch * pIPrintAry = NULL; IPrint * pIPrint = NULL; VARIANT varInvokeOut; VARIANT varInvokeParam; DISPPARAMS DispParams; DVTARGETDEVICE * pTargetDevice = NULL; PAGESET * pPageSet = NULL; long cIPrint, i; long lFirstPage, lPages, lLastPage; Assert(V_VT(pvarIPrintAry) == VT_UNKNOWN); Assert(V_UNKNOWN(pvarIPrintAry)); VariantInit(&varInvokeOut); VariantInit(&varInvokeParam); hr = V_UNKNOWN(pvarIPrintAry)->QueryInterface(IID_IDispatch, (void **)&pIPrintAry); if (hr) goto Cleanup; Assert(pIPrintAry); DispParams.cNamedArgs = 0; DispParams.rgdispidNamedArgs = NULL; DispParams.cArgs = 0; DispParams.rgvarg = NULL; hr = pIPrintAry->Invoke(DISPID_IHTMLIPRINTCOLLECTION_LENGTH, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &DispParams, &varInvokeOut, NULL, NULL); if ( hr || V_VT(&varInvokeOut) != VT_I4 || V_I4(&varInvokeOut) <= 0 ) goto Cleanup; cIPrint = V_I4(&varInvokeOut); VariantClear(&varInvokeOut); hr = CreateIPrintParams(&pTargetDevice, &pPageSet); if (hr) goto Cleanup; lFirstPage = pPageSet->rgPages[0].nFromPage; lLastPage = pPageSet->rgPages[0].nToPage; DispParams.cArgs = 1; DispParams.rgvarg = &varInvokeParam; V_VT(&varInvokeParam) = VT_I4; for (i = 0; i < cIPrint; i++) { V_I4(&varInvokeParam) = i; hr = pIPrintAry->Invoke(DISPID_IHTMLIPRINTCOLLECTION_ITEM, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &DispParams, &varInvokeOut, NULL, NULL); if ( hr || V_VT(&varInvokeOut) != VT_UNKNOWN || !V_UNKNOWN(&varInvokeOut)) { VariantClear(&varInvokeOut); continue; } hr = V_UNKNOWN(&varInvokeOut)->QueryInterface(IID_IPrint, (void **)&pIPrint); VariantClear(&varInvokeOut); if (hr) continue; Assert(pIPrint); hr = pIPrint->Print( PRINTFLAG_MAYBOTHERUSER, &pTargetDevice, &pPageSet, NULL, NULL, lFirstPage, &lPages, // out &lLastPage // out ); ReleaseInterface(pIPrint); pIPrint = NULL; } hr = S_OK; Cleanup: if (pTargetDevice) ::CoTaskMemFree(pTargetDevice); if (pPageSet) ::CoTaskMemFree(pPageSet); ReleaseInterface(pIPrintAry); return hr; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::showPrintDialog, ensurePrintDialogDefaults // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::ensurePrintDialogDefaults(VARIANT_BOOL *p) { TEMPLATESECURITYCHECK(); if (!p) return E_POINTER; // If we already have default printer information, use it. if (_hDevNames && _hDevMode) *p = VB_TRUE; // Otherwise, go get it. else GetPrintDialogSettings(FALSE, p); return S_OK; } STDMETHODIMP CTemplatePrinter::showPrintDialog(VARIANT_BOOL *p) { TEMPLATESECURITYCHECK(); if (!p) return E_POINTER; GetPrintDialogSettings(TRUE, p); return S_OK; } //+---------------------------------------------------------------------------- // // (ITemplatePrinter) CTemplatePrinter::showPageSetupDialog // //----------------------------------------------------------------------------- STDMETHODIMP CTemplatePrinter::showPageSetupDialog(VARIANT_BOOL *p) { TEMPLATESECURITYCHECK(); HRESULT hr; HWND hWnd; BOOL fMetricUnits = FALSE; IHTMLEventObj2 *pEvent = NULL; // To populate with dialog parameters IDocHostUIHandler *pUIHandler = NULL; IOleCommandTarget *pUICommandHandler = NULL; HGLOBAL hPageSetup = NULL; PAGESETUPDLG * ppagesetupdlg = NULL; VARIANT varIn; if (!p) { hr = E_POINTER; goto Cleanup; } *p = VB_FALSE; if (_hDC) { hr = E_FAIL; goto Cleanup; } hPageSetup = ::GlobalAlloc(GHND, sizeof(PAGESETUPDLG)); if (!hPageSetup) { hr = E_OUTOFMEMORY; goto Cleanup; } ppagesetupdlg = (PAGESETUPDLG *)::GlobalLock(hPageSetup); if (!ppagesetupdlg) { hr = E_FAIL; goto Cleanup; } hr = InitDialog(&hWnd, &pEvent, &pUICommandHandler); if (hr) goto Cleanup; { // Are we using metric or British (US) for margins? TCHAR achLocale[32]; int iLocale = 32; fMetricUnits = ( GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IMEASURE, achLocale, iLocale) && achLocale[0] == TCHAR('0')); } // Now, initialize the event's type and expandos. { BSTR bstrTemp = SysAllocString( L"pagesetup" ); VARIANT var; if (bstrTemp) { pEvent->put_type( bstrTemp ); SysFreeString(bstrTemp); } V_VT(&var) = VT_PTR; V_BYREF(&var) = ppagesetupdlg; hr = pEvent->setAttribute(_T("pagesetupStruct"), var, 0); if (hr) goto Cleanup; V_VT(&var) = VT_BSTR; bstrTemp = SysAllocString(_achHeader); if (bstrTemp) { V_BSTR(&var) = bstrTemp; hr = pEvent->setAttribute(_T("pagesetupHeader"), var, 0); SysFreeString(bstrTemp); } bstrTemp = SysAllocString(_achFooter); if (bstrTemp) { V_BSTR(&var) = bstrTemp; hr = pEvent->setAttribute(_T("pagesetupFooter"), var, 0); SysFreeString(bstrTemp); } } // Fill out PAGESETUPDLG structure ::ZeroMemory(ppagesetupdlg, sizeof(PAGESETUPDLG)); ppagesetupdlg->lStructSize = sizeof(PAGESETUPDLG); ppagesetupdlg->hwndOwner = hWnd; ppagesetupdlg->hDevMode = _hDevMode; ppagesetupdlg->hDevNames = _hDevNames; ppagesetupdlg->Flags |= PSD_DEFAULTMINMARGINS; if (_ptPaperSize.x != -1) { ppagesetupdlg->ptPaperSize = _ptPaperSize; } if (_rcMargin.left != -1) { ppagesetupdlg->Flags |= PSD_MARGINS; ppagesetupdlg->rtMargin = _rcMargin; if (fMetricUnits) { // Margins from PrintInfoBag are in 1/100000" and need to be converted to 1/100 mm. ppagesetupdlg->rtMargin.left = MulDivQuick(ppagesetupdlg->rtMargin.left , 2540, 100000); ppagesetupdlg->rtMargin.right = MulDivQuick(ppagesetupdlg->rtMargin.right , 2540, 100000); ppagesetupdlg->rtMargin.top = MulDivQuick(ppagesetupdlg->rtMargin.top , 2540, 100000); ppagesetupdlg->rtMargin.bottom = MulDivQuick(ppagesetupdlg->rtMargin.bottom, 2540, 100000); } else { // Margins from PrintInfoBag are in 1/100000" and need to be converted to 1/1000". ppagesetupdlg->rtMargin.left = ppagesetupdlg->rtMargin.left / 100; ppagesetupdlg->rtMargin.right = ppagesetupdlg->rtMargin.right / 100; ppagesetupdlg->rtMargin.top = ppagesetupdlg->rtMargin.top / 100; ppagesetupdlg->rtMargin.bottom = ppagesetupdlg->rtMargin.bottom / 100; } } CommCtrlNativeFontSupport(); V_VT(&varIn) = VT_UNKNOWN; V_UNKNOWN(&varIn) = pEvent; // Query host to show dialog if (pUICommandHandler) { // We may be marshalling this call across threads. RPC doesn't allow VT_PTRs to cross threads. // We work around this by sticking the structure into a GHND contents and pass the VT_HANDLE. // We then delegate to the browse document, who will obtain a copy of the GHND pointer and use that for the VT_PTR // the struct. (CDoc::DelegateShowPrintingDialog) // // In theory, we could detect this by looking at the __IE_uPrintFlags to see if it is flagged synchronous to avoid playing with handles. // The downside: as with all dialogArguments, anyone could have mucked around with the flags) VARIANT var; V_VT(&var) = VT_HANDLE; V_BYREF(&var) = hPageSetup; pEvent->setAttribute(_T("hPageSetup"), var, 0); // Delegate call to browse Trident hr = pUICommandHandler->Exec( NULL, // For Trident OLECMDID_SHOWPAGESETUP, 0, &varIn, NULL); pEvent->removeAttribute(_T("hPageSetup"), 0, NULL); } if ( !pUICommandHandler || hr == OLECMDERR_E_NOTSUPPORTED || hr == OLECMDERR_E_UNKNOWNGROUP || hr == E_FAIL || hr == E_NOTIMPL ) { ClearInterface(&pUICommandHandler); // Create backup UI Handler // (greglett) Cache this - CoCreate is expensive. hr = CoCreateInstance(CLSID_DocHostUIHandler, NULL, CLSCTX_INPROC_SERVER, IID_IDocHostUIHandler, (void**)&pUIHandler); if (!pUIHandler) goto Cleanup; hr = pUIHandler->QueryInterface(IID_IOleCommandTarget,(void**)&pUICommandHandler); if (!pUICommandHandler) goto Cleanup; hr = pUICommandHandler->Exec( &CGID_DocHostCommandHandler, // For a dochost object OLECMDID_SHOWPAGESETUP, 0, &varIn, NULL); } // If the dialog was cancelled, or there was a problem showing the dialog, // do not update values. if (hr) goto Cleanup; *p = VB_TRUE; // OK was pressed. // // Retrieve page setup changes from the page setup dialog structure. // _hDevMode = ppagesetupdlg->hDevMode; _hDevNames = ppagesetupdlg->hDevNames; GetDeviceProperties(); if (fMetricUnits) { // Margins from Page Setup dialog are in 1/100 mm and need to be converted to 1/100000" _rcMargin.left = MulDivQuick(ppagesetupdlg->rtMargin.left , 100000, 2540); _rcMargin.right = MulDivQuick(ppagesetupdlg->rtMargin.right , 100000, 2540); _rcMargin.top = MulDivQuick(ppagesetupdlg->rtMargin.top , 100000, 2540); _rcMargin.bottom = MulDivQuick(ppagesetupdlg->rtMargin.bottom, 100000, 2540); } else { // Margins from Page Setup dialog are in 1/1000" and need to be converted to 1/100000" _rcMargin.left = ppagesetupdlg->rtMargin.left * 100; _rcMargin.right = ppagesetupdlg->rtMargin.right * 100; _rcMargin.top = ppagesetupdlg->rtMargin.top * 100; _rcMargin.bottom = ppagesetupdlg->rtMargin.bottom * 100; } // (greglett) 99% of OS's use PSD_DEFAULTMINMARGINS to restrict the margins to the printable page area. // NT4SP6 without the IE shell extensions doesn't. So, we are forced to do more work for yet another violation of the API documentation. // Force margins to be at least as large as the unprintable region. // Do we want also to display an error message if they are different? (Otherwise, we change it, and the user doesn't see it.) if (_rcMargin.left < MulDivQuick(_rcUnprintable.left, 100000, _szResolution.cx)) _rcMargin.left = MulDivQuick(_rcUnprintable.left, 100000, _szResolution.cx); if (_rcMargin.right < MulDivQuick(_rcUnprintable.right, 100000, _szResolution.cx)) _rcMargin.right = MulDivQuick(_rcUnprintable.right, 100000, _szResolution.cx); if (_rcMargin.top < MulDivQuick(_rcUnprintable.top, 100000, _szResolution.cy)) _rcMargin.top = MulDivQuick(_rcUnprintable.top, 100000, _szResolution.cy); if (_rcMargin.bottom < MulDivQuick(_rcUnprintable.bottom, 100000, _szResolution.cy)) _rcMargin.bottom = MulDivQuick(_rcUnprintable.bottom, 100000, _szResolution.cy); // // Read in Trident specific values from the page setup dialog // { VARIANT var; TCHAR *pchTemp; if ( !pEvent->getAttribute(_T("pagesetupHeader"),0,&var) && var.vt == VT_BSTR && var.bstrVal) { pchTemp = var.bstrVal; _tcscpy(_achHeader, pchTemp); SysFreeString(var.bstrVal); } else _achHeader[0] = _T('\0'); if ( !pEvent->getAttribute(_T("pagesetupFooter"),0,&var) && var.vt == VT_BSTR && var.bstrVal) { pchTemp = var.bstrVal; _tcscpy(_achFooter, pchTemp); SysFreeString(var.bstrVal); } else _achFooter[0] = _T('\0'); } // // Persist results of the page setup dialog out to the registry. // { HKEY keyPageSetup = NULL; if (GetRegPrintOptionsKey(PRINTOPTSUBKEY_PAGESETUP,&keyPageSetup) == ERROR_SUCCESS) { if (_fPersistHFToRegistry) { WriteHeaderFooterToRegistry(keyPageSetup); } WriteMarginsToRegistry(keyPageSetup); RegCloseKey(keyPageSetup); } } Cleanup: ReleaseInterface(pEvent); ReleaseInterface(pUIHandler); ReleaseInterface(pUICommandHandler); if (hPageSetup) { if (ppagesetupdlg) ::GlobalUnlock(hPageSetup); ::GlobalFree(hPageSetup); } return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::GetPrintDialogSettings // // Synopsis : 'Finishes' the doc - takes pages printed via printPage and queues the job // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::GetPrintDialogSettings(BOOL fBotherUser, VARIANT_BOOL *pvarfOKOrCancel) { HRESULT hr; HWND hWnd = NULL; IHTMLEventObj2 *pEvent = NULL; // To populate with dialog parameters IDocHostUIHandler *pUIHandler = NULL; IOleCommandTarget *pUICommandHandler = NULL; HGLOBAL hPrint = NULL; PRINTDLG * pprintdlg = NULL; VARIANT varIn; int nFontSize; void *pDevMode = NULL; if (pvarfOKOrCancel) *pvarfOKOrCancel = VB_FALSE; hPrint = ::GlobalAlloc(GHND, sizeof(PRINTDLG)); if (!hPrint) { hr = E_OUTOFMEMORY; goto Cleanup; } pprintdlg = (PRINTDLG *)::GlobalLock(hPrint); if (!pprintdlg) { hr = E_FAIL; goto Cleanup; } if (_hDC) { hr = E_FAIL; goto Cleanup; } hr = InitDialog(&hWnd, &pEvent, &pUICommandHandler, &nFontSize); if (hr) goto Cleanup; // PrintDlgEx (only available NT5+) fails with a NULL HWND. // It is likely that the DHUI that raises the dialog will use PrintDlgEx (our default DHUI in shdocvw does). if ( g_dwPlatformID == VER_PLATFORM_WIN32_NT && hWnd == NULL ) hWnd = GetDesktopWindow(); // PrintDlgEx (only available NT5+) fails with a NULL HWND. // It is likely that the DHUI that raises the dialog will use PrintDlgEx (our default DHUI in shdocvw does). if ( g_dwPlatformID == VER_PLATFORM_WIN32_NT && hWnd == NULL ) hWnd = GetDesktopWindow(); // Now, initialize the event's type and expandos. { BSTR bstrTemp = SysAllocString( L"print" ); VARIANT var; if (bstrTemp) { pEvent->put_type(bstrTemp); SysFreeString(bstrTemp); } V_VT(&var) = VT_BOOL; V_BOOL(&var) = AreRatingsEnabled() ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfAreRatingsEnabled"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = _fFramesetDocument ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfRootDocumentHasFrameset"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = _fFrameActive ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfActiveFrame"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = _fFrameActiveEnabled ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfActiveFrameEnabled"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = _fFrameAsShown ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfAsShown"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = _fPrintAllLinkedDocuments ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfLinked"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = _fPrintSelectionEnabled ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfSelection"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = _fPrintTableOfLinks ? VB_TRUE : VB_FALSE; hr = pEvent->setAttribute(_T("printfShortcutTable"), var, 0); if (hr) goto Cleanup; V_BOOL(&var) = VB_FALSE; hr = pEvent->setAttribute(_T("printToFileOk"),var, 0); if (hr) goto Cleanup; V_VT(&var) = VT_INT; V_INT(&var) = nFontSize; hr = pEvent->setAttribute(_T("printiFontScaling"), var, 0); if (hr) goto Cleanup; V_VT(&var) = VT_PTR; V_BYREF(&var) = pprintdlg; hr = pEvent->setAttribute(_T("printStruct"), var, 0); if (hr) goto Cleanup; V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = NULL; hr = pEvent->setAttribute(_T("printpBodyActiveTarget"), var, 0); if (hr) goto Cleanup; V_VT(&var) = VT_BSTR; bstrTemp = SysAllocString(_achFileName); if (bstrTemp) { V_BSTR(&var) = bstrTemp; hr = pEvent->setAttribute(_T("printToFileName"), var, 0); SysFreeString(bstrTemp); } } // // Initialize the PRINTDLG structure // ::ZeroMemory(pprintdlg, sizeof(PRINTDLG)); pprintdlg->lStructSize = sizeof(PRINTDLG); pprintdlg->hwndOwner = hWnd; pprintdlg->hDevMode = _hDevMode; pprintdlg->hDevNames = _hDevNames; pprintdlg->nCopies = _nCopies; pprintdlg->nFromPage = _nPageFrom; pprintdlg->nToPage = _nPageTo; pprintdlg->nMinPage = 1; pprintdlg->nMaxPage = 0xffff; pprintdlg->Flags |= (_fPrintSelectionEnabled ? 0 : PD_NOSELECTION); pprintdlg->Flags |= (_fCollate ? PD_COLLATE : 0); pprintdlg->Flags |= (_fPrintSelectedPages ? PD_PAGENUMS : 0); pprintdlg->Flags |= (_fPrintToFile ? PD_PRINTTOFILE : 0); pprintdlg->Flags |= (_fCurrentPageAvail ? (_fPrintCurrentPage ? PD_CURRENTPAGE : 0) : PD_NOCURRENTPAGE); if (!fBotherUser) { // this indicates we only want to retrieve the defaults, // not to bring up the dialog pprintdlg->hDevMode = NULL; pprintdlg->hDevNames = NULL; pprintdlg->Flags |= PD_RETURNDEFAULT; } CommCtrlNativeFontSupport(); V_VT(&varIn) = VT_UNKNOWN; V_UNKNOWN(&varIn) = pEvent; // Query host to show dialog hr = E_FAIL; if ( pvarfOKOrCancel // Don't delegate on the ::Init call. Only delegate script calls. && pUICommandHandler) { // We may be marshalling this call across threads. RPC doesn't allow VT_PTRs to cross threads. // We work around this by sticking the structure into a GHND contents and pass the VT_HANDLE. // We then delegate to the browse document, who will obtain a copy of the GHND pointer and use that for the VT_PTR // the struct. (CDoc::DelegateShowPrintingDialog) // // In theory, we could detect this by looking at the __IE_uPrintFlags to see if it is flagged synchronous to avoid playing with handles. // The downside: as with all dialogArguments, anyone could have mucked around with the flags) VARIANT var; V_VT(&var) = VT_HANDLE; V_BYREF(&var) = hPrint; pEvent->setAttribute(_T("hPrint"), var, 0); // Delegate call to browse Trident hr = pUICommandHandler->Exec( NULL, // For Trident OLECMDID_SHOWPRINT, 0, &varIn, NULL); pEvent->removeAttribute(_T("hPrint"), 0, NULL); } if ( hr == OLECMDERR_E_NOTSUPPORTED || hr == OLECMDERR_E_UNKNOWNGROUP || hr == E_FAIL || hr == E_NOTIMPL ) { ClearInterface(&pUICommandHandler); // Create backup UI Handler // (greglett) Cache this - CoCreate is expensive. hr = CoCreateInstance(CLSID_DocHostUIHandler, NULL, CLSCTX_INPROC_SERVER, IID_IDocHostUIHandler, (void**)&pUIHandler); if (!pUIHandler) goto Cleanup; hr = pUIHandler->QueryInterface(IID_IOleCommandTarget,(void**)&pUICommandHandler); if (!pUICommandHandler) goto Cleanup; hr = pUICommandHandler->Exec( &CGID_DocHostCommandHandler, OLECMDID_SHOWPRINT, 0, &varIn, NULL); } // If the dialog was cancelled, or there was a problem showing the dialog, // do not update values. if (FAILED(hr)) goto Cleanup; // this can be false because either init failed for no installed printer // or the user clicked cancel. if (hr==S_FALSE) { // Three cases: // fBotherUser *pvarfOkOrCancel // 1 1 fBotherUser && varfOkOrCancel - We are being called from script. We tried to raise a dialog, and // failed or were cancelled - UI has been displayed in the former case. // 0 1 We are being called from script. We tried to get default printer info // and failed - this is probably because no default it set. As we used // to, we need to raise the dialog (despite the fBotherUser) to get enough info. // 0 0 We are being called from the init, with no default printer. We will continue // through the procedure to get defaults. // 1 0 Never occurs. Assert(!(!pvarfOKOrCancel && fBotherUser)); if (fBotherUser) { hr = S_OK; goto Cleanup; } else if (pvarfOKOrCancel) { hr = GetPrintDialogSettings(TRUE, pvarfOKOrCancel); goto Cleanup; } // Otherwise, we need to set the default values. else hr = S_OK; } else if (pvarfOKOrCancel) *pvarfOKOrCancel = VB_TRUE; // OK was pressed. // // Take base print dialog return values and store them. // _hDevMode = pprintdlg->hDevMode; _hDevNames = pprintdlg->hDevNames; _nCopies = (fBotherUser) ? pprintdlg->nCopies : 1; // bug in printDLG _nPageFrom = pprintdlg->nFromPage; _nPageTo = pprintdlg->nToPage; _fPrintSelectedPages = (!!(pprintdlg->Flags & PD_PAGENUMS)); _fPrintSelection = (!!(pprintdlg->Flags & PD_SELECTION)); _fCollate = (!!(pprintdlg->Flags & PD_COLLATE)); _fPrintToFile = (!!(pprintdlg->Flags & PD_PRINTTOFILE)); _fPrintCurrentPage = (!!(pprintdlg->Flags & PD_CURRENTPAGE)); // Collate/Copy information is in both the struct and the DEVMODE. // The DEVMODE bits instruct the printer to do its own copy/collate information. // This means that only one copy of the document is uploaded to the printer/server, and the printer itself does the replication/duplex work. if (_hDevMode) { pDevMode = ::GlobalLock(_hDevMode); if (pDevMode) { // (greglett) DEVMODE is returned A on Win9x platforms, not W. if (g_fUnicodePlatform) { if (((DEVMODEW *)pDevMode)->dmFields & DM_COLLATE) _fCollate = (((DEVMODEW *)pDevMode)->dmCollate == DMCOLLATE_TRUE) || _fCollate; if ( ((DEVMODEW *)pDevMode)->dmFields & DM_COPIES && ((DEVMODEW *)pDevMode)->dmCopies > _nCopies ) _nCopies = ((DEVMODEW *)pDevMode)->dmCopies; } else { if (((DEVMODEA *)pDevMode)->dmFields & DM_COLLATE) _fCollate = (((DEVMODEA *)pDevMode)->dmCollate == DMCOLLATE_TRUE) || _fCollate; if ( ((DEVMODEA *)pDevMode)->dmFields & DM_COPIES && ((DEVMODEA *)pDevMode)->dmCopies > _nCopies ) _nCopies = ((DEVMODEA *)pDevMode)->dmCopies; } ::GlobalUnlock(_hDevMode); } } GetDeviceProperties(); // Read in changes to the Trident specific print options. { VARIANT var; // Read changed values from event object if (!pEvent->getAttribute(_T("printfLinked"),0,&var)) { Assert(var.vt == VT_BOOL); _fPrintAllLinkedDocuments = var.boolVal; } if (!pEvent->getAttribute(_T("printfActiveFrame"), 0, &var)) { Assert(var.vt == VT_BOOL); _fFrameActive = var.boolVal; } if (!pEvent->getAttribute(_T("printfAsShown"), 0, &var)) { Assert(var.vt == VT_BOOL); _fFrameAsShown = var.boolVal; } if (!pEvent->getAttribute(_T("printfShortcutTable"), 0, &var)) { Assert(var.vt == VT_BOOL); _fPrintTableOfLinks = var.boolVal; } } if (_fPrintToFile) { // assume failure, treating as canceling VARIANT var; hr = S_FALSE; if ( !pEvent->getAttribute(_T("printToFileOK"), 0, &var) && var.boolVal) { if ( !pEvent->getAttribute(_T("printToFileName"), 0, &var) && var.vt == VT_BSTR) { TCHAR * pchFullPath = var.bstrVal; _tcscpy(_achFileName, pchFullPath); SysFreeString(var.bstrVal); // (greglett) Code used to update Trident's default save path here. (pre 5.5) // Not being internal, we can't do that anymore. hr = S_OK; } } if (hr != S_OK) *pvarfOKOrCancel = VB_FALSE; } Cleanup: ReleaseInterface(pUIHandler); ReleaseInterface(pUICommandHandler); ReleaseInterface(pEvent); if (hPrint) { if (pprintdlg) ::GlobalUnlock(hPrint); ::GlobalFree(hPrint); } return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::InitDialog // // Synopsis : Does all the COM schtick to get the appropriate interfaces to show // a dialog. // // Parameters: hWnd: Will try to fill with Trident's hWnd. May return as NULL with S_OK. // ppEventObj2: Will create an IHTMLEventObj2. Must be created if S_OK is returned. // ppUIHandler: Will return Trident's UI Handler. May return NULL with S_OK. // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::InitDialog(HWND *phWnd, IHTMLEventObj2 **ppEventObj2, IOleCommandTarget **ppUIHandler, int *pnFontScale) { HRESULT hr; IHTMLElement *pElement = NULL; IDispatch *pDispatch = NULL; IHTMLDocument2 *pDoc = NULL; IOleWindow *pDocWin = NULL; IHTMLEventObj *pEventObj = NULL; VARIANT varHost; Assert(phWnd); Assert(ppEventObj2); Assert(ppUIHandler); VariantInit(&varHost); *phWnd = NULL; *ppEventObj2 = NULL; *ppUIHandler = NULL; if (pnFontScale) *pnFontScale = 2; if (!_pPeerSiteOM) { hr = E_FAIL; goto Cleanup; } if (!phWnd || !ppEventObj2 || !ppUIHandler) { hr = E_POINTER; goto Cleanup; } hr = _pPeerSite->GetElement(&pElement); if (hr) goto Cleanup; Assert(pElement); hr = pElement->get_document(&pDispatch); if (hr) goto Cleanup; Assert(pDispatch); hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void**) &pDoc); if (hr) goto Cleanup; hr = pDoc->QueryInterface(IID_IOleWindow, (void **)&pDocWin); if (!hr) { Assert(pDocWin); pDocWin->GetWindow(phWnd); } // Create an event object to pass to our host with the print information hr = _pPeerSiteOM->CreateEventObject(&pEventObj); if (hr) goto Cleanup; Assert(pEventObj); // Now get the appropriate interface to populate the event object with parameters // for the dialog. hr = pEventObj->QueryInterface(IID_IHTMLEventObj2, (void**)ppEventObj2); if (hr) goto Cleanup; Assert(ppEventObj2); // Get the instance of the Trident Host UI browse document, if it exists. // We use this to delegate to our host. if ( GetDialogArgument(&varHost, PRINTARG_BROWSEDOC) == S_OK && V_VT(&varHost) == VT_UNKNOWN && V_UNKNOWN(&varHost) ) { hr = V_UNKNOWN(&varHost)->QueryInterface(IID_IOleCommandTarget, (void **)ppUIHandler); Assert(*ppUIHandler); } Cleanup: ReleaseInterface(pElement); ReleaseInterface(pDispatch); ReleaseInterface(pDoc); ReleaseInterface(pDocWin); ReleaseInterface(pEventObj); VariantClear(&varHost); return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::ReturnPrintHandles // // Synopsis : Show our print handles to the browse instance of Trident. // //----------------------------------------------------------------------------- void CTemplatePrinter::ReturnPrintHandles() { HRESULT hr; VARIANT varHost; VARIANT varIn; SAFEARRAYBOUND sabound; IOleCommandTarget * pioct = NULL; SAFEARRAY * psa = NULL; long lArg = 0; VariantInit(&varHost); VariantInit(&varIn); if (!_hDevNames || !_hDevMode) goto Cleanup; // // Get handle back to the browse document to update its print handles. // if ( GetDialogArgument(&varHost, PRINTARG_BROWSEDOC) != S_OK || V_VT(&varHost) != VT_UNKNOWN || !V_UNKNOWN(&varHost) ) goto Cleanup; hr = V_UNKNOWN(&varHost)->QueryInterface(IID_IOleCommandTarget, (void **)&pioct); if (hr) goto Cleanup; Assert(pioct); // // Create a SAFEARRAY filled with our two print handles { DEVNAMES, DEVMODE } // sabound.cElements = 2; sabound.lLbound = 0; psa = SafeArrayCreate(VT_HANDLE, 1, &sabound); if (!psa) goto Cleanup; // // Bundle the array in the Exec argument... // V_VT(&varIn) = VT_ARRAY | VT_HANDLE; V_ARRAY(&varIn) = psa; if ( SafeArrayPutElement(psa, &lArg, &_hDevNames) != S_OK || SafeArrayPutElement(psa, &(++lArg), &_hDevMode) != S_OK ) { goto Cleanup; } // // Actually make the call, passing our handles to the browse instance // pioct->Exec( &CGID_MSHTML, IDM_SETPRINTHANDLES, NULL, &varIn, NULL); Cleanup: VariantClear(&varIn); // Destroys SAFEARRAY. VariantClear(&varHost); ReleaseInterface(pioct); return; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::GetDialogArgument // // Synopsis : The dialogArgument on the attached print template has several important expandos. // This function gets the dialogArguments and caches a ptr to it. // // Returens: S_OK: dlgArgs saved // E_*: Error encounterd // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::GetDialogArguments() { HRESULT hr; IHTMLElement *pElement = NULL; IDispatch *pDispatch = NULL; IServiceProvider *pIDocSrvProv = NULL; IHTMLDialog *pIHTMLDialog = NULL; VARIANT varDlgArgs; Assert(_pPeerSite); VariantInit(&varDlgArgs); hr = _pPeerSite->GetElement(&pElement); if (hr) goto Cleanup; Assert(pElement); hr = pElement->get_document(&pDispatch); if (hr) goto Cleanup; Assert(pDispatch); hr = pDispatch->QueryInterface(IID_IServiceProvider, (void **)&pIDocSrvProv); if (FAILED(hr)) goto Cleanup; Assert(pIDocSrvProv); hr = pIDocSrvProv->QueryService(IID_IHTMLDialog, IID_IHTMLDialog, (void**)&pIHTMLDialog); if (FAILED(hr)) goto Cleanup; Assert(pIHTMLDialog); hr = pIHTMLDialog->get_dialogArguments(&varDlgArgs); if (hr) goto Cleanup; if ( V_VT(&varDlgArgs) != VT_UNKNOWN || !V_UNKNOWN(&varDlgArgs) ) { hr = E_FAIL; // Major badness. This MUST be there. goto Cleanup; } hr = V_UNKNOWN(&varDlgArgs)->QueryInterface(IID_IHTMLEventObj2, (void**)&_pevDlgArgs); if (hr) goto Cleanup; Assert(_pevDlgArgs); Cleanup: ReleaseInterface(pElement); ReleaseInterface(pDispatch); ReleaseInterface(pIDocSrvProv); ReleaseInterface(pIHTMLDialog); VariantClear(&varDlgArgs); return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::GetDialogArgument // // Synopsis : The dialogArgument on the attached print template has several important expandos. // Function gets the expando specified by the argument enum // // Returens: S_OK: expando obtained (might be VT_NULL or VT_EMPTY) // E_*: Error encounterd // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::GetDialogArgument(VARIANT *pvar, PRINTARG eArg) { HRESULT hr = S_OK; BSTR bstrTarget = NULL; Assert(pvar); Assert(eArg >= 0 && eArg < PRINTTYPE_LAST); // (greglett) TODO: Implement a caching system for these args. We access some of // often, and shouldn't do the (potentially) cross-thread OLE each time. if (!_pevDlgArgs) return E_FAIL; VariantClear(pvar); bstrTarget = SysAllocString(s_aachPrintArg[eArg]); if (!bstrTarget) { hr = E_OUTOFMEMORY; goto Cleanup; } hr = _pevDlgArgs->getAttribute(bstrTarget, 0, pvar); Cleanup: SysFreeString(bstrTarget); return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::RemoveDialogArgument // // Synopsis : Remove the dialogArgument specified byt he argument enum. // // Returens: S_OK: expando obtained (might be VT_NULL or VT_EMPTY) // E_*: Error encounterd // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::RemoveDialogArgument(PRINTARG eArg) { HRESULT hr = S_OK; BSTR bstrTarget = NULL; VARIANT_BOOL fSuccess; Assert(eArg >= 0 && eArg > PRINTTYPE_LAST); if (!_pevDlgArgs) return E_FAIL; bstrTarget = SysAllocString(s_aachPrintArg[eArg]); if (!bstrTarget) { hr = E_OUTOFMEMORY; goto Cleanup; } hr = _pevDlgArgs->removeAttribute(bstrTarget, 0, &fSuccess); Cleanup: SysFreeString(bstrTarget); return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::EnsureMLLoadLibrary // // Synopsis : Ensure the library with the default header/footer/margins has // been loaded and return it. // //----------------------------------------------------------------------------- HINSTANCE CTemplatePrinter::EnsureMLLoadLibrary() { if (!_hInstResource) { _hInstResource = MLLoadLibrary(_T("shdoclc.dll"), NULL, ML_CROSSCODEPAGE); Assert(_hInstResource && "Resource DLL is not loaded!"); } return _hInstResource; } //+---------------------------------------------------------------------- // // Function: CTemplatePrinter::GetDefaultMargin // // Purpose: Get default values for the margin from // (1) The registry // (2) The resource DLL // (3) Arbitrary, hard-coded values. // // Parameters keyOldValues registry key for the margins or NULL // pMarginName "margin_top", "margin_bottom", &c... // pMarginValue buffer for value // cchMargin length of the buffer in TCHARs // dwMarginConst const for getting the margin from the resource file //----------------------------------------------------------------------- HRESULT CTemplatePrinter::GetDefaultMargin(HKEY keyOldValues, TCHAR* pMarginName, TCHAR* pMarginValue, DWORD cchMargin, DWORD dwMarginConst) { HRESULT hr = E_FAIL; DWORD cchLen; Assert(pMarginName); Assert(pMarginValue); Assert(cchMargin > 0); // First try the passed registry key. if (keyOldValues != NULL) { hr = ReadSubkeyFromRegistry(keyOldValues, pMarginName, pMarginValue, cchMargin); } // Next try the resource file. if (hr) { cchLen = ::LoadString(EnsureMLLoadLibrary(), dwMarginConst, pMarginValue, cchMargin); if (cchLen > 0) hr = ERROR_SUCCESS; } // Lastly, just do hardcoded values. if (hr) { cchLen = _tcslen(_T("0.750000")) + 1; if (cchLen <= cchMargin) { _tcscpy(pMarginValue,_T("0.750000")); hr = ERROR_SUCCESS; } } return hr; } //+--------------------------------------------------------------------------- // // Member: CTemplatePrinter::GetDefaultHeaderFooter // // Purpose: Get default values for the header/footer from // (1) The registry // (2) The resource DLL // (3) Arbitrary, hard-coded values. // // Arguments: keyOldValues If Not Null try to read from the IE3 defaults, If NULL or the read // was not successfull, get it from the resources // pValueName "header" or "footer" // pDefault ptr to the default header or footer // cbDefault size of the array to hold the header-footer (in TCHAR) // pDefaultLiteral default value if there is no def. in resources // // Returns : None // //---------------------------------------------------------------------------- HRESULT CTemplatePrinter::GetDefaultHeaderFooter(HKEY keyOldValues, TCHAR* pValueName, TCHAR* pDefault, DWORD cchDefault, DWORD dwResourceID, TCHAR* pDefaultLiteral) { HRESULT hr = E_FAIL; Assert(pValueName); Assert(pDefault); Assert(pDefaultLiteral); Assert(cchDefault > 0); // Try registry for a left/right header/footer first. if (keyOldValues != NULL) { TCHAR achName[32]; TCHAR achLeft [MAX_DEFAULTHEADFOOT] = _T(""); TCHAR achRight[MAX_DEFAULTHEADFOOT] = _T(""); TCHAR achSeparator[3] = _T("&b"); DWORD cchTotal = 0; _tcscpy(achName,pValueName); _tcscat(achName,_T("_left")); if (!ReadSubkeyFromRegistry(keyOldValues, achName, achLeft, MAX_DEFAULTHEADFOOT)) cchTotal += _tcslen(achLeft); _tcscpy(achName,pValueName); _tcscat(achName,_T("_right")); if (!ReadSubkeyFromRegistry(keyOldValues, achName, achRight, MAX_DEFAULTHEADFOOT)) cchTotal += _tcslen(achRight); if (cchTotal) { // Include the null - add it in. cchTotal += _tcslen(achSeparator) + 1; if (cchTotal <= cchDefault) { _tcscpy(pDefault,achLeft); _tcscat(pDefault,achSeparator); _tcscat(pDefault,achRight); hr = ERROR_SUCCESS; } } } // Concatenate the left/right if it exists and return it. if (hr) { DWORD cchLen; cchLen = ::LoadString(EnsureMLLoadLibrary(), dwResourceID, pDefault, cchDefault); if (cchLen > 0) hr = ERROR_SUCCESS; } // Otherwise, use the passed default value. if (hr) { if (_tcslen(pDefaultLiteral) + 1 <= cchDefault) { _tcscpy(pDefault, pDefaultLiteral); hr = ERROR_SUCCESS; } } return hr; } //+--------------------------------------------------------------------------- // // Member: CTemplatePrinter::GetDefaultPageSetupValues // // Synopsis: Try to get the old page setup values from HKEY_LOCAL_MACHINE. If found copies them into // HKEY_CURRENT_USER, if not, copies the default values // // Arguments: None // // Returns : S_OK or E_FAIL // // Summary : --- // //---------------------------------------------------------------------------- HRESULT CTemplatePrinter::GetDefaultPageSetupValues(HKEY keyExplorer,HKEY * pKeyPrintOptions) { TCHAR achDefaultHeader[MAX_DEFAULTHEADFOOT]; TCHAR achDefaultFooter[MAX_DEFAULTHEADFOOT]; TCHAR achDefaultMarginTop [MAX_MARGINLENGTH]; TCHAR achDefaultMarginBottom [MAX_MARGINLENGTH]; TCHAR achDefaultMarginLeft [MAX_MARGINLENGTH]; TCHAR achDefaultMarginRight [MAX_MARGINLENGTH]; HKEY keyOldValues; HRESULT hr = S_OK; if (!pKeyPrintOptions) { hr = E_POINTER; goto Cleanup; } // Check the default machine registry values if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Internet Explorer\\PageSetup"),0, KEY_ALL_ACCESS, &keyOldValues) != ERROR_SUCCESS) { keyOldValues = NULL; } GetDefaultHeaderFooter(keyOldValues, _T("header"), (TCHAR*)&achDefaultHeader, MAX_DEFAULTHEADFOOT, IDS_DEFAULTHEADER, _T("&w&bPage &p of &P")); GetDefaultHeaderFooter(keyOldValues, _T("footer"), (TCHAR*)&achDefaultFooter, MAX_DEFAULTHEADFOOT, IDS_DEFAULTFOOTER, _T("&u&b&d")); GetDefaultMargin(keyOldValues, _T("margin_bottom"), (TCHAR*)&achDefaultMarginBottom, MAX_MARGINLENGTH, IDS_DEFAULTMARGINBOTTOM); GetDefaultMargin(keyOldValues, _T("margin_top"), (TCHAR*)&achDefaultMarginTop, MAX_MARGINLENGTH, IDS_DEFAULTMARGINTOP); GetDefaultMargin(keyOldValues, _T("margin_left"), (TCHAR*)&achDefaultMarginLeft, MAX_MARGINLENGTH, IDS_DEFAULTMARGINLEFT); GetDefaultMargin(keyOldValues, _T("margin_right"), (TCHAR*)&achDefaultMarginRight, MAX_MARGINLENGTH, IDS_DEFAULTMARGINRIGHT); ::RegCloseKey(keyOldValues); keyOldValues = NULL; // Create the new user registry key if (::RegCreateKeyEx(keyExplorer, _T("PageSetup"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, pKeyPrintOptions, NULL) == ERROR_SUCCESS) { // Put our default values into registry keys. WriteSubkeyToRegistry(*pKeyPrintOptions, _T("header"), achDefaultHeader); WriteSubkeyToRegistry(*pKeyPrintOptions, _T("footer"), achDefaultFooter); WriteSubkeyToRegistry(*pKeyPrintOptions, _T("margin_bottom"), achDefaultMarginBottom); WriteSubkeyToRegistry(*pKeyPrintOptions, _T("margin_left"), achDefaultMarginLeft); WriteSubkeyToRegistry(*pKeyPrintOptions, _T("margin_right"), achDefaultMarginRight); WriteSubkeyToRegistry(*pKeyPrintOptions, _T("margin_top"), achDefaultMarginTop); }; Cleanup: return hr; } //+--------------------------------------------------------------------------- // // Member: CTemplatePrinter::GetRegPrintOptionsKey // // Synopsis: Get handle of requested key under \HKCU\Software\Microsoft\Internet Explorer // // Arguments: PrintSubKey - subkey of printoptions root to return key for // pKeyPrintOptions - ptr to handle of requested key in registry // // Returns : S_OK or E_FAIL // // Summary : First it tries to get the values from "new place" // HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\PageSetup // If there is no such a key, it creates it and tries to get the values from "old place" // HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\PageSetup // If successful it copies the values into the "new place" // If not, it tries to get the values from the registry, // If no luck, it uses the hardcoded strings // NOTE : If the procedure returns with S_OK, it guaranties that they will be a // "new place" with values. // //---------------------------------------------------------------------------- HRESULT CTemplatePrinter::GetRegPrintOptionsKey(PRINTOPTIONS_SUBKEY PrintSubKey, HKEY * pKeyPrintOptions) { HKEY keyExplorer; HRESULT hr = E_FAIL; if (RegOpenKeyEx( HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer"), 0, KEY_ALL_ACCESS, &keyExplorer) == ERROR_SUCCESS) { LPTSTR szSubKey = (PrintSubKey == PRINTOPTSUBKEY_MAIN ? _T("Main") : _T("PageSetup")); if (RegOpenKeyEx(keyExplorer, szSubKey, 0, KEY_ALL_ACCESS, pKeyPrintOptions) == ERROR_SUCCESS) { if (PrintSubKey == PRINTOPTSUBKEY_PAGESETUP) { // // For the PageSetup key, we do some additional checks to make // sure that (at least) the header and footer keys exist. // DWORD dwT; if ( (RegQueryValueEx(*pKeyPrintOptions, _T("header"), 0, NULL, NULL, &dwT) == ERROR_SUCCESS) && (RegQueryValueEx(*pKeyPrintOptions, _T("footer"), 0, NULL, NULL, &dwT) == ERROR_SUCCESS)) { // the header and footer keys exist, we're fine hr = S_OK; } else { // whoops. fall back... hr = GetDefaultPageSetupValues(keyExplorer, pKeyPrintOptions); } } else hr = S_OK; } else { // For page setup, if we don't have default values, create them. if (PrintSubKey == PRINTOPTSUBKEY_PAGESETUP) { hr = GetDefaultPageSetupValues(keyExplorer, pKeyPrintOptions); } } RegCloseKey(keyExplorer); } return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::ReadBoolFromRegistry // // Synopsis : Takes an open registry key and subkey name, and returns the // value as a boolean. // //----------------------------------------------------------------------------- BOOL CTemplatePrinter::ReadBoolFromRegistry(HKEY hKey, TCHAR *pSubkeyName) { TCHAR achBool[MAX_DEFAULTBOOL]; BOOL fRet = FALSE; achBool[0] = '\0'; if (!ReadSubkeyFromRegistry(hKey, pSubkeyName, achBool, MAX_DEFAULTBOOL)) { fRet = !_tcsicmp(achBool, _T("yes")); } return fRet; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::AreRatingsEnabled // // Synopsis : Checks MS_RATING.DLL to see if ratings are enabled. // //----------------------------------------------------------------------------- BOOL CTemplatePrinter::AreRatingsEnabled() { BOOL fRet = FALSE; typedef HRESULT (STDAPICALLTYPE *PFN)(void); if (!_hInstRatings) LoadLibrary("MSRATING.DLL", &_hInstRatings); if (_hInstRatings) { PFN pfn; pfn = (PFN) ::GetProcAddress(_hInstRatings, "RatingEnabledQuery"); if (!pfn) { goto Cleanup; } fRet = !pfn() ? TRUE: FALSE; } Cleanup: return fRet; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::DecimalTCHARToMargin // // Synopsis : Takes a decimal representation in 1" and returns a long // with that value in 1/100000" // //----------------------------------------------------------------------------- long CTemplatePrinter::DecimalTCHARToFixed(TCHAR* pString, int nPowersOfTen) { TCHAR* p = pString; int iLen = _tcslen(pString); int i; int j = 0; int iChar = 0; long nRet = 0; if (pString == NULL) goto Cleanup; // Clear leading whitespace for (i=0;i _T('9'))) break; nRet = nRet * 10 + (iChar - _T('0')); } if (iChar == _T('.')) { // Do the decimal part. for (i++,p++; (i+j _T('9'))) break; nRet = nRet * 10 + (iChar - _T('0')); } } // Make sure we are in 1/100000" for (;j < nPowersOfTen; j++) nRet *= 10; Cleanup: return nRet; } BOOL CTemplatePrinter::CommCtrlNativeFontSupport() { BOOL fRet = FALSE; typedef BOOL (APIENTRY *PFN)(LPINITCOMMONCONTROLSEX); if (!_hInstComctl32) LoadLibrary("COMCTL32.DLL", &_hInstComctl32); if (_hInstComctl32) { INITCOMMONCONTROLSEX icc; PFN pfn; pfn = (PFN) ::GetProcAddress(_hInstComctl32, "InitCommonControlsEx"); if (!pfn) { goto Cleanup; } icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_NATIVEFNTCTL_CLASS; fRet = pfn(&icc); } Cleanup: return fRet; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::WriteFixedToRegistry // // Synopsis : Takes an open registry key and subkey name, and writes the // passed value to the resulting registry key in whole units. // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::WriteFixedToRegistry(HKEY hKeyPS, const TCHAR* pValueName,LONG nMargin, int nFactor) { TCHAR achFixed[MAX_MARGINLENGTH]; // Convert 1/100000" units to a TCHAR representation of decimal in 1" units. FixedToDecimalTCHAR(nMargin, achFixed, nFactor); return WriteSubkeyToRegistry(hKeyPS, pValueName, achFixed); } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::ReadFixedFromRegistry // // Synopsis : Takes an open registry key and subkey name, and gets a fixed point value // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::ReadFixedFromRegistry(HKEY hKeyPS, const TCHAR *pValueName, LONG *pFixed, int nPowersOfTen) { HRESULT hr; TCHAR achFixed[MAX_MARGINLENGTH]; achFixed[0] = '\0'; Assert(pValueName); Assert(pFixed); *pFixed = 0; hr = ReadSubkeyFromRegistry(hKeyPS, pValueName, achFixed, MAX_MARGINLENGTH); if (!hr) *pFixed = DecimalTCHARToFixed(achFixed, nPowersOfTen); return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::ReadDeviceUnicode // // Synopsis : Creates device information given the printer name, // //----------------------------------------------------------------------------- HRESULT CTemplatePrinter::ReadDeviceUnicode(TCHAR *pchPrinter, TCHAR *pchDriver, TCHAR *pchPort) { HRESULT hr = S_OK; HANDLE hPrinter = NULL; PRINTER_INFO_2 * pPrintInfo = NULL; Assert(pchPrinter); if ( ::OpenPrinter(pchPrinter, &hPrinter, NULL) && hPrinter) { DWORD nStructSize; if (!pchDriver || !pchPort) { ::GetPrinter(hPrinter, 2, NULL, 0, &nStructSize); pPrintInfo = (PRINTER_INFO_2 *) ::GlobalAlloc(GPTR, nStructSize); if (!pPrintInfo) { hr = E_OUTOFMEMORY; goto Cleanup; } if (!::GetPrinter(hPrinter, 2, (byte *)pPrintInfo, nStructSize, &nStructSize)) { hr = E_FAIL; goto Cleanup; } } hr = CreateDevNames(pchDriver ? pchDriver : pPrintInfo->pDriverName, pchPrinter, pchPort ? pchPort : pPrintInfo->pPortName, &_hDevNames); if (hr) goto Cleanup; nStructSize = ::DocumentProperties(0, hPrinter, pchPrinter, NULL, NULL, 0); if (nStructSize < sizeof(DEVMODE)) { Assert(!"Memory size suggested by DocumentProperties is smaller than DEVMODE"); nStructSize = sizeof(DEVMODE); } _hDevMode = ::GlobalAlloc(GHND, nStructSize); if (_hDevMode) { DEVMODE *pDevMode = (DEVMODE *) ::GlobalLock(_hDevMode); if (pDevMode) { ::DocumentProperties(0, hPrinter, pchPrinter, pDevMode, NULL, DM_OUT_BUFFER); pDevMode->dmFields &= ~DM_COLLATE; pDevMode->dmCollate = DMCOLLATE_FALSE; ::GlobalUnlock(_hDevMode); } else { ::GlobalFree(_hDevMode); _hDevMode = NULL; } } } Cleanup: if (hPrinter) ::ClosePrinter(hPrinter); if (pPrintInfo) ::GlobalFree(pPrintInfo); return hr; } //+---------------------------------------------------------------------------- // // Member : CTemplatePrinter::ReadDeviceNonUnicode // // Synopsys : Because non-Unicode platforms (Win9x) don't properly implement // many of the printing widechar calls, they need to explicitly // make multibyte (A) calls. // //+---------------------------------------------------------------------------- HRESULT CTemplatePrinter::ReadDeviceNonUnicode(TCHAR *pchPrinterWide, TCHAR *pchDriverWide, TCHAR *pchPortWide) { HRESULT hr = S_OK; HANDLE hPrinter = NULL; LPSTR pchPrinter = NULL; TCHAR * pchDriverWideLocal = NULL; TCHAR * pchPortWideLocal = NULL; PRINTER_INFO_2A * pPrintInfo = NULL; Assert(pchPrinterWide); pchPrinter = InitMultiByteFromWideChar(pchPrinterWide); if (!pchPrinter) { hr = E_FAIL; goto Cleanup; } if ( ::OpenPrinterA(pchPrinter, &hPrinter, NULL) && hPrinter ) { DWORD nStructSize; if (!pchDriverWide || !pchPortWide) { ::GetPrinterA(hPrinter, 2, NULL, 0, &nStructSize); pPrintInfo = (PRINTER_INFO_2A *)::GlobalAlloc(GPTR, nStructSize); if (!pPrintInfo) { hr = E_OUTOFMEMORY; goto Cleanup; } if (!::GetPrinterA(hPrinter, 2, (byte *)pPrintInfo, nStructSize, &nStructSize)) { hr = E_FAIL; goto Cleanup; } pchDriverWideLocal = InitWideCharFromMultiByte(pPrintInfo->pDriverName); pchPortWideLocal = InitWideCharFromMultiByte(pPrintInfo->pPortName); } hr = CreateDevNames(pchDriverWide ? pchDriverWide : pchDriverWideLocal, pchPrinterWide, pchPortWide ? pchPortWide : pchPortWideLocal, &_hDevNames); if (hr) goto Cleanup; // NB (105850) (mikhaill) -- Windows Millennium's routine DocumentPropertiesA() // impudently changes processor state. This happens just once after // reboot and cause, in particular, unmasking floating point exception flags. // At some later moment processor meet any suspicious condition (overflow, // underflow, zero-divide, precision loose, etc) in some innocent routine, // generates unhandled exception and eventually crashes. // The following fsave/frstor pair is an ugly patch that should be removed // after millennium bug fix. // Windows Millennium build versions tested: 4.90.2485, 4.90.2491. #ifdef _M_IX86 { FLOATING_SAVE_AREA fsa; _asm fsave fsa; #endif //_M_IX86 nStructSize = ::DocumentPropertiesA(0, hPrinter, pchPrinter, NULL, NULL, 0); #ifdef _M_IX86 _asm frstor fsa; } #endif //_M_IX86 if (nStructSize < sizeof(DEVMODEA)) { Assert(!"Memory size suggested by DocumentProperties is smaller than DEVMODEA"); nStructSize = sizeof(DEVMODEA); } _hDevMode = ::GlobalAlloc(GHND, nStructSize); if (_hDevMode) { DEVMODEA *pDevMode = (DEVMODEA *) ::GlobalLock(_hDevMode); if (pDevMode) { // NB (109499) same as 105850 above // but appeared in Windows98 (mikhaill 5/7/00) #ifdef _M_IX86 { FLOATING_SAVE_AREA fsa; _asm fsave fsa; #endif //_M_IX86 ::DocumentPropertiesA(0, hPrinter, pchPrinter, pDevMode, NULL, DM_OUT_BUFFER); #ifdef _M_IX86 _asm frstor fsa; } #endif //_M_IX86 pDevMode->dmFields &= ~DM_COLLATE; pDevMode->dmCollate = DMCOLLATE_FALSE; ::GlobalUnlock(_hDevMode); } else { ::GlobalFree(_hDevMode); _hDevMode = NULL; } } } Cleanup: if (hPrinter) ::ClosePrinter(hPrinter); if (pPrintInfo) ::GlobalFree(pPrintInfo); if (pchPrinter) delete []pchPrinter; if (pchDriverWideLocal) delete []pchDriverWideLocal; if (pchPortWideLocal) delete []pchPortWideLocal; return hr; } //+----------------------------------------------------------------------------- // // Member: CTemplatePrinter::GetDeviceProperties // // Synopsis : Gets the relevant physical properties of the device currently specified // in _hDevNames and _hDevMode // //------------------------------------------------------------------------------ HRESULT CTemplatePrinter::GetDeviceProperties() { IHTMLElementRender *pRender = NULL; IHTMLElement *pElement = NULL; BSTR bstrPrinter = NULL; HRESULT hr = E_FAIL; if (_hDevNames && _hDevMode) { DEVNAMES *pDevNames = ((DEVNAMES *)::GlobalLock(_hDevNames)); void *pDevMode = ::GlobalLock(_hDevMode); if (pDevNames && pDevMode) { HDC hDC = NULL; // (greglett) Non-Unicode badness. See comment at definition of _hDevMode if (g_fUnicodePlatform) { hDC = ::CreateICW(((TCHAR *)pDevNames) + pDevNames->wDriverOffset, ((TCHAR *)pDevNames) + pDevNames->wDeviceOffset, NULL, (DEVMODEW *)pDevMode); } else { LPSTR pchDriver = InitMultiByteFromWideChar(((TCHAR *)pDevNames) + pDevNames->wDriverOffset); LPSTR pchDevice = InitMultiByteFromWideChar(((TCHAR *)pDevNames) + pDevNames->wDeviceOffset); if (pchDriver && pchDevice) { hDC = ::CreateICA(pchDriver, pchDevice, NULL, (DEVMODEA *)pDevMode); } if (pchDriver) delete []pchDriver; if (pchDevice) delete []pchDevice; } if (hDC) { SIZE szPage; // Obtain the resolution and unprintable areas for this device. _szResolution.cx = ::GetDeviceCaps(hDC, LOGPIXELSX); _szResolution.cy = ::GetDeviceCaps(hDC, LOGPIXELSY); szPage.cx = ::GetDeviceCaps(hDC, PHYSICALWIDTH); szPage.cy = ::GetDeviceCaps(hDC, PHYSICALHEIGHT); _rcUnprintable.left = ::GetDeviceCaps(hDC, PHYSICALOFFSETX); _rcUnprintable.top = ::GetDeviceCaps(hDC, PHYSICALOFFSETY); _rcUnprintable.right = szPage.cx - ::GetDeviceCaps(hDC, HORZRES) - _rcUnprintable.left; _rcUnprintable.bottom = szPage.cy - ::GetDeviceCaps(hDC, VERTRES) - _rcUnprintable.top; Assert(_rcUnprintable.right >= 0); Assert(_rcUnprintable.bottom >= 0); _ptPaperSize.x = (_szResolution.cx) ? MulDivQuick(szPage.cx, 1000, _szResolution.cx) : 8500; _ptPaperSize.y = (_szResolution.cy) ? MulDivQuick(szPage.cy, 1000, _szResolution.cy) : 11000; hr = _pPeerSite->GetElement(&pElement); if (!hr) { Assert(pElement); hr = pElement->QueryInterface(IID_IHTMLElementRender, (void **)&pRender); if (!hr) { Assert(pRender); bstrPrinter = ::SysAllocString(((TCHAR *)pDevNames) + pDevNames->wDeviceOffset); if (bstrPrinter) pRender->SetDocumentPrinter(bstrPrinter,hDC); else hr = E_OUTOFMEMORY; } } ::DeleteDC(hDC); } } ::GlobalUnlock(_hDevNames); ::GlobalUnlock(_hDevMode); hr = S_OK; } ReleaseInterface(pElement); ReleaseInterface(pRender); if (bstrPrinter) ::SysFreeString(bstrPrinter); return hr; } HRESULT CTemplatePrinter::CreateIPrintParams(DVTARGETDEVICE **ppTargetDevice, PAGESET **ppPageSet) { HRESULT hr = S_OK; DWORD nStructSize; Assert(ppTargetDevice && ppPageSet); (*ppTargetDevice) = NULL; (*ppPageSet) = NULL; // Create a PAGESET. (*ppPageSet) = (PAGESET *)::CoTaskMemAlloc(sizeof(PAGESET)); if (!(*ppPageSet)) { hr = E_OUTOFMEMORY; goto Cleanup; } ::ZeroMemory(*ppPageSet, sizeof(PAGESET)); (*ppPageSet)->cbStruct = sizeof(PAGESET) ; (*ppPageSet)->cPageRange = 1; if (_fPrintSelectedPages) { (*ppPageSet)->rgPages[0].nFromPage = _nPageFrom; (*ppPageSet)->rgPages[0].nToPage = _nPageTo; } else { (*ppPageSet)->rgPages[0].nFromPage = 1; (*ppPageSet)->rgPages[0].nToPage = PAGESET_TOLASTPAGE; } (*ppTargetDevice) = InitTargetDevice(); if (!(*ppTargetDevice)) { // Error! Clear the PageSet structure. ::CoTaskMemFree(*ppPageSet); (*ppPageSet) = NULL; hr = E_FAIL; goto Cleanup; } Cleanup: return hr; } // NB (greglett) // Alas, Win9x returns a ASCII DEVMODE and a Unicode DEVNAMES from the dialog functions. // The old code avoided converting the second structure, *except* to create a TARGETDEVICE for IPrint objects. // Since we will need to do this, I have brought this function over. // If this function works *really* well, then maybe we can always convert and get rid of all the explicit calls to // ANSI functions above. (CreateICA, CreateDCA, OpenPrinterA, &c...). DVTARGETDEVICE * DevModeWFromDevModeA( DVTARGETDEVICE *ptd ) { // NOTE: Only the DEVMODE structure is in the wrong (ascii) format! DEVMODEA * lpdma = NULL; DVTARGETDEVICE * ptdW = NULL; if (!ptd || !ptd->tdExtDevmodeOffset) goto Cleanup; lpdma = (DEVMODEA *) (((BYTE *)ptd) + ptd->tdExtDevmodeOffset); // If the reported size is too small for our conception of a DEVMODEA, don't risk a GPF // in our code and bail out now. if ( (DWORD)lpdma->dmSize + lpdma->dmDriverExtra < offsetof(DEVMODEA, dmLogPixels) ) goto Cleanup; ptdW = (DVTARGETDEVICE *)::CoTaskMemAlloc( ptd->tdSize + (sizeof(BCHAR) - sizeof(char)) * (CCHDEVICENAME + CCHFORMNAME) ); if (ptdW) { // Copy the entire structure up to DEVMODE part. memcpy(ptdW, ptd, ptd->tdExtDevmodeOffset); // Account for the increase of the two DEVMODE unicode strings. ptdW->tdSize += (sizeof(BCHAR) - sizeof(char)) * (CCHDEVICENAME + CCHFORMNAME); // Convert the devmode structure. { DEVMODEW * lpdmw = (DEVMODEW *) (((BYTE *)ptdW) + ptdW->tdExtDevmodeOffset); long nCapChar; // Copy the first string (CCHDEVICENAME). // Really, 0 indicates a conversion error. However, we really can't do much about it other than construct a NULL string. if (!::MultiByteToWideChar(CP_ACP, 0, (char *)lpdma->dmDeviceName, -1, lpdmw->dmDeviceName, CCHDEVICENAME)) { lpdmw->dmDeviceName[0] = _T('\0'); } // Copy the gap between strings. memcpy( &lpdmw->dmSpecVersion, &lpdma->dmSpecVersion, offsetof(DEVMODEA, dmFormName) - offsetof(DEVMODEA, dmSpecVersion) ); // Copy the first string (CCHDEVICENAME). if (!::MultiByteToWideChar(CP_ACP, 0, (char *)lpdma->dmFormName, -1, lpdmw->dmFormName, CCHFORMNAME)) { lpdmw->dmFormName[0] = _T('\0'); } // Copy the last part including the driver-specific DEVMODE part (dmDriverExtra). memcpy( &lpdmw->dmLogPixels, &lpdma->dmLogPixels, (DWORD)lpdma->dmSize + lpdma->dmDriverExtra - offsetof(DEVMODEA, dmLogPixels) ); // Correct the dmSize member by accounting for larger unicode strings. lpdmw->dmSize += (sizeof(BCHAR) - sizeof(char)) * (CCHDEVICENAME + CCHFORMNAME); } } Cleanup: return ptdW; } //+---------------------------------------------------------------------- // // Function: InitPrintHandles // // Purpose: Allocate a DVTARGETDEVICE structure, and initialize // it according to the hDevMode and hDevNames. // Also allocated an HIC. // // Note: IMPORTANT: Note that the DEVMODE structure is not wrapped // on non-unicode platforms. (See comments below for details.) // // Returns: HRESULT // //----------------------------------------------------------------------- DVTARGETDEVICE * CTemplatePrinter::InitTargetDevice() { HRESULT hr = S_OK; LPDEVNAMES pDN = NULL; LPDEVMODE pDM = NULL; LPDEVMODEA pDMA = NULL; DVTARGETDEVICE * ptd = NULL; WORD nMaxOffset; DWORD dwDevNamesSize, dwDevModeSize, dwPtdSize; int nNameLength; if (!_hDevNames || !_hDevMode) goto Cleanup; pDN = (LPDEVNAMES)::GlobalLock(_hDevNames); if (!pDN) goto Cleanup; if (g_fUnicodePlatform) { pDM = (LPDEVMODE)::GlobalLock(_hDevMode); if (!pDM) goto Cleanup; } else { pDMA = (LPDEVMODEA)::GlobalLock(_hDevMode); if (!pDMA) goto Cleanup; } // IMPORTANT: We have painstakingly // converted only the hDevNames parameter and NOT hDevMode (NOT!!!) to have TCHAR // members. nMaxOffset = max( pDN->wDriverOffset, pDN->wDeviceOffset ); nMaxOffset = max( nMaxOffset, pDN->wOutputOffset ); nNameLength = _tcslen( (TCHAR *)pDN + nMaxOffset ); // dw* are in bytes, not TCHARS dwDevNamesSize = sizeof(TCHAR) * ((DWORD)nMaxOffset + nNameLength + 1); dwDevModeSize = g_fUnicodePlatform ? ((DWORD)pDM->dmSize + pDM->dmDriverExtra) : ((DWORD)pDMA->dmSize + pDMA->dmDriverExtra); dwPtdSize = sizeof(DWORD) + dwDevNamesSize + dwDevModeSize; ptd = (DVTARGETDEVICE *)::CoTaskMemAlloc(dwPtdSize); if (!ptd) goto Cleanup; else { ptd->tdSize = dwPtdSize; // This is an ugly trick. ptd->tdDriverNameOffset and pDN happen // to match up, so we just copy that plus the data in one big chunk. // Remember, I didn't write this -- this code is based on the OLE2 SDK. // Offsets are in characters, not bytes. memcpy( &ptd->tdDriverNameOffset, pDN, dwDevNamesSize ); ptd->tdDriverNameOffset *= sizeof(TCHAR); ptd->tdDriverNameOffset += sizeof(DWORD); ptd->tdDeviceNameOffset *= sizeof(TCHAR); ptd->tdDeviceNameOffset += sizeof(DWORD); ptd->tdPortNameOffset *= sizeof(TCHAR); ptd->tdPortNameOffset += sizeof(DWORD); // IMPORTANT: We are not converting the DEVMODE structure back and forth // from ASCII to Unicode on Win9x anymore because we are not touching the // two strings or any other member. Converting the DEVMODE structure can // be tricky because of potential and common discrepancies between the // value of the dmSize member and sizeof(DEVMODE). (25155) if (g_fUnicodePlatform) memcpy((BYTE *)&ptd->tdDriverNameOffset + dwDevNamesSize, pDM, dwDevModeSize); else memcpy((BYTE *)&ptd->tdDriverNameOffset + dwDevNamesSize, pDMA, dwDevModeSize); ptd->tdExtDevmodeOffset = USHORT(sizeof(DWORD) + dwDevNamesSize); // We must return a corrent (all WCHAR) DVTARGETDEVICEW structure. // Convert the nasty DEVMODEA if we've just copied it over. if (!g_fUnicodePlatform) { DVTARGETDEVICE *ptdOld; ptdOld = ptd; ptd = DevModeWFromDevModeA(ptdOld); ::CoTaskMemFree(ptdOld); } } Cleanup: if (pDM || pDMA) ::GlobalUnlock(_hDevMode); if (pDN) ::GlobalUnlock(_hDevNames); return ptd; } #ifdef DBG // // CTemplatePrinter Debug-Only functions // void CTemplatePrinter::VerifyOrientation() { // Verify that the page size reflects the current orientation bit in the DEVMODE // These properties should always be in sync. if (_hDevMode) { void *pDevMode = ::GlobalLock(_hDevMode); if (pDevMode) { BOOL fOrientationValid; BOOL fLandscape; // (greglett) Non-Unicode badness. See comment at definition of _hDevMode if (g_fUnicodePlatform) { fOrientationValid = !!(((DEVMODEW *)pDevMode)->dmFields & DM_ORIENTATION); fLandscape = (((DEVMODEW *)pDevMode)->dmOrientation == DMORIENT_LANDSCAPE); } else { fOrientationValid = !!(((DEVMODEA *)pDevMode)->dmFields & DM_ORIENTATION); fLandscape = (((DEVMODEA *)pDevMode)->dmOrientation == DMORIENT_LANDSCAPE); } Assert( !fOrientationValid || fLandscape == (_ptPaperSize.x > _ptPaperSize.y) ); ::GlobalUnlock(_hDevMode); } } } #endif