VC Web打印解决方案概述

来源:互联网 发布:淘宝上宝贝排名靠前 编辑:程序博客网 时间:2024/05/18 01:18

VC Web打印解决方案概述

Web打印在实际应用中比较广泛,比如公司报表、银行回单等。如果是直接在网页中嵌入打印比较简单,直接使用JS的打印功能。但如果生成的报表以html的形式存储于本地呢?下面基于vc++介绍两种方法。

通过COM组建调用IE浏览器的打印功能

大致流程如下:

Created with Raphaël 2.1.0start//定义变量 IOleCommandTarget *pCommandTarget; IDispatch *pDispatch; IWebBrowser2 *pWebBrowser2; IUnknown *pUnknown;初始化COM:CoInitialize(NULL)调用CoCreateInstance(/****/)创建浏览器对象,并获取IUnknown接口指针调用IUnknown的方法QueryInterface()获取IWebBrowser2接口调用IWebBrowser2的Navigate方法导航到指定URL调用IWebBrowser2的get_Document()方法加载文档,输出IDispatch接口调用IDispatch的QueryInterface()方法获取IOleCommandTarget接口调用IOleCommandTarget的Exec()方法执行打印detect print statu print over?释放COM资源,然后调用CoUninitialize();Endyesno

这种方式有很大的缺点,可控性差,有时可能默认浏览器不是IE、或者浏览器注册表被修改都会导致创建IE COM组件失败。并且每次打印都要去创建一次COM组件,打印完成再释放浏览器。如果通过增加COM引用计数的方式,经测试加载文档会失败,可能没有更深入的了解,微软官网上也是看得一知半解。


使用MFC的CHtmlView的打印功能

使用CHtmlView有两种方式,建工程时可以选择文档\视图结构,也可以选择基于对话框模式。如果选择文档\视图模式,后续选择工程视图的基类为CHtmlView,这样编译器会自动添加消息映射。


.1 使用文档/视图方式

使用这种方式相对简单,只需添加如下消息映射:

BEGIN_MESSAGE_MAP(CWebBrowserDocumentView, CHtmlView)    //{{AFX_MSG_MAP(CWebBrowserDocumentView)    ON_COMMAND(ID_FILE_PRINT, OnFilePrint)    ON_WM_DESTROY()    //}}AFX_MSG_MAP    // Standard printing commands    //ON_COMMAND(ID_FILE_PRINT, CHtmlView::OnFilePrint)END_MESSAGE_MAP()

如果消息映射为 ON_COMMAND(ID_FILE_PRINT, CHtmlView::OnFilePrint),点击打印的时候会弹出打印设置对话框(CPrintDialog),为了屏蔽对话框,需自己实现打印,所以添加ON_COMMAND(ID_FILE_PRINT, OnFilePrint)消息映射,然后在类里面添加消息响应方法:afx_msg void OnFilePrint()。

那么要怎么实现OnFilePrint()方法呢?

在vc编译器的安装目录:“VC98\MFC\SRC”下有个VIEWHTML.cpp文件,打开可以看到打印的实现方法,如下:

    void CHtmlView::OnFilePrint()    {        // get the HTMLDocument        if (m_pBrowserApp != NULL)        {            LPOLECOMMANDTARGET lpTarget = NULL;            LPDISPATCH lpDisp = GetHtmlDocument();            if (lpDisp != NULL)            {                // the control will handle all printing UI                if (SUCCEEDED(lpDisp->QueryInterface(IID_IOleCommandTarget,                        (LPVOID*) &lpTarget)))                {                    lpTarget->Exec(NULL, OLECMDID_PRINT, 0, NULL, NULL);                    lpTarget->Release();                }                lpDisp->Release();            }        }    }

所以,直接拿过来用就可以了。下面看一下IOleCommandTarget接口的Exec方法:

    HRESULT Exec(        [in]      const GUID    *pguidCmdGroup,        [in]            DWORD   nCmdID,        [in]            DWORD   nCmdexecopt,        [in]            VARIANT *pvaIn,        [in, out]       VARIANT *pvaOut    );

参数:
pguidCmdGroup【in】 唯一的指令集标志,为NULL表示标准指令集,这里可以是CGID_MSHTML
nCmdID 【in】 要执行的指令ID,必须在pguidCmdGroup指定的指令集中
nCmdexecopt 【in】 指定如何执行该指令。可能取值为OLECMDEXECOPT和OLECMDID_WINDOWSTATE_FLAG的枚举值
pvaIn 【in】指向包含输入参数的VARIANTARG结构体指针,可以为NULL
pvaOut 【in out】 输出参数VARIANTARG结构体指针,可以为NULL

注意:关键是nCmdID、nCmdexecopt的值,打印的时候为了不弹出打印设置对话框,nCmdexecopt的值为OLECMDEXECOPT_DONTPROMPTUSER.


.2 基于对话框模式

基于对话框方式实际上也是间接使用文档\视图结构。首先定义一个框架类,如下:

class CWebPrintFrame : public CFrameWnd  {public:    CWebPrintFrame();    DECLARE_DYNCREATE(CWebPrintFrame)public:    virtual ~CWebPrintFrame();public:    void OnFilePrint(char* url);    CWebPrintView   *m_pView;    LPCTSTR m_szUrl;    BOOL m_bLoaded;    // Generated message map functionsprotected:    //{{AFX_MSG(CMainFrame)    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);    afx_msg void OnClose();    //}}AFX_MSG    DECLARE_MESSAGE_MAP()};

实现如下:

//////////////////////////////////////////////////////////////////////// CWebPrintFrame //////////////////////////////////////////////////////////////////////IMPLEMENT_DYNCREATE(CWebPrintFrame, CFrameWnd)BEGIN_MESSAGE_MAP(CWebPrintFrame, CFrameWnd)    //{{AFX_MSG_MAP(CWebPrintFrame)    ON_WM_CREATE()    ON_WM_CLOSE()    //}}AFX_MSG_MAPEND_MESSAGE_MAP()CWebPrintFrame::CWebPrintFrame(){    if (!Create(NULL,"PrintPreview",WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,        CRect(200,0,1224,800)))    {        TRACE0("Failed to create View Window!");    }}CWebPrintFrame::~CWebPrintFrame(){}int CWebPrintFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)    {        return -1;    }    CCreateContext contex;    contex.m_pNewViewClass = RUNTIME_CLASS(CWebPrintView);    contex.m_pCurrentFrame = this;    contex.m_pCurrentDoc = NULL;    contex.m_pLastView = NULL;    m_pView = STATIC_DOWNCAST(CWebPrintView,CreateView(&contex));    m_bLoaded = FALSE;    if (m_pView != NULL)    {        m_pView->ShowWindow(SW_SHOW);        SetActiveView(m_pView);        //SetLandscapeMode(DMORIENT_PORTRAIT);    }    ShowWindow(SW_SHOW);    CWinApp *pApp = AfxGetApp();    pApp->m_pMainWnd = this;    return 0;}void CWebPrintFrame::OnFilePrint(char* url){    this->m_szUrl = url;    m_pView->Navigate2(m_szUrl,NULL,NULL);    //等待加载完成    MSG msg;    while(!m_bLoaded)    {        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))        {            TranslateMessage(&msg);            DispatchMessage(&msg);        }           }    //直接打印    m_pView->SendMessage(WM_COMMAND,ID_FILE_PRINT);}void CWebPrintFrame::OnClose(){    CWebPrintFrame *pFrame = (CWebPrintFrame *)::AfxGetMainWnd();    CWinApp *pApp = AfxGetApp();    //pApp->m_pMainWnd = pFrame->m_pDlg;    pFrame->DestroyWindow();    //CFrameWnd::OnClose();}

然后定义视图类,如下:

class CWebPrintView : public CHtmlView  {public:    CWebPrintView();    DECLARE_DYNCREATE(CWebPrintView)        // Operationspublic:    // Overrides    // ClassWizard generated virtual function overrides    //{{AFX_VIRTUAL(CWebPrintView)public:    virtual void OnDraw(CDC* pDC);  // overridden to draw this view    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);protected:    virtual void OnInitialUpdate(); // called first time after construct    //}}AFX_VIRTUAL    // Implementationpublic:    virtual ~CWebPrintView();#ifdef _DEBUG    virtual void AssertValid() const;    virtual void Dump(CDumpContext& dc) const;#endifprotected:    // Generated message map functionsprotected:    //{{AFX_MSG(CWebPrintView)    afx_msg void OnFilePrint();    afx_msg void OnDocumentComplete(LPCTSTR lpszURL);    //}}AFX_MSG    DECLARE_MESSAGE_MAP()};

实现如下:

//////////////////////////////////////////////////////////////////////// CWebPrintView//////////////////////////////////////////////////////////////////////IMPLEMENT_DYNCREATE(CWebPrintView, CHtmlView)BEGIN_MESSAGE_MAP(CWebPrintView, CHtmlView)    //{{AFX_MSG_MAP(CWebPrintView)    ON_COMMAND(ID_FILE_PRINT, OnFilePrint)    ON_COMMAND(READYSTATE_COMPLETE,OnDocumentComplete)    //}}AFX_MSG_MAP    // Standard printing commandsEND_MESSAGE_MAP()CWebPrintView::CWebPrintView(){}CWebPrintView::~CWebPrintView(){}BOOL CWebPrintView::PreCreateWindow(CREATESTRUCT& cs){    // TODO: Modify the Window class or styles here by modifying    //  the CREATESTRUCT cs    return CHtmlView::PreCreateWindow(cs);}/////////////////////////////////////////////////////////////////////////////// CWebPrintView drawingvoid CWebPrintView::OnDraw(CDC* pDC){    ASSERT_VALID(pDC);}void CWebPrintView::OnInitialUpdate(){    CHtmlView::OnInitialUpdate();}/////////////////////////////////////////////////////////////////////////////// CWebPrintView diagnostics#ifdef _DEBUGvoid CWebPrintView::AssertValid() const{    CHtmlView::AssertValid();}void CWebPrintView::Dump(CDumpContext& dc) const{    CHtmlView::Dump(dc);}#endif //_DEBUG/////////////////////////////////////////////////////////////////////////////// CPrintView message handlersvoid CWebPrintView::OnFilePrint() {                  LPOLECOMMANDTARGET lpTarget = NULL;    LPDISPATCH lpDisp = GetHtmlDocument();    if (lpDisp != NULL)    {        // the control will handle all printing UI        if (SUCCEEDED(lpDisp->QueryInterface(IID_IOleCommandTarget,            (LPVOID*) &lpTarget)))        {            lpTarget->Exec(NULL, OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);            lpTarget->Release();        }        lpDisp->Release();    }}void CWebPrintView::OnDocumentComplete(LPCTSTR lpszURL){    CHtmlView::OnDocumentComplete(lpszURL);    CWebPrintFrame* pFrame= (CWebPrintFrame* )AfxGetMainWnd();    pFrame->m_bLoaded = TRUE;}

调用方式如下:

CWebPrintFrame* pFrame = new CWebPrintFrame();pFrame->OnFilePrint(szURL);
0 0