VC Web打印解决方案概述

VC Web打印解决方案概述

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

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

大致流程如下:

Created with Raphaël 2.1.0 start //定义变量 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(); End yes no

这种方式有很大的缺点,可控性差,有时可能默认浏览器不是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 functions
protected:
    //{{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_MAP
END_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)

        // Operations
public:
    // 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

    // Implementation
public:
    virtual ~CWebPrintView();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:

    // Generated message map functions
protected:
    //{{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 commands
END_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 drawing

void CWebPrintView::OnDraw(CDC* pDC)
{
    ASSERT_VALID(pDC);
}

void CWebPrintView::OnInitialUpdate()
{
    CHtmlView::OnInitialUpdate();
}
/////////////////////////////////////////////////////////////////////////////
// CWebPrintView diagnostics

#ifdef _DEBUG
void CWebPrintView::AssertValid() const
{
    CHtmlView::AssertValid();
}

void CWebPrintView::Dump(CDumpContext& dc) const
{
    CHtmlView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CPrintView message handlers
void 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);

你可能感兴趣的:(CHtmlView,web-print)