|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionI have been using Code Project for several years now, and I've always felt that I should give back something to help my fellow software developers. Therefore, when I finished my last project that required some research into ActiveX control printing, I thought it would be a good topic to create a demo program. This project demonstrates several different ways to print an ActiveX control. I've tried to show some of the most common alternatives, and in turn, describe some of the problems that you might encounter when printing. This is by no means an exhaustive coverage of printing. BackgroundThis article assumes that you have basic familiarity with ActiveX controls in C++. In addition, it
is assumed that you understand how to use The demo program was created as a simple "Single document" MFC application using the AppWizard.
To show how a "standard" ActiveX control operates, the demo app uses the MS Chart control and the MS Calendar control. These
controls show all of the basic concepts and they appear to ship with Windows XP. The picture control is thrown in to show
one alternative (non COM) method of printing ( Printing AlternativesThere are several ways to print an ActiveX control. This article demonstrates the following methods:
Each of these methods has its advantages and disadvantages. In fact, not all ActiveX controls will support all of these methods, and even when they do, the results might be less than ideal. The sections that follow describe how to use each method. The demo program can be used to play with the different alternatives and see how the ActiveX controls react to the different methods. The IViewObject Interface (Screen or Printer Device Context)Many ActiveX controls support this interface. This interface allows an object to place a representation of
itself onto a device context. The Since the caller is responsible for setting up the device context to draw into, the caller has several choices. The printer device context can be used directly, or an off-screen memory device context can be used. Internet Explorer 4.0 uses this method to print, passing the device context of the printer. During my own testing with a third party control, I tried to use the The alternative to printing directly into the printer device context was to create an off-screen memory area that I
passed into the Printing to the off-screen memory is very similar to doing a "screen capture" of the control. However, you don't have to worry about whether the control is hidden or obscured, etc. The demo program will allow you to test the // get the IUnknown pointer for the control object // (note, don't release this one per MSDN) LPUNKNOWN pUnk = pWnd->GetControlUnknown(); if (!pUnk) { AfxMessageBox(_T("Not an ActiveX Control")); return; } // ... // query for the IViewObject interface for printing // (note, some don't support this, like lite controls) IViewObjectPtr spViewObj; hRes = pUnk->QueryInterface(__uuidof(spViewObj), (void **) &spViewObj); if (FAILED(hRes)) _com_issue_error(hRes); // draw the object into the printer device context hRes = spViewObj->Draw(DVASPECT_CONTENT, -1, NULL, NULL, NULL, pDC->m_hDC, &rectPrn, NULL, NULL, 0); if (FAILED(hRes)) _com_issue_error(hRes); Note: if you attempt use the One additional note. When I was attempting to transfer the off-screen memory onto the printer device context, I found
that on some computers/printers the printout would not show up. It would show up blank and there did not appear to be any errors.
After doing some research on this, I found the MSDN article Q195830 - "INFO: Blitting Between DCs for Different Devices
Is Unsupported". This explained why my direct use of The IViewObject Interface (Metafile)This method uses the same interface as described in the previous section, however in this case a metafile device context is passed in. The control then draws into the metafile and the client can use the metafile to draw into the printer device context. This is the method used by Visual Basic 6.0. An excerpt from the code is shown below.// create a metafile to draw into CMetaFileDC dcMeta; if (!dcMeta.Create()) _com_issue_error(HRESULT_FROM_WIN32(::GetLastError())); // query for the IViewObject interface for printing // (note, some don't support this, like lite controls) IViewObjectPtr spViewObj; hRes = pUnk->QueryInterface(__uuidof(spViewObj), (void **) &spViewObj); if (FAILED(hRes)) _com_issue_error(hRes); // draw the object into the metafile device context hRes = spViewObj->Draw(DVASPECT_CONTENT, -1, NULL, NULL, NULL, dcMeta, &rectPrn, NULL, NULL, 0); if (FAILED(hRes)) _com_issue_error(hRes); // get the completed meta file handle HMETAFILE hMeta = dcMeta.Close(); if (!hMeta) _com_issue_error(HRESULT_FROM_WIN32(::GetLastError())); // play the meta file into the printer device context pDC->PlayMetaFile(hMeta); // free up the metafile memory DeleteMetaFile(hMeta); The IDataObject Interface (Metafile)This interface is also supported by many ActiveX controls. The interface is used for data transfer. The The code excerpt below shows the basic technique. In this case, the client queries for the
// query for the IDataObject interface for GetData // (some controls don't support this) IDataObjectPtr spDataObj; hRes = pUnk->QueryInterface(__uuidof(spDataObj), (void **) &spDataObj); if (FAILED(hRes)) _com_issue_error(hRes); // setup the structures for retrieving the results FORMATETC Formatetc; Formatetc.cfFormat = CF_METAFILEPICT; Formatetc.ptd = NULL; Formatetc.dwAspect = DVASPECT_CONTENT; Formatetc.lindex = -1; Formatetc.tymed = TYMED_MFPICT; STGMEDIUM Medium = { 0 }; // draw the control into a metafile hRes = spDataObj->GetData(&Formatetc, &Medium); if (FAILED(hRes)) _com_issue_error(hRes); // the returned type should be a metafile since // that's what we requested if (Medium.tymed & TYMED_MFPICT) { // get the metafile picture pointer so we // can get the metafile handle METAFILEPICT *pMetaPict = (METAFILEPICT *) GlobalLock(Medium.hMetaFilePict); // scale appropriately pDC->SetMapMode(pMetaPict->mm); pDC->SetViewportOrg(rectPrn.left, rectPrn.top); pDC->SetViewportExt(rectPrn.right - rectPrn.left + 1, rectPrn.bottom - rectPrn.top + 1); // play the meta file into the printer device context pDC->PlayMetaFile(pMetaPict->hMF); // unlock the metafile picture handle GlobalUnlock(Medium.hMetaFilePict); // release the results ReleaseStgMedium(&Medium); } Not all controls support the Obviously, the picture control does not support this method. The picture control is not an ActiveX control. The WM_PRINT MessageFor some types of controls, you can use the The code used to test this method is shown below. // send the message to print the control // (could use pWnd->Print directly) LRESULT lRes = pWnd->SendMessage(WM_PRINT, (WPARAM) pDC->GetSafeHdc(), PRF_CLIENT | PRF_CHILDREN | PRF_OWNED); Examining the codeThe demo program can be used to test each of the print techniques. All of the code of interest is
in the There are a couple of other support routines included; to print a title and output an error message. The content of these routines should be self explanatory. All of the other code in the application was generated using the AppWizard. ConclusionMy goal was to show a few different methods of printing ActiveX controls. My intent was not to show every possible way to print, but to present a few concepts that may be helpful to others. Hopefully these simple code snippets will be of use to others facing the same problems that I did. History
|
||||||||||||||||||||||